[php-src] master: Merge branch 'PHP-8.4' into PHP-8.5
Ilia Alshanetsky <[email protected]>
| Newsgroups | gmane.comp.php.cvs.general |
|---|---|
| Message-ID | <[email protected]> |
Author: Ilia Alshanetsky (iliaal)
Date: 2026-06-25T18:55:02-04:00
Commit: https://github.com/php/php-src/commit/f605d20cfd23417230c5b5e5e5269530b10e0e8f
Raw diff: https://github.com/php/php-src/commit/f605d20cfd23417230c5b5e5e5269530b10e0e8f.diff
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4:
Report dynamic property shadowing a private parent in Reflection
Changed paths:
A ext/reflection/tests/gh22441.phpt
M NEWS
M ext/reflection/php_reflection.c
Diff:
diff --git a/NEWS b/NEWS
index 4a08f3391d77..ef8d40d00f82 100644
--- a/NEWS
+++ b/NEWS
@@ -31,6 +31,8 @@ PHP NEWS
- Reflection:
. Fixed bug GH-22324 (Ignore leading namespace separator in
ReflectionParameter::__construct()). (jorgsowa)
+ . Fixed bug GH-22441 (ReflectionClass::hasProperty() and getProperty() ignore
+ dynamic properties shadowing a private parent property). (iliaal)
- SPL:
. Fix class_parents for classes with leading slash in non-autoload mode.
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 5d7a15e24fef..956e6ea22d41 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -4616,19 +4616,17 @@ ZEND_METHOD(ReflectionClass, hasProperty)
}
GET_REFLECTION_OBJECT_PTR(ce);
- if ((property_info = zend_hash_find_ptr(&ce->properties_info, name)) != NULL) {
- if ((property_info->flags & ZEND_ACC_PRIVATE) && property_info->ce != ce) {
- RETURN_FALSE;
- }
+ if ((property_info = zend_hash_find_ptr(&ce->properties_info, name)) != NULL
+ && (!(property_info->flags & ZEND_ACC_PRIVATE)
+ || property_info->ce == ce)) {
RETURN_TRUE;
- } else {
- if (Z_TYPE(intern->obj) != IS_UNDEF) {
- if (Z_OBJ_HANDLER(intern->obj, has_property)(Z_OBJ(intern->obj), name, ZEND_PROPERTY_EXISTS, NULL)) {
- RETURN_TRUE;
- }
+ }
+ if (Z_TYPE(intern->obj) != IS_UNDEF) {
+ if (Z_OBJ_HANDLER(intern->obj, has_property)(Z_OBJ(intern->obj), name, ZEND_PROPERTY_EXISTS, NULL)) {
+ RETURN_TRUE;
}
- RETURN_FALSE;
}
+ RETURN_FALSE;
}
/* }}} */
@@ -4647,12 +4645,13 @@ ZEND_METHOD(ReflectionClass, getProperty)
}
GET_REFLECTION_OBJECT_PTR(ce);
- if ((property_info = zend_hash_find_ptr(&ce->properties_info, name)) != NULL) {
- if (!(property_info->flags & ZEND_ACC_PRIVATE) || property_info->ce == ce) {
- reflection_property_factory(ce, name, property_info, return_value);
- return;
- }
- } else if (Z_TYPE(intern->obj) != IS_UNDEF) {
+ if ((property_info = zend_hash_find_ptr(&ce->properties_info, name)) != NULL
+ && (!(property_info->flags & ZEND_ACC_PRIVATE)
+ || property_info->ce == ce)) {
+ reflection_property_factory(ce, name, property_info, return_value);
+ return;
+ }
+ if (Z_TYPE(intern->obj) != IS_UNDEF) {
/* Check for dynamic properties */
if (zend_hash_exists(Z_OBJ_HT(intern->obj)->get_properties(Z_OBJ(intern->obj)), name)) {
reflection_property_factory(ce, name, NULL, return_value);
diff --git a/ext/reflection/tests/gh22441.phpt b/ext/reflection/tests/gh22441.phpt
new file mode 100644
index 000000000000..9ca3cb0eee74
--- /dev/null
+++ b/ext/reflection/tests/gh22441.phpt
@@ -0,0 +1,44 @@
+--TEST--
+GH-22441 (ReflectionClass::hasProperty()/getProperty() ignore dynamic properties shadowing a private parent property)
+--FILE--
+<?php
+
+class Base {
+ private mixed $shadow;
+ private mixed $onlyBase;
+}
+
+#[AllowDynamicProperties]
+class Child extends Base {}
+
+$o = new Child();
+$o->shadow = true;
+$o->noShadow = true;
+
+$r = new ReflectionObject($o);
+
+echo "hasProperty:\n";
+echo "shadow (dynamic over private parent): "; var_dump($r->hasProperty('shadow'));
+echo "noShadow (plain dynamic): "; var_dump($r->hasProperty('noShadow'));
+echo "onlyBase (private parent, no dynamic): "; var_dump($r->hasProperty('onlyBase'));
+
+echo "\ngetProperty:\n";
+foreach (['shadow', 'noShadow', 'onlyBase'] as $name) {
+ try {
+ $p = $r->getProperty($name);
+ printf("%s: %s::\$%s\n", $name, $p->getDeclaringClass()->getName(), $p->getName());
+ } catch (ReflectionException $e) {
+ printf("%s: %s\n", $name, $e->getMessage());
+ }
+}
+?>
+--EXPECT--
+hasProperty:
+shadow (dynamic over private parent): bool(true)
+noShadow (plain dynamic): bool(true)
+onlyBase (private parent, no dynamic): bool(false)
+
+getProperty:
+shadow: Child::$shadow
+noShadow: Child::$noShadow
+onlyBase: Property Child::$onlyBase does not exist