]> granicus.if.org Git - php/commitdiff
Add __debugInfo() magic method
authorSara Golemon <pollita@php.net>
Tue, 18 Feb 2014 03:13:00 +0000 (19:13 -0800)
committerSara Golemon <pollita@php.net>
Tue, 18 Feb 2014 03:33:56 +0000 (19:33 -0800)
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);

NEWS
Zend/tests/debug_info.phpt [new file with mode: 0644]
Zend/zend.h
Zend/zend_API.c
Zend/zend_API.h
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_object_handlers.c

diff --git a/NEWS b/NEWS
index 1c224759bb4445331efec354c3d9766789be9d19..53dfe6a0fa0e6373ad1c26dc130bbf8f45065052 100644 (file)
--- 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 (file)
index 0000000..c7c9f23
--- /dev/null
@@ -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)
+}
index ac2b28a1ef0a9dd9921c2e84b0ba1574e4c7e841..82e81370e86f6caa6d3ba3bf72328a3f435a625b 100644 (file)
@@ -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;
 
index c17af9813647576f65f55559c7059542c5ec5d1d..ed13c6a7d6031bee871ac4608f7e3714efbbdda1 100644 (file)
@@ -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;
index 0a7695b1c8112f8c80f7f9edfaa2938cd37609b0..efc267f918e61bb1df802ca894efc8d8f97043db 100644 (file)
@@ -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;                                               \
index f789e3397f25c5e5d79dc5d017209ad30c9e7abe..34cc6cd5803ed78abe454630fc25122c979e30de 100644 (file)
@@ -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);
index f884df19466a522f7543e87dd29d280127e38585..9974c18431ab769de59b14a443ebaffe9010eed3 100644 (file)
@@ -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 */
index 991dca2eecf0583536b78fae76eaee1b3211c01a..bc6531396273770a4ef7cee8877161593df44e2f 100644 (file)
@@ -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 */