Skip to content

Commit

Permalink
Export AST for default value strings in reflection
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nikic committed Oct 1, 2021
1 parent 6d3ef57 commit 5f478ec
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 46 deletions.
15 changes: 14 additions & 1 deletion Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
32 changes: 8 additions & 24 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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");

Expand Down
20 changes: 13 additions & 7 deletions ext/reflection/tests/ReflectionAttribute_toString.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@ ReflectionAttribute::__toString
--FILE--
<?php

#[Foo, Bar(a: "foo", b: 1234), Baz("foo", 1234), XXX(ERROR)]
#[Foo, Bar(a: "foo", b: 1234), Baz("foo", 1234), X(NO_ERROR), Y(new stdClass)]
function foo() {}

$refl = new ReflectionFunction('foo');
echo $refl->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--
Expand All @@ -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() ]
}
}
2 changes: 1 addition & 1 deletion ext/reflection/tests/ReflectionClass_export_basic1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Class [ <user> class C extends A ] {
Parameter #0 [ <required> A $a ]
Parameter #1 [ <required> $b ]
Parameter #2 [ <optional> ?C $c = NULL ]
Parameter #3 [ <optional> $d = '16 chars long -...' ]
Parameter #3 [ <optional> $d = K ]
Parameter #4 [ <optional> $e = '15 chars long -' ]
Parameter #5 [ <optional> $f = NULL ]
Parameter #6 [ <optional> $g = false ]
Expand Down
4 changes: 2 additions & 2 deletions ext/reflection/tests/ReflectionMethod_defaultArg.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ Method [ <user> private method bar ] {
@@ %s

- Parameters [1] {
Parameter #0 [ <optional> $a = 'T' ]
Parameter #0 [ <optional> $a = self::class ]
}
}
Method [ <user> private method bar ] {
@@ %s

- Parameters [1] {
Parameter #0 [ <optional> $a = 'B' ]
Parameter #0 [ <optional> $a = self::class ]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ string(%d) "Method [ <user, overwrites DateTimeZone, prototype DateTimeZone> sta
@@ %s

- Parameters [2] {
Parameter #0 [ <optional> int $timezoneGroup = %d ]
Parameter #0 [ <optional> int $timezoneGroup = DateTimeZone::ALL ]
Parameter #1 [ <optional> ?string $countryCode = NULL ]
}
- Return [ string ]
Expand Down
17 changes: 17 additions & 0 deletions ext/reflection/tests/ReflectionParameter_new_initializer.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
ReflectionParameter::__toString() with new initializer
--FILE--
<?php

function test(
$p1 = new stdClass,
$p2 = new SomeClass(new With, some: new Parameters)
) {}

echo new ReflectionParameter('test', 'p1'), "\n";
echo new ReflectionParameter('test', 'p2'), "\n";

?>
--EXPECT--
Parameter #0 [ <optional> $p1 = new \stdClass() ]
Parameter #1 [ <optional> $p2 = new \SomeClass(new \With(), some: new \Parameters()) ]
2 changes: 1 addition & 1 deletion ext/reflection/tests/bug33389.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Class [ <user> class Test ] {
@@ %sbug33389.php 4 - 5

- Parameters [1] {
Parameter #0 [ <optional> $arg = 1 ]
Parameter #0 [ <optional> $arg = foobar ]
}
}

Expand Down
8 changes: 4 additions & 4 deletions ext/reflection/tests/bug45765.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -51,31 +51,31 @@ Object of class [ <user> class foo extends foo2 ] {
@@ %s 10 - 11

- Parameters [1] {
Parameter #0 [ <optional> $a = 'foo's bar' ]
Parameter #0 [ <optional> $a = self::BAR ]
}
}

Method [ <user> public method test2 ] {
@@ %s 13 - 14

- Parameters [1] {
Parameter #0 [ <optional> $a = 'foobar' ]
Parameter #0 [ <optional> $a = parent::BAR ]
}
}

Method [ <user> public method test3 ] {
@@ %s 16 - 17

- Parameters [1] {
Parameter #0 [ <optional> $a = 'foo's bar' ]
Parameter #0 [ <optional> $a = foo::BAR ]
}
}

Method [ <user> public method test4 ] {
@@ %s 19 - 20

- Parameters [1] {
Parameter #0 [ <optional> $a = 'foobar' ]
Parameter #0 [ <optional> $a = foo2::BAR ]
}
}
}
Expand Down
30 changes: 25 additions & 5 deletions ext/reflection/tests/bug74673.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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 [ <user> class A ] {
@@ %s

- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [0] {
}

- Methods [1] {
Method [ <user> public method method ] {
@@ %s

- Parameters [1] {
Parameter #0 [ <optional> $test = PHP_SELF + 1 ]
}
}
}
}

0 comments on commit 5f478ec

Please sign in to comment.