Skip to content

Commit

Permalink
Reflection: show the name of object constants used as default properties
Browse files Browse the repository at this point in the history
When a property default is based on a global constant, identify and use the
name of that constant. Previously, `format_default_value()` assumed that
non-scalar and non-array defaults were always going to be `IS_CONSTANT_AST`
pointers, and when the AST expression had been evaluated and produced an
object, depending on when the `ReflectionClass` or `ReflectionProperty`
instance had been created, the default was shown as one of `callable`,
`__CLASS__`, or `...`.

Instead, if the default value is an object (`IS_OBJECT`), find the name of the
`zend_constant` in the global `EG(zend_constants)` that points to the same
value, and show that name. If no constant is found, instead of the confusing
output of treating the object as an `IS_CONSTANT_AST` value, show
`"<Unknown object value>"`.

Add test cases for each of the `callable`, `__CLASS__`, and `...` cases to
confirm that they all now properly show the name of the constant.

Closes phpgh-15902
  • Loading branch information
DanielEScherzer committed Sep 16, 2024
1 parent c7397f5 commit e6683a9
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 0 deletions.
20 changes: 20 additions & 0 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,26 @@ static int format_default_value(smart_str *str, zval *value) {
format_default_value(str, zv);
} ZEND_HASH_FOREACH_END();
smart_str_appendc(str, ']');
} else if (Z_TYPE_P(value) == IS_OBJECT) {
// Look through the global constants, GH-15902
zend_constant *constant;
zend_string *name = NULL;

ZEND_HASH_MAP_FOREACH_PTR(EG(zend_constants), constant) {
if (
Z_TYPE(constant->value) == IS_OBJECT &&
Z_OBJ(constant->value) == Z_OBJ_P(value)
) {
// Found the right constant
name = constant->name;
break;
}
} ZEND_HASH_FOREACH_END();
if (name == NULL) {
smart_str_appends(str, "<Unknown object value>");
} else {
smart_str_append(str, name);
}
} else {
ZEND_ASSERT(Z_TYPE_P(value) == IS_CONSTANT_AST);
zend_string *ast_str = zend_ast_export("", Z_ASTVAL_P(value), "");
Expand Down
36 changes: 36 additions & 0 deletions ext/reflection/tests/gh15902/ReflectionClass-callable.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
ReflectionClass object default property - used to say "callable"
--FILE--
<?php

class C {
public stdClass $a = FOO;
}
define('FOO', new stdClass);

new C;

$reflector = new ReflectionClass(C::class);
var_dump( (string)$reflector );
?>
--EXPECTF--
string(%d) "Class [ <user> class C ] {
@@ %sReflectionClass-callable.php %d-%d

- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [1] {
Property [ public stdClass $a = FOO ]
}

- Methods [0] {
}
}
"
37 changes: 37 additions & 0 deletions ext/reflection/tests/gh15902/ReflectionClass-class.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
ReflectionClass object default property - used to say "__CLASS__"
--FILE--
<?php

class C {
public stdClass $a = FOO;
}
$reflector = new ReflectionClass(C::class);

define('FOO', new stdClass);
new C;

var_dump( (string)$reflector );

?>
--EXPECTF--
string(%d) "Class [ <user> class C ] {
@@ %sReflectionClass-class.php %d-%d

- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [1] {
Property [ public stdClass $a = FOO ]
}

- Methods [0] {
}
}
"
39 changes: 39 additions & 0 deletions ext/reflection/tests/gh15902/ReflectionClass-dots.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
ReflectionClass object default property - used to say "..."
--FILE--
<?php

class C {
public stdClass $a = FOO;
}
$reflector = new ReflectionClass(C::class);
$c = $reflector->newLazyGhost(function () {});

define('FOO', new stdClass);

$c->a;

var_dump( (string)$reflector );

?>
--EXPECTF--
string(%d) "Class [ <user> class C ] {
@@ %sReflectionClass-dots.php %d-%d

- Constants [0] {
}

- Static properties [0] {
}

- Static methods [0] {
}

- Properties [1] {
Property [ public stdClass $a = FOO ]
}

- Methods [0] {
}
}
"
19 changes: 19 additions & 0 deletions ext/reflection/tests/gh15902/ReflectionProperty-callable.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
ReflectionProperty object default - used to say "callable"
--FILE--
<?php

class C {
public stdClass $a = FOO;
}
define('FOO', new stdClass);

new C;

$reflector = new ReflectionProperty(C::class, 'a');
var_dump( (string)$reflector );

?>
--EXPECTF--
string(%d) "Property [ public stdClass $a = FOO ]
"
19 changes: 19 additions & 0 deletions ext/reflection/tests/gh15902/ReflectionProperty-class.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
ReflectionProperty object default - used to say "__CLASS__"
--FILE--
<?php

class C {
public stdClass $a = FOO;
}
$reflector = new ReflectionProperty(C::class, 'a');

define('FOO', new stdClass);
new C;

var_dump( (string)$reflector );

?>
--EXPECTF--
string(%d) "Property [ public stdClass $a = FOO ]
"
22 changes: 22 additions & 0 deletions ext/reflection/tests/gh15902/ReflectionProperty-dots.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
ReflectionProperty object default - used to say "..."
--FILE--
<?php

class C {
public stdClass $a = FOO;
}
$reflector = new ReflectionProperty(C::class, 'a');
$lazyFactory = new ReflectionClass(C::class);
$c = $lazyFactory->newLazyGhost(function () {});

define('FOO', new stdClass);

$c->a;

var_dump( (string)$reflector );

?>
--EXPECTF--
string(%d) "Property [ public stdClass $a = FOO ]
"

0 comments on commit e6683a9

Please sign in to comment.