從源代碼級別的角度看php var_dump和echo的區別

從源代碼級別的角度看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,在之後根據類型打印,而不是直接打印字符串後在判斷類型,這也是本文開頭的兩組代碼的區別原因.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章