From ea9b596b493e2e6177aa394e6981eae9827770ab Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Thu, 16 Jan 2014 02:35:38 -0800 Subject: [PATCH 1/2] Add __debug_info() magic method class Foo { private $val = 'Random, meaningless data'; public function count() { return 42; } public function __debug_info() { return ['count' => $this->count()]; } } $f = new Foo; var_dump($f); --- Zend/tests/debug_info.phpt | 26 ++++++++++++++++++++++++++ Zend/zend.h | 1 + Zend/zend_API.c | 13 ++++++++++++- Zend/zend_API.h | 1 + Zend/zend_compile.c | 12 ++++++++++++ Zend/zend_compile.h | 1 + Zend/zend_object_handlers.c | 35 ++++++++++++++++++++++++++++++++--- 7 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/debug_info.phpt diff --git a/Zend/tests/debug_info.phpt b/Zend/tests/debug_info.phpt new file mode 100644 index 0000000000000..7001748fc858d --- /dev/null +++ b/Zend/tests/debug_info.phpt @@ -0,0 +1,26 @@ +--TEST-- +Testing __debug_info() magic method +--FILE-- +1, "\0*\0b"=>2, "\0Foo\0c"=>3]; + } +} + +$f = new Foo; +var_dump($f); +--EXPECT-- +object(Foo)#1 (3) { + ["a"]=> + int(1) + ["b":protected]=> + int(2) + ["c":"Foo":private]=> + int(3) +} diff --git a/Zend/zend.h b/Zend/zend.h index c8b57cd043ff7..554ad38969e85 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -502,6 +502,7 @@ struct _zend_class_entry { union _zend_function *__call; union _zend_function *__callstatic; union _zend_function *__tostring; + union _zend_function *__debug_info; union _zend_function *serialize_func; union _zend_function *unserialize_func; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 553060e0a07ea..4040ee3e7c651 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2018,6 +2018,9 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce, !memcmp(lcname, ZEND_TOSTRING_FUNC_NAME, sizeof(ZEND_TOSTRING_FUNC_NAME)-1) && fptr->common.num_args != 0 ) { zend_error(error_type, "Method %s::%s() cannot take arguments", ce->name, ZEND_TOSTRING_FUNC_NAME); + } else if (name_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME) - 1 && + !memcmp(lcname, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && fptr->common.num_args != 0) { + zend_error(error_type, "Method %s::%s() cannot take arguments", ce->name, ZEND_DEBUGINFO_FUNC_NAME); } } /* }}} */ @@ -2031,7 +2034,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL; + zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debug_info = NULL; const char *lowercase_name; int fname_len; const char *lc_class_name = NULL; @@ -2180,6 +2183,8 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio __unset = reg_function; } else if ((fname_len == sizeof(ZEND_ISSET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME) - 1)) { __isset = reg_function; + } else if ((fname_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME) - 1)) { + __debug_info = reg_function; } else { reg_function = NULL; } @@ -2218,6 +2223,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio scope->__set = __set; scope->__unset = __unset; scope->__isset = __isset; + scope->__debug_info = __debug_info; if (ctor) { ctor->common.fn_flags |= ZEND_ACC_CTOR; if (ctor->common.fn_flags & ZEND_ACC_STATIC) { @@ -2281,6 +2287,11 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio } __isset->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC; } + if (__debug_info) { + if (__debug_info->common.fn_flags & ZEND_ACC_STATIC) { + zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __debug_info->common.function_name); + } + } efree((char*)lc_class_name); } return SUCCESS; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 569407b19a7ef..97631adcd16a6 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -197,6 +197,7 @@ typedef struct _zend_fcall_info_cache { class_container.__set = handle_propset; \ class_container.__unset = handle_propunset; \ class_container.__isset = handle_propisset; \ + class_container.__debug_info = NULL; \ class_container.serialize_func = NULL; \ class_container.unserialize_func = NULL; \ class_container.serialize = NULL; \ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index f789e3397f25c..b92894da2cc62 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1622,6 +1622,11 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __invoke() must have public visibility and cannot be static"); } + + } else if ((name_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __debug_info() must have public visibility and cannot be static"); + } } } else { char *class_lcname; @@ -1682,6 +1687,11 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { zend_error(E_WARNING, "The magic method __invoke() must have public visibility and cannot be static"); } + } else if ((name_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1))) { + if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { + zend_error(E_WARNING, "The magic method __debug_info() must have public visibility and cannot be static"); + } + CG(active_class_entry)->__debug_info = (zend_function *) CG(active_op_array); } else if (!(fn_flags & ZEND_ACC_STATIC)) { CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC; } @@ -3954,6 +3964,8 @@ static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint ce->__callstatic = fe; } else if (!strncmp(mname, ZEND_TOSTRING_FUNC_NAME, mname_len)) { ce->__tostring = fe; + } else if (!strncmp(mname, ZEND_DEBUGINFO_FUNC_NAME, mname_len)) { + ce->__debug_info = fe; } else if (ce->name_length + 1 == mname_len) { char *lowercase_name = emalloc(ce->name_length + 1); zend_str_tolower_copy(lowercase_name, ce->name, ce->name_length); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index f884df19466a5..7db6293d36f64 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -873,6 +873,7 @@ END_EXTERN_C() #define ZEND_TOSTRING_FUNC_NAME "__tostring" #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" #define ZEND_INVOKE_FUNC_NAME "__invoke" +#define ZEND_DEBUGINFO_FUNC_NAME "__debug_info" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 0af8e278b2d3d..73219d3ac6b09 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -138,8 +138,37 @@ ZEND_API HashTable *zend_std_get_gc(zval *object, zval ***table, int *n TSRMLS_D ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */ { - *is_temp = 0; - return zend_std_get_properties(object TSRMLS_CC); + zend_class_entry *ce = Z_OBJCE_P(object); + zval *retval = NULL; + + if (!ce->__debug_info) { + *is_temp = 0; + return Z_OBJ_HANDLER_P(object, get_properties) + ? Z_OBJ_HANDLER_P(object, get_properties)(object TSRMLS_CC) + : NULL; + } + + zend_call_method_with_0_params(&object, ce, &ce->__debug_info, ZEND_DEBUGINFO_FUNC_NAME, &retval); + if (retval && Z_TYPE_P(retval) == IS_ARRAY) { + HashTable *ht = Z_ARRVAL_P(retval); + if (Z_REFCOUNT_P(retval) <= 1) { + *is_temp = 1; + efree(retval); + return ht; + } else { + *is_temp = 0; + zval_ptr_dtor(&retval); + } + return ht; + } + if (retval && Z_TYPE_P(retval) == IS_NULL) { + zval ret; + array_init(&ret); + *is_temp = 1; + return Z_ARRVAL(ret); + } + + zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array"); } /* }}} */ @@ -1639,7 +1668,7 @@ ZEND_API zend_object_handlers std_object_handlers = { zend_std_compare_objects, /* compare_objects */ zend_std_cast_object_tostring, /* cast_object */ NULL, /* count_elements */ - NULL, /* get_debug_info */ + zend_std_get_debug_info, /* get_debug_info */ zend_std_get_closure, /* get_closure */ zend_std_get_gc, /* get_gc */ NULL, /* do_operation */ From ef9a1646c4045b7331e5184c5ca46d4162ed011f Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Thu, 16 Jan 2014 06:10:13 -0800 Subject: [PATCH 2/2] Rename __debug_info to __debugInfo --- Zend/tests/debug_info.phpt | 4 ++-- Zend/zend.h | 2 +- Zend/zend_API.c | 12 ++++++------ Zend/zend_API.h | 2 +- Zend/zend_compile.c | 8 ++++---- Zend/zend_compile.h | 2 +- Zend/zend_object_handlers.c | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Zend/tests/debug_info.phpt b/Zend/tests/debug_info.phpt index 7001748fc858d..c7c9f23be58c5 100644 --- a/Zend/tests/debug_info.phpt +++ b/Zend/tests/debug_info.phpt @@ -1,5 +1,5 @@ --TEST-- -Testing __debug_info() magic method +Testing __debugInfo() magic method --FILE-- 1, "\0*\0b"=>2, "\0Foo\0c"=>3]; } } diff --git a/Zend/zend.h b/Zend/zend.h index 554ad38969e85..b6e5ef0e4af1c 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -502,7 +502,7 @@ struct _zend_class_entry { union _zend_function *__call; union _zend_function *__callstatic; union _zend_function *__tostring; - union _zend_function *__debug_info; + union _zend_function *__debugInfo; union _zend_function *serialize_func; union _zend_function *unserialize_func; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 4040ee3e7c651..5b9279124a14b 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2034,7 +2034,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debug_info = NULL; + zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debugInfo = NULL; const char *lowercase_name; int fname_len; const char *lc_class_name = NULL; @@ -2184,7 +2184,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio } else if ((fname_len == sizeof(ZEND_ISSET_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_ISSET_FUNC_NAME, sizeof(ZEND_ISSET_FUNC_NAME) - 1)) { __isset = reg_function; } else if ((fname_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && !memcmp(lowercase_name, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME) - 1)) { - __debug_info = reg_function; + __debugInfo = reg_function; } else { reg_function = NULL; } @@ -2223,7 +2223,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio scope->__set = __set; scope->__unset = __unset; scope->__isset = __isset; - scope->__debug_info = __debug_info; + scope->__debugInfo = __debugInfo; if (ctor) { ctor->common.fn_flags |= ZEND_ACC_CTOR; if (ctor->common.fn_flags & ZEND_ACC_STATIC) { @@ -2287,9 +2287,9 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio } __isset->common.fn_flags &= ~ZEND_ACC_ALLOW_STATIC; } - if (__debug_info) { - if (__debug_info->common.fn_flags & ZEND_ACC_STATIC) { - zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __debug_info->common.function_name); + if (__debugInfo) { + if (__debugInfo->common.fn_flags & ZEND_ACC_STATIC) { + zend_error(error_type, "Method %s::%s() cannot be static", scope->name, __debugInfo->common.function_name); } } efree((char*)lc_class_name); diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 97631adcd16a6..62a4a8307c5b9 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -197,7 +197,7 @@ typedef struct _zend_fcall_info_cache { class_container.__set = handle_propset; \ class_container.__unset = handle_propunset; \ class_container.__isset = handle_propisset; \ - class_container.__debug_info = NULL; \ + class_container.__debugInfo = NULL; \ class_container.serialize_func = NULL; \ class_container.unserialize_func = NULL; \ class_container.serialize = NULL; \ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index b92894da2cc62..34cc6cd5803ed 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1625,7 +1625,7 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } else if ((name_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1))) { if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { - zend_error(E_WARNING, "The magic method __debug_info() must have public visibility and cannot be static"); + zend_error(E_WARNING, "The magic method __debugInfo() must have public visibility and cannot be static"); } } } else { @@ -1689,9 +1689,9 @@ void zend_do_begin_function_declaration(znode *function_token, znode *function_n } } else if ((name_len == sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1) && (!memcmp(lcname, ZEND_DEBUGINFO_FUNC_NAME, sizeof(ZEND_DEBUGINFO_FUNC_NAME)-1))) { if (fn_flags & ((ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC) ^ ZEND_ACC_PUBLIC)) { - zend_error(E_WARNING, "The magic method __debug_info() must have public visibility and cannot be static"); + zend_error(E_WARNING, "The magic method __debugInfo() must have public visibility and cannot be static"); } - CG(active_class_entry)->__debug_info = (zend_function *) CG(active_op_array); + CG(active_class_entry)->__debugInfo = (zend_function *) CG(active_op_array); } else if (!(fn_flags & ZEND_ACC_STATIC)) { CG(active_op_array)->fn_flags |= ZEND_ACC_ALLOW_STATIC; } @@ -3965,7 +3965,7 @@ static void zend_add_magic_methods(zend_class_entry* ce, const char* mname, uint } else if (!strncmp(mname, ZEND_TOSTRING_FUNC_NAME, mname_len)) { ce->__tostring = fe; } else if (!strncmp(mname, ZEND_DEBUGINFO_FUNC_NAME, mname_len)) { - ce->__debug_info = fe; + ce->__debugInfo = fe; } else if (ce->name_length + 1 == mname_len) { char *lowercase_name = emalloc(ce->name_length + 1); zend_str_tolower_copy(lowercase_name, ce->name, ce->name_length); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 7db6293d36f64..9974c18431ab7 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -873,7 +873,7 @@ END_EXTERN_C() #define ZEND_TOSTRING_FUNC_NAME "__tostring" #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" #define ZEND_INVOKE_FUNC_NAME "__invoke" -#define ZEND_DEBUGINFO_FUNC_NAME "__debug_info" +#define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 73219d3ac6b09..ed5256ee57472 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -141,14 +141,14 @@ ZEND_API HashTable *zend_std_get_debug_info(zval *object, int *is_temp TSRMLS_DC zend_class_entry *ce = Z_OBJCE_P(object); zval *retval = NULL; - if (!ce->__debug_info) { + if (!ce->__debugInfo) { *is_temp = 0; return Z_OBJ_HANDLER_P(object, get_properties) ? Z_OBJ_HANDLER_P(object, get_properties)(object TSRMLS_CC) : NULL; } - zend_call_method_with_0_params(&object, ce, &ce->__debug_info, ZEND_DEBUGINFO_FUNC_NAME, &retval); + zend_call_method_with_0_params(&object, ce, &ce->__debugInfo, ZEND_DEBUGINFO_FUNC_NAME, &retval); if (retval && Z_TYPE_P(retval) == IS_ARRAY) { HashTable *ht = Z_ARRVAL_P(retval); if (Z_REFCOUNT_P(retval) <= 1) {