mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
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);
This commit is contained in:
parent
4e4d319e62
commit
1e752ce9c5
3
NEWS
3
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
|
||||
|
26
Zend/tests/debug_info.phpt
Normal file
26
Zend/tests/debug_info.phpt
Normal file
@ -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)
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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; \
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user