From 5f478ec94979b850f05c222dca2aebd5a8630f20 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Fri, 1 Oct 2021 10:45:05 +0200 Subject: [PATCH] Export AST for default value strings in reflection When dumping default values in ReflectionXXX::__toString(), for expression initializers print the AST export instead of trying to evaluate the expression. With the introduction of "new in initializers" the result of the evaluation will commonly not be printable at all, and "__toString" will throw an exception, which is not particularly useful. Using the AST export also provides more information on how the parameter was originally declared, e.g. it preserves the fact that a certain constant was used. --- Zend/zend_ast.c | 15 ++++++++- ext/reflection/php_reflection.c | 32 +++++-------------- .../tests/ReflectionAttribute_toString.phpt | 20 ++++++++---- .../tests/ReflectionClass_export_basic1.phpt | 2 +- .../tests/ReflectionMethod_defaultArg.phpt | 4 +-- ...eflectionMethod_tentative_return_type.phpt | 2 +- .../ReflectionParameter_new_initializer.phpt | 17 ++++++++++ ext/reflection/tests/bug33389.phpt | 2 +- ext/reflection/tests/bug45765.phpt | 8 ++--- ext/reflection/tests/bug74673.phpt | 30 ++++++++++++++--- 10 files changed, 86 insertions(+), 46 deletions(-) create mode 100644 ext/reflection/tests/ReflectionParameter_new_initializer.phpt diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 2fbdc850bdc8..a064927f93a8 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1947,7 +1947,20 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_name(str, ast->child[1], 0, indent); break; case ZEND_AST_CLASS_NAME: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + if (ast->child[0] == NULL) { + /* The const expr representation stores the fetch type instead. */ + switch (ast->attr) { + case ZEND_FETCH_CLASS_SELF: + smart_str_appends(str, "self"); + break; + case ZEND_FETCH_CLASS_PARENT: + smart_str_appends(str, "parent"); + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + } else { + zend_ast_export_ns_name(str, ast->child[0], 0, indent); + } smart_str_appends(str, "::class"); break; case ZEND_AST_ASSIGN: BINARY_OP(" = ", 90, 91, 90); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9fa69428c51e..e1892e550bc6 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -610,24 +610,16 @@ static zval *get_default_from_recv(zend_op_array *op_array, uint32_t offset) { } static int format_default_value(smart_str *str, zval *value, zend_class_entry *scope) { - zval zv; - ZVAL_COPY(&zv, value); - if (UNEXPECTED(zval_update_constant_ex(&zv, scope) == FAILURE)) { - zval_ptr_dtor(&zv); - return FAILURE; - } - - if (Z_TYPE(zv) <= IS_STRING) { - smart_str_append_scalar(str, &zv, 15); - } else if (Z_TYPE(zv) == IS_ARRAY) { + if (Z_TYPE_P(value) <= IS_STRING) { + smart_str_append_scalar(str, value, 15); + } else if (Z_TYPE_P(value) == IS_ARRAY) { smart_str_appends(str, "Array"); } else { - zend_string *tmp_zv_str; - zend_string *zv_str = zval_get_tmp_string(&zv, &tmp_zv_str); - smart_str_append(str, zv_str); - zend_tmp_string_release(tmp_zv_str); + ZEND_ASSERT(Z_TYPE_P(value) == IS_CONSTANT_AST); + zend_string *ast_str = zend_ast_export("", Z_ASTVAL_P(value), ""); + smart_str_append(str, ast_str); + zend_string_release(ast_str); } - zval_ptr_dtor(&zv); return SUCCESS; } @@ -6404,26 +6396,18 @@ ZEND_METHOD(ReflectionAttribute, __toString) smart_str_append_printf(&str, " - Arguments [%d] {\n", attr->data->argc); for (uint32_t i = 0; i < attr->data->argc; i++) { - zval tmp; - if (FAILURE == zend_get_attribute_value(&tmp, attr->data, i, attr->scope)) { - smart_str_free(&str); - RETURN_THROWS(); - } - smart_str_append_printf(&str, " Argument #%d [ ", i); if (attr->data->args[i].name != NULL) { smart_str_append(&str, attr->data->args[i].name); smart_str_appends(&str, " = "); } - if (format_default_value(&str, &tmp, NULL) == FAILURE) { - zval_ptr_dtor(&tmp); + if (format_default_value(&str, &attr->data->args[i].value, NULL) == FAILURE) { smart_str_free(&str); RETURN_THROWS(); } smart_str_appends(&str, " ]\n"); - zval_ptr_dtor(&tmp); } smart_str_appends(&str, " }\n"); diff --git a/ext/reflection/tests/ReflectionAttribute_toString.phpt b/ext/reflection/tests/ReflectionAttribute_toString.phpt index 515dfa44d855..a5a6d705a2c0 100644 --- a/ext/reflection/tests/ReflectionAttribute_toString.phpt +++ b/ext/reflection/tests/ReflectionAttribute_toString.phpt @@ -3,18 +3,15 @@ ReflectionAttribute::__toString --FILE-- getAttributes()[0]; echo $refl->getAttributes()[1]; echo $refl->getAttributes()[2]; -try { - echo $refl->getAttributes()[3]; -} catch (Error $e) { - echo $e->getMessage(), "\n"; -} +echo $refl->getAttributes()[3]; +echo $refl->getAttributes()[4]; ?> --EXPECT-- @@ -31,4 +28,13 @@ Attribute [ Baz ] { Argument #1 [ 1234 ] } } -Undefined constant "ERROR" +Attribute [ X ] { + - Arguments [1] { + Argument #0 [ NO_ERROR ] + } +} +Attribute [ Y ] { + - Arguments [1] { + Argument #0 [ new \stdClass() ] + } +} diff --git a/ext/reflection/tests/ReflectionClass_export_basic1.phpt b/ext/reflection/tests/ReflectionClass_export_basic1.phpt index 318e47b35532..bbe0bed6dc4b 100644 --- a/ext/reflection/tests/ReflectionClass_export_basic1.phpt +++ b/ext/reflection/tests/ReflectionClass_export_basic1.phpt @@ -51,7 +51,7 @@ Class [ class C extends A ] { Parameter #0 [ A $a ] Parameter #1 [ $b ] Parameter #2 [ ?C $c = NULL ] - Parameter #3 [ $d = '16 chars long -...' ] + Parameter #3 [ $d = K ] Parameter #4 [ $e = '15 chars long -' ] Parameter #5 [ $f = NULL ] Parameter #6 [ $g = false ] diff --git a/ext/reflection/tests/ReflectionMethod_defaultArg.phpt b/ext/reflection/tests/ReflectionMethod_defaultArg.phpt index 521f29da582b..939862928820 100644 --- a/ext/reflection/tests/ReflectionMethod_defaultArg.phpt +++ b/ext/reflection/tests/ReflectionMethod_defaultArg.phpt @@ -32,13 +32,13 @@ Method [ private method bar ] { @@ %s - Parameters [1] { - Parameter #0 [ $a = 'T' ] + Parameter #0 [ $a = self::class ] } } Method [ private method bar ] { @@ %s - Parameters [1] { - Parameter #0 [ $a = 'B' ] + Parameter #0 [ $a = self::class ] } } diff --git a/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt b/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt index d1b5c49c2cbb..05d18084f210 100644 --- a/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt +++ b/ext/reflection/tests/ReflectionMethod_tentative_return_type.phpt @@ -49,7 +49,7 @@ string(%d) "Method [ sta @@ %s - Parameters [2] { - Parameter #0 [ int $timezoneGroup = %d ] + Parameter #0 [ int $timezoneGroup = DateTimeZone::ALL ] Parameter #1 [ ?string $countryCode = NULL ] } - Return [ string ] diff --git a/ext/reflection/tests/ReflectionParameter_new_initializer.phpt b/ext/reflection/tests/ReflectionParameter_new_initializer.phpt new file mode 100644 index 000000000000..265ab14f8911 --- /dev/null +++ b/ext/reflection/tests/ReflectionParameter_new_initializer.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionParameter::__toString() with new initializer +--FILE-- + +--EXPECT-- +Parameter #0 [ $p1 = new \stdClass() ] +Parameter #1 [ $p2 = new \SomeClass(new \With(), some: new \Parameters()) ] diff --git a/ext/reflection/tests/bug33389.phpt b/ext/reflection/tests/bug33389.phpt index e394b1f2968c..3484b5681f2b 100644 --- a/ext/reflection/tests/bug33389.phpt +++ b/ext/reflection/tests/bug33389.phpt @@ -42,7 +42,7 @@ Class [ class Test ] { @@ %sbug33389.php 4 - 5 - Parameters [1] { - Parameter #0 [ $arg = 1 ] + Parameter #0 [ $arg = foobar ] } } diff --git a/ext/reflection/tests/bug45765.phpt b/ext/reflection/tests/bug45765.phpt index af8dd9b47a80..098e56d38d13 100644 --- a/ext/reflection/tests/bug45765.phpt +++ b/ext/reflection/tests/bug45765.phpt @@ -51,7 +51,7 @@ Object of class [ class foo extends foo2 ] { @@ %s 10 - 11 - Parameters [1] { - Parameter #0 [ $a = 'foo's bar' ] + Parameter #0 [ $a = self::BAR ] } } @@ -59,7 +59,7 @@ Object of class [ class foo extends foo2 ] { @@ %s 13 - 14 - Parameters [1] { - Parameter #0 [ $a = 'foobar' ] + Parameter #0 [ $a = parent::BAR ] } } @@ -67,7 +67,7 @@ Object of class [ class foo extends foo2 ] { @@ %s 16 - 17 - Parameters [1] { - Parameter #0 [ $a = 'foo's bar' ] + Parameter #0 [ $a = foo::BAR ] } } @@ -75,7 +75,7 @@ Object of class [ class foo extends foo2 ] { @@ %s 19 - 20 - Parameters [1] { - Parameter #0 [ $a = 'foobar' ] + Parameter #0 [ $a = foo2::BAR ] } } } diff --git a/ext/reflection/tests/bug74673.phpt b/ext/reflection/tests/bug74673.phpt index a8b9d998ca76..3b3d0216a90e 100644 --- a/ext/reflection/tests/bug74673.phpt +++ b/ext/reflection/tests/bug74673.phpt @@ -15,8 +15,28 @@ $class = new ReflectionClass('A'); echo $class; ?> --EXPECTF-- -Fatal error: Uncaught Error: Undefined constant "PHP_SELF" in %s:%d -Stack trace: -#0 %s(%d): ReflectionClass->__toString() -#1 {main} - thrown in %s on line %d +Class [ class A ] { + @@ %s + + - Constants [0] { + } + + - Static properties [0] { + } + + - Static methods [0] { + } + + - Properties [0] { + } + + - Methods [1] { + Method [ public method method ] { + @@ %s + + - Parameters [1] { + Parameter #0 [ $test = PHP_SELF + 1 ] + } + } + } +}