在一个php群里有朋友提到 :
用 var_dump ($list); 可以输出来内容,而 echo $list["name"]; 却不能输出 内容来,这是什么情况啊
这种问题的答案肯定要在源码里面找一找了
git回来了php-src 的master版本
查找函数php_var_dump,源码如下:
PHPAPI void php_var_dump(zval **struc, int level TSRMLS_DC) /* {{{ */ { HashTable *myht; const char *class_name; zend_uint class_name_len; int (*php_element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*); int is_temp; if (level > 1) { php_printf("%*c", level - 1, ' '); } switch (Z_TYPE_PP(struc)) { case IS_BOOL: php_printf("%sbool(%s)\n", COMMON, Z_LVAL_PP(struc) ? "true" : "false"); break; case IS_NULL: php_printf("%sNULL\n", COMMON); break; case IS_LONG: php_printf("%sint(%ld)\n", COMMON, Z_LVAL_PP(struc)); break; case IS_DOUBLE: php_printf("%sfloat(%.*G)\n", COMMON, (int) EG(precision), Z_DVAL_PP(struc)); break; case IS_STRING: php_printf("%sstring(%d) \"", COMMON, Z_STRLEN_PP(struc)); PHPWRITE(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc)); PUTS("\"\n"); break; case IS_ARRAY: myht = Z_ARRVAL_PP(struc); if (++myht->nApplyCount > 1) { PUTS("*RECURSION*\n"); --myht->nApplyCount; return; } php_printf("%sarray(%d) {\n", COMMON, zend_hash_num_elements(myht)); php_element_dump_func = php_array_element_dump; is_temp = 0; goto head_done; case IS_OBJECT: myht = Z_OBJDEBUG_PP(struc, is_temp); if (myht && ++myht->nApplyCount > 1) { PUTS("*RECURSION*\n"); --myht->nApplyCount; return; } if (Z_OBJ_HANDLER(**struc, get_class_name)) { Z_OBJ_HANDLER(**struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC); php_printf("%sobject(%s)#%d (%d) {\n", COMMON, class_name, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0); efree((char*)class_name); } else { php_printf("%sobject(unknown class)#%d (%d) {\n", COMMON, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0); } php_element_dump_func = php_object_property_dump; head_done: if (myht) { zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_element_dump_func, 1, level); --myht->nApplyCount; if (is_temp) { zend_hash_destroy(myht); efree(myht); } } if (level > 1) { php_printf("%*c", level-1, ' '); } PUTS("}\n"); break; case IS_RESOURCE: { const char *type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC); php_printf("%sresource(%ld) of type (%s)\n", COMMON, Z_LVAL_PP(struc), type_name ? type_name : "Unknown"); break; } default: php_printf("%sUNKNOWN:0\n", COMMON); break; } }
对于每一种类型的变量都把具体得信息打印了出来
再看看echo,echo是由zend引擎来执行
void zend_do_echo(const znode *arg TSRMLS_DC) /* {{{ */ { zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->opcode = ZEND_ECHO; SET_NODE(opline->op1, arg); SET_UNUSED(opline->op2); } /* }}} */
ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMP|VAR|CV, ANY) { USE_OPLINE zend_free_op free_op1; zval *z; SAVE_OPLINE(); z = GET_OP1_ZVAL_PTR(BP_VAR_R); if (OP1_TYPE == IS_TMP_VAR && Z_TYPE_P(z) == IS_OBJECT) { INIT_PZVAL(z); } zend_print_variable(z); FREE_OP1(); CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); }
代码到这里就可以明白,echo是交给zend虚拟机了,再看看这个
ZEND_API int zend_print_variable(zval *var) { return zend_print_zval(var, 0); } ZEND_API int zend_print_zval(zval *expr, int indent) /* {{{ */ { return zend_print_zval_ex(zend_write, expr, indent); } /* }}} */ 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 (Z_STRLEN_P(expr) == 0) { /* optimize away empty strings */ if (use_copy) { zval_dtor(expr); } return 0; } write_func(Z_STRVAL_P(expr), Z_STRLEN_P(expr)); if (use_copy) { zval_dtor(expr); } return Z_STRLEN_P(expr); } /* }}} */
到这里可以看到,最终是由zend_write来执行,zend_write是一个函数指针,在zend_sartup里赋值
int zend_startup(zend_utility_functions *utility_functions, char **extensions TSRMLS_DC) /* {{{ */ { /*......省略部分.......*/ /* Set up utility functions and values */ zend_error_cb = utility_functions->error_function; zend_printf = utility_functions->printf_function; zend_write = (zend_write_func_t) utility_functions->write_function; /*.....省略部分......*/ }
php_output_startup(); //这个函数注意,里面有函数指针赋值 zuf.error_function = php_error_cb; zuf.printf_function = php_printf; zuf.write_function = php_output_wrapper; zuf.fopen_function = php_fopen_wrapper_for_zend; zuf.message_handler = php_message_handler_for_zend; zuf.block_interruptions = sapi_module.block_interruptions; zuf.unblock_interruptions = sapi_module.unblock_interruptions; zuf.get_configuration_directive = php_get_configuration_directive_for_zend; zuf.ticks_function = php_run_ticks; zuf.on_timeout = php_on_timeout; zuf.stream_open_function = php_stream_open_for_zend; zuf.vspprintf_function = vspprintf; zuf.getenv_function = sapi_getenv; zuf.resolve_path_function = php_resolve_path_for_zend; zend_startup(&zuf, NULL TSRMLS_CC);
static int php_output_wrapper(const char *str, uint str_length) { TSRMLS_FETCH(); return php_output_write(str, str_length TSRMLS_CC); } /* }}} */ PHPAPI int php_output_write(const char *str, size_t len TSRMLS_DC) { if (OG(flags) & PHP_OUTPUT_DISABLED) { return 0; } if (OG(flags) & PHP_OUTPUT_ACTIVATED) { php_output_op(PHP_OUTPUT_HANDLER_WRITE, str, len TSRMLS_CC); return (int) len; } return php_output_direct(str, len); } /* }}} */
php_output_direct被 php_output_startup函数赋值为php_output_stdout
PHPAPI void php_output_startup(void) { ZEND_INIT_MODULE_GLOBALS(output, php_output_init_globals, NULL); zend_hash_init(&php_output_handler_aliases, 0, NULL, NULL, 1); zend_hash_init(&php_output_handler_conflicts, 0, NULL, NULL, 1); zend_hash_init(&php_output_handler_reverse_conflicts, 0, NULL, (void (*)(void *)) zend_hash_destroy, 1); php_output_direct = php_output_stdout; } /* }}} */
/* {{{ stderr/stdout writer if not PHP_OUTPUT_ACTIVATED */ static int php_output_stdout(const char *str, size_t str_len) { fwrite(str, 1, str_len, stdout); return str_len; }
最终echo的结果是fwrite。
最后总结一下,var_dump函数把变量的具体信息都打印了出来,如果是数组的话,就循环输出;echo则是zend来解析的一个opcode,最终调用的是fwrite函数。
另外,echo中输出的是zval结构体中的value.str.val
Z_STRVAL_P(zval *expr)
#define Z_STRVAL_P(zval_p) Z_STRVAL(*zval_p) #define Z_STRVAL(zval) (zval).value.str.val typedef struct _zval_struct zval; struct _zval_struct { /* Variable information */ zvalue_value value; /* value */ zend_uint refcount__gc; zend_uchar type; /* active type */ zend_uchar is_ref__gc; }; typedef union _zvalue_value { long lval; /* long value */ double dval; /* double value */ struct { char *val; //可以看到echo输出的这部分内容 int len; } str; HashTable *ht; /* hash table value */ zend_object_value obj; zend_ast *ast; } zvalue_value;
针对echo 打印的具体内容分析,可以看到
zend_make_printable_zval(expr, &expr_copy, &use_copy); //分析这个函数
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: Z_STRVAL_P(expr_copy) = (char *) emalloc(sizeof("Resource id #") - 1 + MAX_LENGTH_OF_LONG); Z_STRLEN_P(expr_copy) = snprintf(Z_STRVAL_P(expr_copy), sizeof("Resource id #") - 1 + MAX_LENGTH_OF_LONG, "Resource id #%ld", Z_LVAL_P(expr)); break; case IS_ARRAY: zend_error(E_NOTICE, "Array to string conversion"); Z_STRLEN_P(expr_copy) = sizeof("Array") - 1; Z_STRVAL_P(expr_copy) = estrndup("Array", Z_STRLEN_P(expr_copy)); break; case IS_OBJECT: { TSRMLS_FETCH(); if (zend_std_cast_object_tostring(expr, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) { break; } if (Z_OBJ_HANDLER_P(expr, cast_object)) { zval *val; ALLOC_ZVAL(val); INIT_PZVAL_COPY(val, expr); zval_copy_ctor(val); if (Z_OBJ_HANDLER_P(expr, cast_object)(val, expr_copy, IS_STRING TSRMLS_CC) == SUCCESS) { zval_ptr_dtor(&val); break; } zval_ptr_dtor(&val); } if (!Z_OBJ_HANDLER_P(expr, cast_object) && Z_OBJ_HANDLER_P(expr, get)) { zval *z = Z_OBJ_HANDLER_P(expr, get)(expr TSRMLS_CC); Z_ADDREF_P(z); if (Z_TYPE_P(z) != IS_OBJECT) { zend_make_printable_zval(z, expr_copy, use_copy); if (*use_copy) { zval_ptr_dtor(&z); } else { ZVAL_ZVAL(expr_copy, z, 0, 1); *use_copy = 1; } return; } zval_ptr_dtor(&z); } zend_error(EG(exception) ? E_ERROR : E_RECOVERABLE_ERROR, "Object of class %s could not be converted to string", Z_OBJCE_P(expr)->name); Z_STRLEN_P(expr_copy) = 0; Z_STRVAL_P(expr_copy) = STR_EMPTY_ALLOC(); } break; 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; } /* }}} */
其他的类型确实很好解决,object的话,需要先找找tostring函数,然后再查找object的两个句柄函数cast_object和get
继续挖掘object的cast_object和get发现
ZEND_API zend_object_handlers std_object_handlers = { zend_objects_store_add_ref, /* add_ref */ zend_objects_store_del_ref, /* del_ref */ zend_objects_clone_obj, /* clone_obj */ zend_std_read_property, /* read_property */ zend_std_write_property, /* write_property */ zend_std_read_dimension, /* read_dimension */ zend_std_write_dimension, /* write_dimension */ zend_std_get_property_ptr_ptr, /* get_property_ptr_ptr */ NULL, /* get */ NULL, /* set */ zend_std_has_property, /* has_property */ zend_std_unset_property, /* unset_property */ zend_std_has_dimension, /* has_dimension */ zend_std_unset_dimension, /* unset_dimension */ zend_std_get_properties, /* get_properties */ zend_std_get_method, /* get_method */ NULL, /* call_method */ zend_std_get_constructor, /* get_constructor */ zend_std_object_get_class, /* get_class_entry */ zend_std_object_get_class_name, /* get_class_name */ zend_std_compare_objects, /* compare_objects */ zend_std_cast_object_tostring, /* cast_object */ NULL, /* count_elements */ zend_std_get_debug_info, /* get_debug_info */ zend_std_get_closure, /* get_closure */ zend_std_get_gc, /* get_gc */ NULL, /* do_operation */ NULL, /* compare */ };
水平有限,文中有错误之处还望各位指正!