從源代碼級別的角度看php var_dump和echo的區別
上週五同事出了一道有趣的題目,題目是這樣:
答案很神奇是1,而一旦把echo替換成了var_dump,則這是一個null.根據文檔我們可以看到unset轉換
當然是什麼造成echo和var_dump的區別呢,這就要從源代碼級別進行討論了.本文並不會像某些文章一些羅列大量的二者的區別,想要看這種面試題,請自行打開搜索引擎.
變量的源代碼
在這裏我們查看官網中關於變量的解釋,很幸運,我們這裏可以直接看到c的代碼,而不是需要在工程中搜索.
typedef struct _zval_struct zval;
struct _zval_struct {
union {
long lval;
double dval;
struct {
char *val;
int len;
} str;
HashTable *ht;
zend_object_value obj;
zend_ast *ast;
} value;
zend_uint refcount__gc;
zend_uchar type;
zend_uchar is_ref__gc;
};
看完源代碼我們猜測unset轉換可能只改了type,而沒有改變其他的東西.
echo的源代碼
網上有這一類的文章 ,我個人選出了一篇不錯的文章出來
深入理解PHP原理之--echo的實現
ZEND_API int zend_print_zval_ex(zend_write_func_t write_func, zval *expr, int indent)
{
zval expr_copy;
int use_copy;
zend_make_printable_zval(expr, &expr_copy, &use_copy);
if (use_copy) {
expr = &expr_copy;
}
if (expr->value.str.len==0) { /* optimize away empty strings */
if (use_copy) {
zval_dtor(expr);
}
return 0;
}
write_func(expr->value.str.val, expr->value.str.len);
if (use_copy) {
zval_dtor(expr);
}
return expr->value.str.len;
}
核心在這一段write_func(expr->value.str.val, expr->value.str.len);
write_func並不複雜,實際上只是一個字符串讀取函數.因此我們可以看出他是直接去讀取zval下面的聯合下的支付串,而不會去幹其他的事情.
var_dump的源代碼
var_dump的源代碼網上並沒有代碼的文章,不過可以看這一篇如何在php源代碼中找尋函數.
通過在下載的源代碼中查找PHP_FUNCTION(var_dump),找到函數的實現.
我們在etc/standard/var.c中查找了實現
PHP_FUNCTION(var_dump)
{
zval *args;
int argc;
int i;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
for (i = 0; i < argc; i++) {
php_var_dump(&args[i], 1);
}
}
我們再看php_var_dump的代碼
switch (Z_TYPE_P(struc)) {
case IS_FALSE:
php_printf("%sbool(false)\n", COMMON);
break;
case IS_TRUE:
php_printf("%sbool(true)\n", COMMON);
break;
case IS_NULL:
php_printf("%sNULL\n", COMMON);
break;
case IS_LONG:
php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
break;
case IS_DOUBLE:
php_printf("%sfloat(%.*G)\n", COMMON, (int) EG(precision), Z_DVAL_P(struc));
break;
case IS_STRING:
php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
php_printf("\" refcount(%u)\n", Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : 1);
break;
........
我們可以看到在打印之前會檢查type類型也就是上文提到了zend_uchar type,在之後根據類型打印,而不是直接打印字符串後在判斷類型,這也是本文開頭的兩組代碼的區別原因.