diff --git a/Zend/tests/__debugInfo_reference.phpt b/Zend/tests/debug_info/__debugInfo_reference.phpt similarity index 100% rename from Zend/tests/__debugInfo_reference.phpt rename to Zend/tests/debug_info/__debugInfo_reference.phpt diff --git a/Zend/tests/debug_info/debug_info-error-0.0.phpt b/Zend/tests/debug_info/debug_info-error-0.0.phpt index ab41b440fc888..8967bf258bb65 100644 --- a/Zend/tests/debug_info/debug_info-error-0.0.phpt +++ b/Zend/tests/debug_info/debug_info-error-0.0.phpt @@ -17,4 +17,8 @@ $c = new C(0.0); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-0.phpt b/Zend/tests/debug_info/debug_info-error-0.phpt index 37289c6468fff..293981aede96d 100644 --- a/Zend/tests/debug_info/debug_info-error-0.phpt +++ b/Zend/tests/debug_info/debug_info-error-0.phpt @@ -17,4 +17,8 @@ $c = new C(0); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-1.0.phpt b/Zend/tests/debug_info/debug_info-error-1.0.phpt index 9b168621b5efe..651cbde6ab378 100644 --- a/Zend/tests/debug_info/debug_info-error-1.0.phpt +++ b/Zend/tests/debug_info/debug_info-error-1.0.phpt @@ -17,4 +17,8 @@ $c = new C(1.0); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-1.phpt b/Zend/tests/debug_info/debug_info-error-1.phpt index ae01a6055c004..96629413eb0cb 100644 --- a/Zend/tests/debug_info/debug_info-error-1.phpt +++ b/Zend/tests/debug_info/debug_info-error-1.phpt @@ -17,4 +17,8 @@ $c = new C(1); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-debug_zval_dump-basic.phpt b/Zend/tests/debug_info/debug_info-error-debug_zval_dump-basic.phpt new file mode 100644 index 0000000000000..0aef96586286a --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-debug_zval_dump-basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar (debug_zval_dump) +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$c = new C(true); +debug_zval_dump($c); + +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): debug_zval_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-debug_zval_dump-within-array.phpt b/Zend/tests/debug_info/debug_info-error-debug_zval_dump-within-array.phpt new file mode 100644 index 0000000000000..d1a1ea39b1f72 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-debug_zval_dump-within-array.phpt @@ -0,0 +1,28 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar inside an array (debug_zval_dump) +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$a = [ + 'foo', + new C(true), + 'bar', +]; +debug_zval_dump($a); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): debug_zval_dump(Array) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-debug_zval_dump-within-object.phpt b/Zend/tests/debug_info/debug_info-error-debug_zval_dump-within-object.phpt new file mode 100644 index 0000000000000..c7d1a0af924e0 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-debug_zval_dump-within-object.phpt @@ -0,0 +1,27 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar inside an object (debug_zval_dump) +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$o = new stdClass(); +$o->foo = 'foo'; +$o->c = new C(true); +$o->bar = 'bar'; +debug_zval_dump($o); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): debug_zval_dump(Object(stdClass)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-empty_str.phpt b/Zend/tests/debug_info/debug_info-error-empty_str.phpt index bbab78cd82021..2c1d8578b7bec 100644 --- a/Zend/tests/debug_info/debug_info-error-empty_str.phpt +++ b/Zend/tests/debug_info/debug_info-error-empty_str.phpt @@ -17,4 +17,8 @@ $c = new C(""); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-false.phpt b/Zend/tests/debug_info/debug_info-error-false.phpt index 3e48372c420c2..2e7475d6c3506 100644 --- a/Zend/tests/debug_info/debug_info-error-false.phpt +++ b/Zend/tests/debug_info/debug_info-error-false.phpt @@ -17,4 +17,8 @@ $c = new C(false); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-object.phpt b/Zend/tests/debug_info/debug_info-error-object.phpt index 42e073999908c..c867e83f08ac8 100644 --- a/Zend/tests/debug_info/debug_info-error-object.phpt +++ b/Zend/tests/debug_info/debug_info-error-object.phpt @@ -17,4 +17,8 @@ $c = new C(new stdClass); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-print_r-basic.phpt b/Zend/tests/debug_info/debug_info-error-print_r-basic.phpt new file mode 100644 index 0000000000000..391e88932a725 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-print_r-basic.phpt @@ -0,0 +1,25 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar (print_r) +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$c = new C(true); +print_r($c); + +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): print_r(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-print_r-within-array.phpt b/Zend/tests/debug_info/debug_info-error-print_r-within-array.phpt new file mode 100644 index 0000000000000..7eb2b7fff0c20 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-print_r-within-array.phpt @@ -0,0 +1,28 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar inside an array (print_r) +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$a = [ + 'foo', + new C(true), + 'bar', +]; +print_r($a); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): print_r(Array) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-print_r-within-object.phpt b/Zend/tests/debug_info/debug_info-error-print_r-within-object.phpt new file mode 100644 index 0000000000000..68f01f862fcf8 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-print_r-within-object.phpt @@ -0,0 +1,27 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar inside an object (print_r) +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$o = new stdClass(); +$o->foo = 'foo'; +$o->c = new C(true); +$o->bar = 'bar'; +print_r($o); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): print_r(Object(stdClass)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-resource.phpt b/Zend/tests/debug_info/debug_info-error-resource.phpt index ccacce7a74b45..62e73984fa6ef 100644 --- a/Zend/tests/debug_info/debug_info-error-resource.phpt +++ b/Zend/tests/debug_info/debug_info-error-resource.phpt @@ -19,4 +19,8 @@ $c = new C(fopen("data:text/plain,Foo", 'r')); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-str.phpt b/Zend/tests/debug_info/debug_info-error-str.phpt index 85d3f63b97e05..391cf514423c5 100644 --- a/Zend/tests/debug_info/debug_info-error-str.phpt +++ b/Zend/tests/debug_info/debug_info-error-str.phpt @@ -17,4 +17,8 @@ $c = new C("foo"); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-true.phpt b/Zend/tests/debug_info/debug_info-error-true.phpt index 3843c6a7a14a5..f0bb2718340ae 100644 --- a/Zend/tests/debug_info/debug_info-error-true.phpt +++ b/Zend/tests/debug_info/debug_info-error-true.phpt @@ -17,4 +17,8 @@ $c = new C(true); var_dump($c); ?> --EXPECTF-- -Fatal error: __debuginfo() must return an array in %s on line %d +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(C)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-within-array.phpt b/Zend/tests/debug_info/debug_info-error-within-array.phpt new file mode 100644 index 0000000000000..87414b379fd9f --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-within-array.phpt @@ -0,0 +1,28 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar inside an array +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$a = [ + 'foo', + new C(true), + 'bar', +]; +var_dump($a); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Array) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-error-within-object.phpt b/Zend/tests/debug_info/debug_info-error-within-object.phpt new file mode 100644 index 0000000000000..e8dfd823c6716 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-error-within-object.phpt @@ -0,0 +1,27 @@ +--TEST-- +Testing __debugInfo() magic method with bad returns scalar inside an object +--FILE-- +val; + } + public function __construct($val) { + $this->val = $val; + } +} + +$o = new stdClass(); +$o->foo = 'foo'; +$o->c = new C(true); +$o->bar = 'bar'; +var_dump($o); +?> +--EXPECTF-- +Fatal error: Uncaught TypeError: __debuginfo() must return an array in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(stdClass)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-null-deprecation-promoted.phpt b/Zend/tests/debug_info/debug_info-null-deprecation-promoted.phpt new file mode 100644 index 0000000000000..cb5e2b8255407 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-null-deprecation-promoted.phpt @@ -0,0 +1,30 @@ +--TEST-- +Testing __debugInfo() magic method with deprecated return null +--FILE-- + +--EXPECTF-- +object(Bar)#2 (0) { +} + +Fatal error: Uncaught Exception: Returning null from Bar::__debugInfo() is deprecated, return an empty array instead in %s:%d +Stack trace: +#0 [internal function]: {closure:%s:%d}(8192, 'Returning null ...', '%s', %d) +#1 %s(%d): var_dump(Object(Bar)) +#2 {main} + thrown in %s on line %d diff --git a/Zend/tests/debug_info/debug_info-null.phpt b/Zend/tests/debug_info/debug_info-null.phpt new file mode 100644 index 0000000000000..d9cba939c7777 --- /dev/null +++ b/Zend/tests/debug_info/debug_info-null.phpt @@ -0,0 +1,20 @@ +--TEST-- +Testing __debugInfo() magic method with deprecated return null +--FILE-- + +--EXPECTF-- +Deprecated: Returning null from Bar::__debugInfo() is deprecated, return an empty array instead in %s on line %d +object(Bar)#%d (0) { +} diff --git a/Zend/tests/debug_info/debug_info.phpt b/Zend/tests/debug_info/debug_info.phpt index bff5876777ba2..c961b5c44efac 100644 --- a/Zend/tests/debug_info/debug_info.phpt +++ b/Zend/tests/debug_info/debug_info.phpt @@ -13,22 +13,12 @@ class Foo { } } -class Bar { - public $val = 123; - - public function __debugInfo() { - return null; - } -} - $f = new Foo; var_dump($f); -$b = new Bar; -var_dump($b); ?> ---EXPECTF-- -object(Foo)#%d (3) { +--EXPECT-- +object(Foo)#1 (3) { ["a"]=> int(1) ["b":protected]=> @@ -36,7 +26,3 @@ object(Foo)#%d (3) { ["c":"Foo":private]=> int(3) } - -Deprecated: Returning null from Bar::__debugInfo() is deprecated, return an empty array instead in %s on line %d -object(Bar)#%d (0) { -} diff --git a/Zend/zend.c b/Zend/zend.c index 4d024444a4be9..474d39c6934f5 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -375,9 +375,9 @@ ZEND_API zend_string *zend_strpprintf_unchecked(size_t max_len, const char *form } /* }}} */ -static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent); +static bool zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent); -static void print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object) /* {{{ */ +static bool print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object) /* {{{ */ { zval *tmp; zend_string *string_key; @@ -398,7 +398,7 @@ static void print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object if (is_object) { const char *prop_name, *class_name; size_t prop_len; - int mangled = zend_unmangle_property_name_ex(string_key, &class_name, &prop_name, &prop_len); + zend_result mangled = zend_unmangle_property_name_ex(string_key, &class_name, &prop_name, &prop_len); smart_str_appendl(buf, prop_name, prop_len); if (class_name && mangled == SUCCESS) { @@ -417,7 +417,9 @@ static void print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object smart_str_append_long(buf, num_key); } smart_str_appends(buf, "] => "); - zend_print_zval_r_to_buf(buf, tmp, indent+PRINT_ZVAL_INDENT); + if (UNEXPECTED(!zend_print_zval_r_to_buf(buf, tmp, indent+PRINT_ZVAL_INDENT))) { + return false; + } smart_str_appends(buf, "\n"); } ZEND_HASH_FOREACH_END(); indent -= PRINT_ZVAL_INDENT; @@ -425,6 +427,7 @@ static void print_hash(smart_str *buf, HashTable *ht, int indent, bool is_object smart_str_appendc(buf, ' '); } smart_str_appends(buf, ")\n"); + return true; } /* }}} */ @@ -541,29 +544,28 @@ ZEND_API void zend_print_flat_zval_r(zval *expr) smart_str_free(&buf); } -static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* {{{ */ +static bool zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* {{{ */ { switch (Z_TYPE_P(expr)) { - case IS_ARRAY: + case IS_ARRAY: { smart_str_appends(buf, "Array\n"); if (!(GC_FLAGS(Z_ARRVAL_P(expr)) & GC_IMMUTABLE)) { if (GC_IS_RECURSIVE(Z_ARRVAL_P(expr))) { smart_str_appends(buf, " *RECURSION*"); - return; + return true; } GC_PROTECT_RECURSION(Z_ARRVAL_P(expr)); } - print_hash(buf, Z_ARRVAL_P(expr), indent, false); + bool status = print_hash(buf, Z_ARRVAL_P(expr), indent, false); GC_TRY_UNPROTECT_RECURSION(Z_ARRVAL_P(expr)); - break; + return status; + } case IS_OBJECT: { - HashTable *properties; - zend_object *zobj = Z_OBJ_P(expr); uint32_t *guard = zend_get_recursion_guard(zobj); zend_string *class_name = Z_OBJ_HANDLER_P(expr, get_class_name)(zobj); - smart_str_appends(buf, ZSTR_VAL(class_name)); + smart_str_append(buf, class_name); zend_string_release_ex(class_name, 0); if (!(zobj->ce->ce_flags & ZEND_ACC_ENUM)) { @@ -579,37 +581,37 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) { smart_str_appends(buf, " *RECURSION*"); - return; + return true; } - if ((properties = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_DEBUG)) == NULL) { - print_hash(buf, (HashTable*) &zend_empty_array, indent, true); - break; + HashTable *properties = zend_get_properties_for(expr, ZEND_PROP_PURPOSE_DEBUG); + /* Either we must have properties as a HashTable (even if empty) or an exception if NULL is returned */ + ZEND_ASSERT(properties || EG(exception)); + if (UNEXPECTED(properties == NULL)) { + return false; } ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj); - print_hash(buf, properties, indent, true); + bool status = print_hash(buf, properties, indent, true); ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj); - zend_release_properties(properties); - break; + zend_array_release(properties); + return status; } case IS_LONG: smart_str_append_long(buf, Z_LVAL_P(expr)); - break; + return true; case IS_REFERENCE: - zend_print_zval_r_to_buf(buf, Z_REFVAL_P(expr), indent); - break; + return zend_print_zval_r_to_buf(buf, Z_REFVAL_P(expr), indent); case IS_STRING: smart_str_append(buf, Z_STR_P(expr)); - break; - default: - { - zend_string *str = zval_get_string_func(expr); - smart_str_append(buf, str); - zend_string_release_ex(str, 0); - } - break; + return true; + default: { + zend_string *str = zval_get_string_func(expr); + smart_str_append(buf, str); + zend_string_release_ex(str, 0); + return true; + } } } /* }}} */ @@ -617,7 +619,11 @@ static void zend_print_zval_r_to_buf(smart_str *buf, zval *expr, int indent) /* ZEND_API zend_string *zend_print_zval_r_to_str(zval *expr, int indent) /* {{{ */ { smart_str buf = {0}; - zend_print_zval_r_to_buf(&buf, expr, indent); + bool status = zend_print_zval_r_to_buf(&buf, expr, indent); + if (UNEXPECTED(!status)) { + smart_str_free(&buf); + return NULL; + } smart_str_0(&buf); return buf.s; } @@ -626,6 +632,10 @@ ZEND_API zend_string *zend_print_zval_r_to_str(zval *expr, int indent) /* {{{ */ ZEND_API void zend_print_zval_r(zval *expr, int indent) /* {{{ */ { zend_string *str = zend_print_zval_r_to_str(expr, indent); + /* If an exception was triggered while printing the zval */ + if (UNEXPECTED(str == NULL)) { + return; + } zend_write(ZSTR_VAL(str), ZSTR_LEN(str)); zend_string_release_ex(str, 0); } diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index cba95810ba496..8c89812577279 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -183,6 +183,9 @@ ZEND_METHOD(SensitiveParameterValue, __debugInfo) static HashTable *attributes_sensitive_parameter_value_get_properties_for(zend_object *zobj, zend_prop_purpose purpose) { + if (purpose == ZEND_PROP_PURPOSE_DEBUG) { + return (HashTable*)&zend_empty_array; + } return NULL; } diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 470fb76ec14e1..2c822f1a9856e 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -229,9 +229,9 @@ ZEND_API HashTable *zend_std_get_debug_info(zend_object *object, int *is_temp) / return ht; } - zend_error_noreturn(E_ERROR, ZEND_DEBUGINFO_FUNC_NAME "() must return an array"); - - return NULL; /* Compilers are dumb and don't understand that noreturn means that the function does NOT need a return value... */ + zval_ptr_dtor(&retval); + zend_type_error(ZEND_DEBUGINFO_FUNC_NAME "() must return an array"); + return NULL; } /* }}} */ diff --git a/ext/dom/node.c b/ext/dom/node.c index de80dba202e99..ea9685c969470 100644 --- a/ext/dom/node.c +++ b/ext/dom/node.c @@ -469,7 +469,8 @@ zend_result dom_node_owner_document_read(dom_object *obj, zval *retval) xmlDocPtr docp = nodep->doc; if (!docp) { - return FAILURE; + ZVAL_NULL(retval); + return SUCCESS; } php_dom_create_object((xmlNodePtr) docp, retval, obj); diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 3619eaef12a5f..6a3f88738f3e6 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -523,7 +523,9 @@ static HashTable* dom_get_debug_info_helper(zend_object *object, int *is_temp) / ZEND_ASSERT(string_key != NULL); if (entry->read_func(obj, &value) == FAILURE) { - continue; + zend_array_release(debug_info); + debug_info = NULL; + goto exit; } if (Z_TYPE(value) == IS_OBJECT) { @@ -535,6 +537,7 @@ static HashTable* dom_get_debug_info_helper(zend_object *object, int *is_temp) / zend_hash_update(debug_info, string_key, &value); } ZEND_HASH_FOREACH_END(); +exit: zend_string_release_ex(object_str, false); DOM_G(suppress_warnings) = false; diff --git a/ext/dom/tests/DOMEntityReference_predefined_free.phpt b/ext/dom/tests/DOMEntityReference_predefined_free.phpt index 46e54e1b7d308..717448d97cda7 100644 --- a/ext/dom/tests/DOMEntityReference_predefined_free.phpt +++ b/ext/dom/tests/DOMEntityReference_predefined_free.phpt @@ -8,7 +8,7 @@ $ref = new DOMEntityReference("amp"); var_dump($ref); ?> --EXPECTF-- -object(DOMEntityReference)#1 (17) { +object(DOMEntityReference)#1 (18) { ["nodeName"]=> string(3) "amp" ["nodeValue"]=> @@ -33,6 +33,8 @@ object(DOMEntityReference)#1 (17) { NULL ["isConnected"]=> bool(false) + ["ownerDocument"]=> + NULL ["namespaceURI"]=> NULL ["prefix"]=> diff --git a/ext/dom/tests/delayed_freeing/without_contructor.phpt b/ext/dom/tests/delayed_freeing/without_contructor.phpt index 4a5f63ed9accf..1f2c7aa3e3099 100644 --- a/ext/dom/tests/delayed_freeing/without_contructor.phpt +++ b/ext/dom/tests/delayed_freeing/without_contructor.phpt @@ -31,8 +31,6 @@ try { ?> --EXPECT-- -object(DOMNode)#2 (0) { -} Invalid State Error Couldn't fetch DOMNode Couldn't fetch DOMNode diff --git a/ext/dom/tests/dom_node_debugInfo.phpt b/ext/dom/tests/dom_node_debugInfo.phpt new file mode 100644 index 0000000000000..f0a2d836f8076 --- /dev/null +++ b/ext/dom/tests/dom_node_debugInfo.phpt @@ -0,0 +1,17 @@ +--TEST-- +DOMNode custom __debugInfo() should abort at the first exception +--EXTENSIONS-- +dom +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught DOMException: Invalid State Error in %s:%d +Stack trace: +#0 %s(%d): var_dump(Object(DOMNode)) +#1 {main} + thrown in %s on line %d diff --git a/ext/dom/tests/gh16316.phpt b/ext/dom/tests/gh16316.phpt index 43368746bc462..1c4b8dead2990 100644 --- a/ext/dom/tests/gh16316.phpt +++ b/ext/dom/tests/gh16316.phpt @@ -24,9 +24,5 @@ try { ?> --EXPECT-- -object(Demo)#1 (1) { - ["registerNodeNamespaces"]=> - bool(true) -} Invalid State Error Invalid State Error diff --git a/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt index bfb150b5cca89..53ce0536cebfe 100644 --- a/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt +++ b/ext/dom/tests/modern/spec/Document_implementation_createDocumentType.phpt @@ -25,7 +25,7 @@ foreach ($test_matrix as $test_item) { ?> --EXPECT-- -object(Dom\DocumentType)#3 (19) { +object(Dom\DocumentType)#3 (20) { ["name"]=> string(5) "qname" ["entities"]=> @@ -46,6 +46,8 @@ object(Dom\DocumentType)#3 (19) { NULL ["isConnected"]=> bool(false) + ["ownerDocument"]=> + NULL ["parentNode"]=> NULL ["parentElement"]=> @@ -68,7 +70,7 @@ object(Dom\DocumentType)#3 (19) { -object(Dom\DocumentType)#2 (19) { +object(Dom\DocumentType)#2 (20) { ["name"]=> string(5) "qname" ["entities"]=> @@ -89,6 +91,8 @@ object(Dom\DocumentType)#2 (19) { NULL ["isConnected"]=> bool(false) + ["ownerDocument"]=> + NULL ["parentNode"]=> NULL ["parentElement"]=> @@ -111,7 +115,7 @@ object(Dom\DocumentType)#2 (19) { -object(Dom\DocumentType)#1 (19) { +object(Dom\DocumentType)#1 (20) { ["name"]=> string(5) "qname" ["entities"]=> @@ -132,6 +136,8 @@ object(Dom\DocumentType)#1 (19) { NULL ["isConnected"]=> bool(false) + ["ownerDocument"]=> + NULL ["parentNode"]=> NULL ["parentElement"]=> @@ -154,7 +160,7 @@ object(Dom\DocumentType)#1 (19) { -object(Dom\DocumentType)#4 (19) { +object(Dom\DocumentType)#4 (20) { ["name"]=> string(5) "qname" ["entities"]=> @@ -175,6 +181,8 @@ object(Dom\DocumentType)#4 (19) { NULL ["isConnected"]=> bool(false) + ["ownerDocument"]=> + NULL ["parentNode"]=> NULL ["parentElement"]=> diff --git a/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt index fb0853939f88e..5b93e459b517e 100644 --- a/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt +++ b/ext/dom/tests/modern/xml/DTDNamedNodeMap.phpt @@ -142,7 +142,7 @@ object(Dom\Entity)#3 (17) { ["textContent"]=> NULL } -object(Dom\Notation)#4 (13) { +object(Dom\Notation)#4 (14) { ["nodeType"]=> int(12) ["nodeName"]=> @@ -151,6 +151,8 @@ object(Dom\Notation)#4 (13) { NULL ["isConnected"]=> bool(false) + ["ownerDocument"]=> + NULL ["parentNode"]=> NULL ["parentElement"]=> diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c index 86b8d29209f40..e636e7d8183f9 100644 --- a/ext/ffi/ffi.c +++ b/ext/ffi/ffi.c @@ -2068,7 +2068,7 @@ static HashTable *zend_ffi_cdata_get_debug_info(zend_object *obj, int *is_temp) switch (type->kind) { case ZEND_FFI_TYPE_VOID: - return NULL; + return (HashTable*)&zend_empty_array; case ZEND_FFI_TYPE_BOOL: case ZEND_FFI_TYPE_CHAR: case ZEND_FFI_TYPE_ENUM: @@ -2090,7 +2090,6 @@ static HashTable *zend_ffi_cdata_get_debug_info(zend_object *obj, int *is_temp) zend_hash_str_add(ht, "cdata", sizeof("cdata")-1, &tmp); *is_temp = 1; return ht; - break; case ZEND_FFI_TYPE_POINTER: if (*(void**)ptr == NULL) { ZVAL_NULL(&tmp); @@ -2145,12 +2144,10 @@ static HashTable *zend_ffi_cdata_get_debug_info(zend_object *obj, int *is_temp) // TODO: function name ??? *is_temp = 1; return ht; - break; default: ZEND_UNREACHABLE(); - break; + return NULL; } - return NULL; } /* }}} */ @@ -2299,7 +2296,7 @@ static int zend_ffi_ctype_compare_objects(zval *o1, zval *o2) /* {{{ */ static HashTable *zend_ffi_ctype_get_debug_info(zend_object *obj, int *is_temp) /* {{{ */ { - return NULL; + return (HashTable*)&zend_empty_array;; } /* }}} */ diff --git a/ext/ffi/tests/035.phpt b/ext/ffi/tests/035.phpt index bd8c6df71f97a..2b730cb016c1d 100644 --- a/ext/ffi/tests/035.phpt +++ b/ext/ffi/tests/035.phpt @@ -23,6 +23,4 @@ object(FFI\CData:uint16_t[2])#%d (2) { [1]=> int(0) } -object(FFI\CData:uint16_t[2])#%d (0) { -} FFI\Exception: Use after free() diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index a5e9ed166d8b3..37f380dba6abe 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -237,7 +237,7 @@ static HashTable* spl_fixedarray_object_get_properties_for(zend_object *obj, zen const zend_long size = intern->array.size; if (size == 0 && (!source_properties || !zend_hash_num_elements(source_properties))) { - return NULL; + return (HashTable*)&zend_empty_array; } zval *const elements = intern->array.elements; HashTable *ht = zend_new_array(size); diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index e0c4230dae27c..c9803c6f0db35 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2079,7 +2079,11 @@ PHP_FUNCTION(print_r) ZEND_PARSE_PARAMETERS_END(); if (do_return) { - RETURN_STR(zend_print_zval_r_to_str(var, 0)); + zend_string *out = zend_print_zval_r_to_str(var, 0); + if (UNEXPECTED(out == NULL)) { + RETURN_THROWS(); + } + RETURN_STR(out); } else { zend_print_zval_r(var, 0); RETURN_TRUE; diff --git a/ext/standard/info.c b/ext/standard/info.c index 952f0f92fe6e2..656ea249a0fe3 100644 --- a/ext/standard/info.c +++ b/ext/standard/info.c @@ -204,6 +204,7 @@ static ZEND_COLD void php_print_gpcse_array(char *name, size_t name_length) if (Z_TYPE_P(tmp) == IS_ARRAY) { if (!sapi_module.phpinfo_as_text) { zend_string *str = zend_print_zval_r_to_str(tmp, 0); + ZEND_ASSERT(str != NULL && "shouldn't be possible to have a debug handler triggering an exception"); php_info_print("
");
php_info_print_html_esc(ZSTR_VAL(str), ZSTR_LEN(str));
php_info_print("");
diff --git a/ext/standard/var.c b/ext/standard/var.c
index a1ef60410a338..6730d2ddd6035 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -37,58 +37,111 @@ struct php_serialize_data {
uint32_t n;
};
-#define COMMON (is_ref ? "&" : "")
+typedef bool (*php_dump_fn_t)(smart_str *buf, zval *struc, int level);
-static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
-{
+static void php_smart_str_indent(smart_str *buf, int level) {
+ for (int i = 0; i < level; i++) {
+ smart_str_appendc(buf, ' ');
+ }
+}
+
+static bool php_array_element_dump(
+ smart_str *buf,
+ zval *zv, zend_ulong index,
+ const zend_string *key,
+ int level,
+ php_dump_fn_t continuation
+) {
+ php_smart_str_indent(buf, level);
+ smart_str_appendc(buf, '[');
if (key == NULL) { /* numeric key */
- php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
+ smart_str_append_long(buf, (zend_long) index);
} else { /* string key */
- php_printf("%*c[\"", level + 1, ' ');
- PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
- php_printf("\"]=>\n");
+ smart_str_appendc(buf, '"');
+ smart_str_append(buf, key);
+ smart_str_appendc(buf, '"');
}
- php_var_dump(zv, level + 2);
+ smart_str_appends(buf, "]=>\n");
+ return continuation(buf, zv, level);
}
-/* }}} */
-static void php_object_property_dump(zend_property_info *prop_info, zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
-{
+static bool php_dump_array_common(
+ smart_str *buf,
+ HashTable *ht,
+ int level,
+ php_dump_fn_t continuation
+) {
+ bool status = true;
+ zend_ulong index;
+ zend_string *key;
+ zval *val;
+ ZEND_HASH_FOREACH_KEY_VAL(ht, index, key, val) {
+ status = php_array_element_dump(buf, val, index, key, level + 2, continuation);
+ /* An exception occurred in a debug handler */
+ if (UNEXPECTED(!status)) {
+ break;
+ }
+ } ZEND_HASH_FOREACH_END();
+ if (!(GC_FLAGS(ht) & GC_IMMUTABLE)) {
+ GC_UNPROTECT_RECURSION(ht);
+ GC_DTOR_NO_REF(ht);
+ }
+ php_smart_str_indent(buf, level);
+ smart_str_appends(buf, "}\n");
+ return status;
+}
+
+static bool php_object_property_dump(
+ smart_str *buf,
+ const zend_property_info *prop_info,
+ zval *zv, zend_ulong index,
+ const zend_string *key,
+ int level,
+ php_dump_fn_t continuation
+) {
const char *prop_name, *class_name;
+ php_smart_str_indent(buf, level);
+ smart_str_appendc(buf, '[');
if (key == NULL) { /* numeric key */
- php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
+ smart_str_append_long(buf, (zend_long) index);
} else { /* string key */
- int unmangle = zend_unmangle_property_name(key, &class_name, &prop_name);
- php_printf("%*c[", level + 1, ' ');
+ zend_result unmangle = zend_unmangle_property_name(key, &class_name, &prop_name);
+ smart_str_appendc(buf, '"');
if (class_name && unmangle == SUCCESS) {
+ smart_str_appends(buf, prop_name);
+ smart_str_appendc(buf, '"');
+ smart_str_appendc(buf, ':');
if (class_name[0] == '*') {
- php_printf("\"%s\":protected", prop_name);
+ smart_str_appends(buf, "protected");
} else {
- php_printf("\"%s\":\"%s\":private", prop_name, class_name);
+ smart_str_appendc(buf, '"');
+ smart_str_appends(buf, class_name);
+ smart_str_appends(buf, "\":private");
}
} else {
- php_printf("\"");
- PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
- php_printf("\"");
+ smart_str_append(buf, key);
+ smart_str_appendc(buf, '"');
}
- ZEND_PUTS("]=>\n");
}
+ smart_str_appends(buf, "]=>\n");
if (Z_TYPE_P(zv) == IS_UNDEF) {
ZEND_ASSERT(ZEND_TYPE_IS_SET(prop_info->type));
zend_string *type_str = zend_type_to_string(prop_info->type);
- php_printf("%*cuninitialized(%s)\n",
- level + 1, ' ', ZSTR_VAL(type_str));
+ php_smart_str_indent(buf, level);
+ smart_str_appends(buf, "uninitialized(");
+ smart_str_append(buf, type_str);
+ smart_str_appends(buf, ")\n");
zend_string_release(type_str);
+ return true;
} else {
- php_var_dump(zv, level + 2);
+ return continuation(buf, zv, level);
}
}
-/* }}} */
-static const char *php_var_dump_object_prefix(zend_object *obj) {
+static const char *php_var_dump_object_prefix(const zend_object *obj) {
if (EXPECTED(!zend_object_is_lazy(obj))) {
return "";
}
@@ -100,343 +153,325 @@ static const char *php_var_dump_object_prefix(zend_object *obj) {
return "lazy ghost ";
}
-PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
-{
- HashTable *myht;
- zend_string *class_name;
- int is_ref = 0;
+static bool php_var_dump_ex(smart_str *buf, zval *struc, int level);
+static bool php_debug_zval_dump_ex(smart_str *buf, zval *struc, int level);
+
+static bool php_dump_object_common(
+ smart_str *buf,
+ zval *object,
+ int level,
+ bool is_ref,
+ php_dump_fn_t continuation
+) {
+ const zend_class_entry *ce = Z_OBJCE_P(object);
+ // TODO: Handle enums separately for debug_zval_dump() like in var_dump() and print_r()?
+ if (continuation == php_var_dump_ex && ce->ce_flags & ZEND_ACC_ENUM) {
+ const zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(object));
+
+ if (is_ref) { smart_str_appendc(buf, '&'); }
+ smart_str_appends(buf, "enum(");
+ smart_str_append(buf, ce->name);
+ smart_str_appends(buf, "::");
+ smart_str_append(buf, Z_STR_P(case_name_zval));
+ smart_str_appends(buf, ")\n");
+ return true;
+ }
+ /* Check if this is already recursing on the object before calling zend_get_properties_for,
+ * to allow infinite recursion detection to work even if classes return temporary arrays,
+ * and to avoid the need to update the properties table in place to reflect the state
+ * if the result won't be used. (https://github.com/php/php-src/issues/8044) */
+ zend_object *zobj = Z_OBJ_P(object);
+ uint32_t *guard = zend_get_recursion_guard(zobj);
+ if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
+ smart_str_appends(buf, "*RECURSION*\n");
+ return true;
+ }
+ ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
+
+ HashTable *myht = zend_get_properties_for(object, ZEND_PROP_PURPOSE_DEBUG);
+ /* Either we must have properties as a HashTable (even if empty) or an exception if NULL is returned */
+ ZEND_ASSERT(myht || EG(exception));
+ if (UNEXPECTED(myht == NULL)) {
+ ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
+ return false;
+ }
+
+ zend_string *class_name = Z_OBJ_HANDLER_P(object, get_class_name)(Z_OBJ_P(object));
+ const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(object));
+
+ if (is_ref) { smart_str_appendc(buf, '&'); }
+ smart_str_appends(buf, prefix);
+ smart_str_appends(buf, "object(");
+ /* We use smart_str_appends() to be able to truncate anonymous class names which are separated with a nul byte */
+ smart_str_appends(buf, ZSTR_VAL(class_name));
+ zend_string_release_ex(class_name, false);
+
+ smart_str_appends(buf, ")#");
+ smart_str_append_unsigned(buf, Z_OBJ_HANDLE_P(object));
+ smart_str_appends(buf, " (");
+ smart_str_append_unsigned(buf, zend_array_count(myht));
+
+ /* Need to specify refcount for debug_zval_dump() */
+ if (continuation == php_debug_zval_dump_ex) {
+ smart_str_appends(buf, ") refcount(");
+ smart_str_append_unsigned(buf, Z_REFCOUNT_P(object));
+ smart_str_appends(buf, "){\n");
+ } else {
+ smart_str_appends(buf, ") {\n");
+ }
+
zend_ulong num;
zend_string *key;
zval *val;
- uint32_t count;
- if (level > 1) {
- php_printf("%*c", level - 1, ' ');
+ bool status = true;
+ ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) {
+ const zend_property_info *prop_info = NULL;
+
+ if (Z_TYPE_P(val) == IS_INDIRECT) {
+ val = Z_INDIRECT_P(val);
+ if (key) {
+ prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(object), val);
+ }
+ }
+
+ if (!Z_ISUNDEF_P(val) || prop_info) {
+ status = php_object_property_dump(buf, prop_info, val, num, key, level + 2, continuation);
+ if (UNEXPECTED(!status)) {
+ break;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ zend_array_release(myht);
+
+ php_smart_str_indent(buf, level);
+ smart_str_appends(buf, "}\n");
+ ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
+ return status;
+}
+
+static void php_var_dump_common(smart_str *buf, const zval *struc)
+{
+ switch (Z_TYPE_P(struc)) {
+ case IS_FALSE:
+ smart_str_appends(buf, "bool(false)\n");
+ return;
+ case IS_TRUE:
+ smart_str_appends(buf, "bool(true)\n");
+ return;
+ case IS_NULL:
+ smart_str_appends(buf, "NULL\n");
+ return;
+ case IS_LONG:
+ smart_str_appends(buf, "int(");
+ smart_str_append_long(buf, Z_LVAL_P(struc));
+ smart_str_appends(buf, ")\n");
+ return;
+ case IS_DOUBLE:
+ smart_str_appends(buf, "float(");
+ smart_str_append_double(buf, Z_DVAL_P(struc), (int) PG(serialize_precision), false);
+ smart_str_appends(buf, ")\n");
+ return;
+ case IS_RESOURCE: {
+ const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
+ smart_str_appends(buf, "resource(");
+ smart_str_append_long(buf, Z_RES_P(struc)->handle);
+ smart_str_appends(buf, ") of type (");
+ smart_str_appends(buf, type_name ? type_name : "Unknown");
+ smart_str_appendc(buf, ')');
+ return;
+ }
+ case IS_STRING:
+ smart_str_appends(buf, "string(");
+ smart_str_append_unsigned(buf, Z_STRLEN_P(struc));
+ smart_str_appends(buf, ") \"");
+ smart_str_append(buf, Z_STR_P(struc));
+ smart_str_appendc(buf, '"');
+ return;
+ default:
+ smart_str_appends(buf, "UNKNOWN:0\n");
+ return;
}
+}
+
+static bool php_var_dump_ex(smart_str *buf, zval *struc, int level) /* {{{ */
+{
+ bool is_ref = false;
+
+ php_smart_str_indent(buf, level);
again:
switch (Z_TYPE_P(struc)) {
case IS_FALSE:
- php_printf("%sbool(false)\n", COMMON);
- break;
case IS_TRUE:
- php_printf("%sbool(true)\n", COMMON);
- break;
case IS_NULL:
- php_printf("%sNULL\n", COMMON);
- break;
case IS_LONG:
- php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
- break;
case IS_DOUBLE:
- php_printf_unchecked("%sfloat(%.*H)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
- break;
+ default:
+ if (is_ref) { smart_str_appendc(buf, '&'); }
+ php_var_dump_common(buf, struc);
+ return true;
case IS_STRING:
- php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
- PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
- PUTS("\"\n");
- break;
- case IS_ARRAY:
- myht = Z_ARRVAL_P(struc);
+ case IS_RESOURCE: {
+ if (is_ref) { smart_str_appendc(buf, '&'); }
+ php_var_dump_common(buf, struc);
+ smart_str_appendc(buf, '\n');
+ return true;
+ }
+ case IS_ARRAY: {
+ HashTable *myht = Z_ARRVAL_P(struc);
if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
if (GC_IS_RECURSIVE(myht)) {
- PUTS("*RECURSION*\n");
- return;
+ smart_str_appends(buf, "*RECURSION*\n");
+ return true;
}
GC_ADDREF(myht);
GC_PROTECT_RECURSION(myht);
}
- count = zend_hash_num_elements(myht);
- php_printf("%sarray(%d) {\n", COMMON, count);
- ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) {
- php_array_element_dump(val, num, key, level);
- } ZEND_HASH_FOREACH_END();
- if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
- GC_UNPROTECT_RECURSION(myht);
- GC_DTOR_NO_REF(myht);
- }
- if (level > 1) {
- php_printf("%*c", level-1, ' ');
- }
- PUTS("}\n");
- break;
- case IS_OBJECT: {
- zend_class_entry *ce = Z_OBJCE_P(struc);
- if (ce->ce_flags & ZEND_ACC_ENUM) {
- zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
- php_printf("%senum(%s::%s)\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval));
- return;
- }
- zend_object *zobj = Z_OBJ_P(struc);
- uint32_t *guard = zend_get_recursion_guard(zobj);
- if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
- PUTS("*RECURSION*\n");
- return;
- }
- ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
- myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
- class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
- const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
+ if (is_ref) { smart_str_appendc(buf, '&'); }
+ smart_str_appends(buf, "array(");
+ smart_str_append_unsigned(buf, zend_hash_num_elements(myht));
+ smart_str_appends(buf, ") {\n");
- php_printf("%s%sobject(%s)#%d (%d) {\n", COMMON, prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0);
- zend_string_release_ex(class_name, 0);
-
- if (myht) {
- zend_ulong num;
- zend_string *key;
- zval *val;
-
- ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) {
- zend_property_info *prop_info = NULL;
-
- if (Z_TYPE_P(val) == IS_INDIRECT) {
- val = Z_INDIRECT_P(val);
- if (key) {
- prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
- }
- }
-
- if (!Z_ISUNDEF_P(val) || prop_info) {
- php_object_property_dump(prop_info, val, num, key, level);
- }
- } ZEND_HASH_FOREACH_END();
- zend_release_properties(myht);
- }
- if (level > 1) {
- php_printf("%*c", level-1, ' ');
- }
- PUTS("}\n");
- ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
- break;
- }
- case IS_RESOURCE: {
- const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
- php_printf("%sresource(" ZEND_LONG_FMT ") of type (%s)\n", COMMON, Z_RES_P(struc)->handle, type_name ? type_name : "Unknown");
- break;
+ return php_dump_array_common(buf, myht, level, php_var_dump_ex);
}
+ case IS_OBJECT:
+ return php_dump_object_common(buf, struc, level, is_ref, php_var_dump_ex);
case IS_REFERENCE:
//??? hide references with refcount==1 (for compatibility)
if (Z_REFCOUNT_P(struc) > 1) {
- is_ref = 1;
+ is_ref = true;
}
struc = Z_REFVAL_P(struc);
goto again;
- break;
- default:
- php_printf("%sUNKNOWN:0\n", COMMON);
- break;
}
}
/* }}} */
+PHPAPI void php_var_dump(zval *struc, int level)
+{
+ smart_str buf = {0};
+ bool status = php_var_dump_ex(&buf, struc, level);
+ if (UNEXPECTED(!status)) {
+ smart_str_free(&buf);
+ return;
+ }
+ smart_str_0(&buf);
+ PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
+ smart_str_free(&buf);
+}
+
/* {{{ Dumps a string representation of variable to output */
PHP_FUNCTION(var_dump)
{
zval *args;
- int argc;
- int i;
+ uint32_t argc;
ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
ZEND_PARSE_PARAMETERS_END();
- for (i = 0; i < argc; i++) {
- php_var_dump(&args[i], 1);
+ for (uint32_t i = 0; i < argc; i++) {
+ php_var_dump(&args[i], 0);
}
}
/* }}} */
-static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
+static bool php_debug_zval_dump_ex(smart_str *buf, zval *struc, int level) /* {{{ */
{
- if (key == NULL) { /* numeric key */
- php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
- } else { /* string key */
- php_printf("%*c[\"", level + 1, ' ');
- PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
- php_printf("\"]=>\n");
- }
- php_debug_zval_dump(zv, level + 2);
-}
-/* }}} */
+ php_smart_str_indent(buf, level);
-static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
-{
- const char *prop_name, *class_name;
-
- if (key == NULL) { /* numeric key */
- php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
- } else { /* string key */
- zend_unmangle_property_name(key, &class_name, &prop_name);
- php_printf("%*c[", level + 1, ' ');
-
- if (class_name) {
- if (class_name[0] == '*') {
- php_printf("\"%s\":protected", prop_name);
+ switch (Z_TYPE_P(struc)) {
+ case IS_FALSE:
+ case IS_TRUE:
+ case IS_NULL:
+ case IS_LONG:
+ case IS_DOUBLE:
+ default:
+ php_var_dump_common(buf, struc);
+ return true;
+ case IS_STRING:
+ case IS_RESOURCE: {
+ php_var_dump_common(buf, struc);
+ if (Z_REFCOUNTED_P(struc)) {
+ smart_str_appends(buf, " refcount(");
+ smart_str_append_unsigned(buf, Z_REFCOUNT_P(struc));
+ smart_str_appends(buf, ")\n");
} else {
- php_printf("\"%s\":\"%s\":private", prop_name, class_name);
+ smart_str_appends(buf, " interned\n");
}
- } else {
- php_printf("\"%s\"", prop_name);
+ return true;
}
- ZEND_PUTS("]=>\n");
- }
- if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) {
- zend_string *type_str = zend_type_to_string(prop_info->type);
- php_printf("%*cuninitialized(%s)\n",
- level + 1, ' ', ZSTR_VAL(type_str));
- zend_string_release(type_str);
- } else {
- php_debug_zval_dump(zv, level + 2);
- }
-}
-/* }}} */
-
-PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */
-{
- HashTable *myht = NULL;
- zend_string *class_name;
- zend_ulong index;
- zend_string *key;
- zval *val;
- uint32_t count;
- char *packed;
-
- if (level > 1) {
- php_printf("%*c", level - 1, ' ');
- }
-
- switch (Z_TYPE_P(struc)) {
- case IS_FALSE:
- PUTS("bool(false)\n");
- break;
- case IS_TRUE:
- PUTS("bool(true)\n");
- break;
- case IS_NULL:
- PUTS("NULL\n");
- break;
- case IS_LONG:
- php_printf("int(" ZEND_LONG_FMT ")\n", Z_LVAL_P(struc));
- break;
- case IS_DOUBLE:
- php_printf_unchecked("float(%.*H)\n", (int) PG(serialize_precision), Z_DVAL_P(struc));
- break;
- case IS_STRING:
- php_printf("string(%zd) \"", Z_STRLEN_P(struc));
- PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
- if (Z_REFCOUNTED_P(struc)) {
- php_printf("\" refcount(%u)\n", Z_REFCOUNT_P(struc));
- } else {
- PUTS("\" interned\n");
- }
- break;
- case IS_ARRAY:
- myht = Z_ARRVAL_P(struc);
- if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
- if (GC_IS_RECURSIVE(myht)) {
- PUTS("*RECURSION*\n");
- return;
+ case IS_ARRAY: {
+ HashTable *myht = Z_ARRVAL_P(struc);
+ if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
+ if (GC_IS_RECURSIVE(myht)) {
+ smart_str_appends(buf, "*RECURSION*\n");
+ return true;
+ }
+ GC_ADDREF(myht);
+ GC_PROTECT_RECURSION(myht);
}
- GC_ADDREF(myht);
- GC_PROTECT_RECURSION(myht);
- }
- count = zend_hash_num_elements(myht);
- packed = HT_IS_PACKED(myht) ? "packed " : "";
- if (Z_REFCOUNTED_P(struc)) {
- /* -1 because of ADDREF above. */
- php_printf("array(%d) %srefcount(%u){\n", count, packed, Z_REFCOUNT_P(struc) - 1);
- } else {
- php_printf("array(%d) %sinterned {\n", count, packed);
- }
- ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
- zval_array_element_dump(val, index, key, level);
- } ZEND_HASH_FOREACH_END();
- if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
- GC_UNPROTECT_RECURSION(myht);
- GC_DTOR_NO_REF(myht);
- }
- if (level > 1) {
- php_printf("%*c", level - 1, ' ');
- }
- PUTS("}\n");
- break;
- case IS_OBJECT: {
- /* Check if this is already recursing on the object before calling zend_get_properties_for,
- * to allow infinite recursion detection to work even if classes return temporary arrays,
- * and to avoid the need to update the properties table in place to reflect the state
- * if the result won't be used. (https://github.com/php/php-src/issues/8044) */
- zend_object *zobj = Z_OBJ_P(struc);
- uint32_t *guard = zend_get_recursion_guard(zobj);
- if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
- PUTS("*RECURSION*\n");
- return;
- }
- ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
-
- myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
- class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
- const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
-
- php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc));
- zend_string_release_ex(class_name, 0);
- if (myht) {
- ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
- zend_property_info *prop_info = NULL;
- if (Z_TYPE_P(val) == IS_INDIRECT) {
- val = Z_INDIRECT_P(val);
- if (key) {
- prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
- }
- }
+ smart_str_appends(buf, "array(");
+ smart_str_append_unsigned(buf, zend_hash_num_elements(myht));
+ smart_str_appendc(buf, ')');
+ if (HT_IS_PACKED(myht)) {
+ smart_str_appends(buf, " packed");
+ }
+ if (Z_REFCOUNTED_P(struc)) {
+ smart_str_appends(buf, " refcount(");
+ /* -1 because of ADDREF above. */
+ smart_str_append_unsigned(buf, Z_REFCOUNT_P(struc) - 1);
+ smart_str_appends(buf, ")");
+ } else {
+ smart_str_appends(buf, " interned ");
+ }
+ smart_str_appends(buf, "{\n");
- if (!Z_ISUNDEF_P(val) || prop_info) {
- zval_object_property_dump(prop_info, val, index, key, level);
- }
- } ZEND_HASH_FOREACH_END();
- zend_release_properties(myht);
+ return php_dump_array_common(buf, myht, level, php_debug_zval_dump_ex);
}
- if (level > 1) {
- php_printf("%*c", level - 1, ' ');
+ case IS_OBJECT:
+ return php_dump_object_common(buf, struc, level, false, php_debug_zval_dump_ex);
+ case IS_REFERENCE: {
+ smart_str_appends(buf, "reference refcount(");
+ smart_str_append_unsigned(buf, Z_REFCOUNT_P(struc));
+ smart_str_appends(buf, ") {\n");
+ bool status = php_debug_zval_dump_ex(buf, Z_REFVAL_P(struc), level + 2);
+ php_smart_str_indent(buf, level);
+ smart_str_appends(buf, "}\n");
+ return status;
}
- PUTS("}\n");
- ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
- break;
- }
- case IS_RESOURCE: {
- const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
- php_printf("resource(" ZEND_LONG_FMT ") of type (%s) refcount(%u)\n", Z_RES_P(struc)->handle, type_name ? type_name : "Unknown", Z_REFCOUNT_P(struc));
- break;
- }
- case IS_REFERENCE:
- php_printf("reference refcount(%u) {\n", Z_REFCOUNT_P(struc));
- php_debug_zval_dump(Z_REFVAL_P(struc), level + 2);
- if (level > 1) {
- php_printf("%*c", level - 1, ' ');
- }
- PUTS("}\n");
- break;
- default:
- PUTS("UNKNOWN:0\n");
- break;
}
}
/* }}} */
+PHPAPI void php_debug_zval_dump(zval *struc, int level)
+{
+ smart_str buf = {0};
+ bool status = php_debug_zval_dump_ex(&buf, struc, level);
+ if (UNEXPECTED(!status)) {
+ smart_str_free(&buf);
+ return;
+ }
+ smart_str_0(&buf);
+ PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
+ smart_str_free(&buf);
+}
+
/* {{{ Dumps a string representation of an internal zend value to output. */
PHP_FUNCTION(debug_zval_dump)
{
zval *args;
- int argc;
- int i;
+ uint32_t argc;
ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
ZEND_PARSE_PARAMETERS_END();
- for (i = 0; i < argc; i++) {
- php_debug_zval_dump(&args[i], 1);
+ for (uint32_t i = 0; i < argc; i++) {
+ php_debug_zval_dump(&args[i], 0);
}
}
/* }}} */