summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSara Golemon <pollita@php.net>2014-02-17 19:13:00 -0800
committerSara Golemon <pollita@php.net>2014-02-17 19:33:56 -0800
commit1e752ce9c57310cced0a5ba8399778c1f500f2b4 (patch)
tree9eb8723cb77cb2c5e5649886252a8c6d76dbe07a
parent4e4d319e6207e64d62a65c4238105f250cd13d0d (diff)
downloadphp-git-1e752ce9c57310cced0a5ba8399778c1f500f2b4.tar.gz
Add __debugInfo() magic method
class Foo { private $val = 'Random, meaningless data'; public function count() { return 42; } public function __debugInfo() { return ['count' => $this->count()]; } } $f = new Foo; var_dump($f);
-rw-r--r--NEWS3
-rw-r--r--Zend/tests/debug_info.phpt26
-rw-r--r--Zend/zend.h1
-rw-r--r--Zend/zend_API.c13
-rw-r--r--Zend/zend_API.h1
-rw-r--r--Zend/zend_compile.c12
-rw-r--r--Zend/zend_compile.h1
-rw-r--r--Zend/zend_object_handlers.c35
8 files changed, 88 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 1c224759bb..53dfe6a0fa 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,9 @@ PHP NEWS
It also made pg_meta_data() return "is enum" always.
(Yasuo)
+- Core
+ . Expose get_debug_info class hook as __debugInfo() magic method. (Sara)
+
13 Feb 2014, PHP 5.6.0 Alpha 2
- Core:
. Added T_POW (**) operator
diff --git a/Zend/tests/debug_info.phpt b/Zend/tests/debug_info.phpt
new file mode 100644
index 0000000000..c7c9f23be5
--- /dev/null
+++ b/Zend/tests/debug_info.phpt
@@ -0,0 +1,26 @@
+--TEST--
+Testing __debugInfo() magic method
+--FILE--
+<?php
+
+class Foo {
+ public $d = 4;
+ protected $e = 5;
+ private $f = 6;
+
+ public function __debugInfo() {
+ return ['a'=>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 ac2b28a1ef..82e81370e8 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 *__debugInfo;
union _zend_function *serialize_func;
union _zend_function *unserialize_func;
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index c17af98136..ed13c6a7d6 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, *__debugInfo = 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)) {
+ __debugInfo = 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->__debugInfo = __debugInfo;
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 (__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);
}
return SUCCESS;
diff --git a/Zend/zend_API.h b/Zend/zend_API.h
index 0a7695b1c8..efc267f918 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.__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 f789e3397f..34cc6cd580 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 __debugInfo() 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 __debugInfo() must have public visibility and cannot be static");
+ }
+ 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;
}
@@ -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->__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 f884df1946..9974c18431 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 "__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 991dca2eec..bc65313962 100644
--- a/Zend/zend_object_handlers.c
+++ b/Zend/zend_object_handlers.c
@@ -137,8 +137,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->__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->__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) {
+ *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");
}
/* }}} */
@@ -1638,7 +1667,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 */