echo 和 var_dump

在一个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 */
};

水平有限,文中有错误之处还望各位指正!

你可能感兴趣的:(echo 和 var_dump)