可用看出如果字符串链接的两个操作数如果不是字符串的话, 则调用zend_make_printable_zval函数将操作数转换为"printable_zval"也就是字符串。
ZEND_API void zend_make_printable_zval(zval *expr, zval *expr_copy, int *use_copy)
{
if (Z_TYPE_P(expr)==IS_STRING) {
*use_copy = 0;
return;
}
switch (Z_TYPE_P(expr)) {
case IS_NULL:
Z_STRLEN_P(expr_copy) = 0;
Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC();
break;
case IS_BOOL:
if (Z_LVAL_P(expr)) {
Z_STRLEN_P(expr_copy) = 1;
Z_STRVAL_P(expr_copy) = estrndup("1", 1);
} else {
Z_STRLEN_P(expr_copy) = 0;
Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC();
}
break;
case IS_RESOURCE:
// ...省略
case IS_ARRAY:
Z_STRLEN_P(expr_copy) = sizeof("Array") - 1;
Z_STRVAL_P(expr_copy) = estrndup("Array", Z_STRLEN_P(expr_copy));
break;
case IS_OBJECT:
// ... 省略
case IS_DOUBLE:
*expr_copy = *expr;
zval_copy_ctor(expr_copy);
zend_locale_sprintf_double(expr_copy ZEND_FILE_LINE_CC);
break;
default:
*expr_copy = *expr;
zval_copy_ctor(expr_copy);
convert_to_string(expr_copy);
break;
}
Z_TYPE_P(expr_copy) = IS_STRING;
*use_copy = 1;
}
ZEND_API void convert_to_null(zval *op) /* {{{ */
{
if (Z_TYPE_P(op) == IS_OBJECT) {
if (Z_OBJ_HT_P(op)->cast_object) {
zval *org;
TSRMLS_FETCH();
ALLOC_ZVAL(org);
*org = *op;
if (Z_OBJ_HT_P(op)->cast_object(org, op, IS_NULL TSRMLS_CC) == SUCCESS) {
zval_dtor(org);
return;
}
*op = *org;
FREE_ZVAL(org);
}
}
zval_dtor(op);
Z_TYPE_P(op) = IS_NULL;
}
转换为NULL非常简单,对变量进行析构操作,然后将数据类型设为IS_NULL即可。 可能读者会好奇(unset)$a和unset($a)这两者有没有关系,其实并没有关系, 前者是将变量$a的类型变为NULL,这只是一个类型的变化,而后者是将这个变量释放,释放后当前作用域内该变量就不存在了。
除了上面提到的与C语言很像,在其它语言中也经常见到的强制数据转换,PHP中有一个极具PHP特色的强制类型转换。 PHP的标准扩展中提供了两个有用的方法settype()以及gettype()方法,前者可以动态的改变变量的数据类型, gettype()方法则是返回变量的数据类型。在ext/standard/type.c文件中找到settype的实现源码:
PHP_FUNCTION(settype)
{
zval **var;
char *type;
int type_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs", &var, &type, &type_len) == FAILURE) {
return;
}
if (!strcasecmp(type, "integer")) {
convert_to_long(*var);
} else if (!strcasecmp(type, "int")) {
convert_to_long(*var);
} else if (!strcasecmp(type, "float")) {
convert_to_double(*var);
} else if (!strcasecmp(type, "double")) { /* deprecated */
convert_to_double(*var);
} else if (!strcasecmp(type, "string")) {
convert_to_string(*var);
} else if (!strcasecmp(type, "array")) {
convert_to_array(*var);
} else if (!strcasecmp(type, "object")) {
convert_to_object(*var);
} else if (!strcasecmp(type, "bool")) {
convert_to_boolean(*var);
} else if (!strcasecmp(type, "boolean")) {
convert_to_boolean(*var);
} else if (!strcasecmp(type, "null")) {
convert_to_null(*var);
} else if (!strcasecmp(type, "resource")) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot convert to resource type");
RETURN_FALSE;
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid type");
RETURN_FALSE;
}
RETVAL_TRUE;
}
这个极具PHP特色的强制类型转换就是这个函数,而这个函数是作为一个代理方法存在, 具体的转换规则由各个类型的处理函数处理,不管是自动还是强制类型转换,最终都会调用这些内部转换方法, 这和前面的强制类型转换在本质上是一样的。
http://www.php-internals.com/book/?p=chapt03/03-07-type-cast