Repository navigation
Strange behavior of ReflectionClass::getProperty with trait and inheritance #15753
Copy link
Copy link
Closed
Description
Description
The following code: https://3v4l.org/Zbnqa
<?php
class ParentClass
{
protected readonly int $value;
}
trait Foo
{
protected readonly int $value;
}
class ChildClassWithoutTrait extends ParentClass
{
protected readonly int $value;
}
$reflector = new ReflectionClass(ChildClassWithoutTrait::class);
$instance = $reflector->newInstanceWithoutConstructor();
var_dump(
$reflector->hasProperty('value'),
$prop = $reflector->getProperty('value')
);
$prop->setValue($instance, 20);
var_dump($instance);
echo '--------------------------------------------------------------------------------------------------------', PHP_EOL;
class ClassWithTraitAndNoParent
{
use Foo;
}
$reflector = new ReflectionClass(ClassWithTraitAndNoParent::class);
$instance = $reflector->newInstanceWithoutConstructor();
var_dump(
$reflector->hasProperty('value'),
$prop = $reflector->getProperty('value')
);
$prop->setValue($instance, 20);
var_dump($instance);
echo '--------------------------------------------------------------------------------------------------------', PHP_EOL;
class ChildClassWithTrait extends ParentClass
{
use Foo;
}
$reflector = new ReflectionClass(ChildClassWithTrait::class);
$instance = $reflector->newInstanceWithoutConstructor();
var_dump(
$reflector->hasProperty('value'),
$prop = $reflector->getProperty('value')
);
$prop->setValue($instance, 20);
var_dump($instance);Resulted in this output:
bool(true)
object(ReflectionProperty)#3 (2) {
["name"]=>
string(5) "value"
["class"]=>
string(22) "ChildClassWithoutTrait"
}
object(ChildClassWithoutTrait)#2 (1) {
["value":protected]=>
int(20)
}
--------------------------------------------------------------------------------------------------------
bool(true)
object(ReflectionProperty)#2 (2) {
["name"]=>
string(5) "value"
["class"]=>
string(25) "ClassWithTraitAndNoParent"
}
object(ClassWithTraitAndNoParent)#1 (1) {
["value":protected]=>
int(20)
}
--------------------------------------------------------------------------------------------------------
bool(true)
object(ReflectionProperty)#1 (2) {
["name"]=>
string(5) "value"
["class"]=>
string(11) "ParentClass"
}
Fatal error: Uncaught Error: Cannot initialize readonly property ParentClass::$value from scope ChildClassWithTrait in /in/Zbnqa:64
Stack trace:
#0 /in/Zbnqa(64): ReflectionProperty->setValue(Object(ChildClassWithTrait), 20)
#1 {main}
thrown in /in/Zbnqa on line 64
Process exited with code 255.
But I expected this output instead:
bool(true)
object(ReflectionProperty)#3 (2) {
["name"]=>
string(5) "value"
["class"]=>
string(22) "ChildClassWithoutTrait"
}
object(ChildClassWithoutTrait)#2 (1) {
["value":protected]=>
int(20)
}
--------------------------------------------------------------------------------------------------------
bool(true)
object(ReflectionProperty)#2 (2) {
["name"]=>
string(5) "value"
["class"]=>
string(25) "ClassWithTraitAndNoParent"
}
object(ClassWithTraitAndNoParent)#1 (1) {
["value":protected]=>
int(20)
}
--------------------------------------------------------------------------------------------------------
bool(true)
object(ReflectionProperty)#1 (2) {
["name"]=>
string(5) "value"
["class"]=>
string(11) "ChildClassWithTrait"
}
object(ChildClassWithTrait)#n (1) {
["value":protected]=>
int(20)
}
It seems that a combination of traits and inheritance somehow breaks the getProperty() method. Even though the hasProperty() method returns true for ChildClassWithTrait, the getProperty() method returns a ReflectionProperty for ParentClass.
PHP Version
all
Operating System
No response
Activity
iluuu1994 commentedon Sep 13, 2024
Simplified example:
Given traits are described as "copy & paste" in the documentation, it seems to me that this should work.
djmarland commentedon Nov 21, 2024
We have also run into this issue, particularly when trying to set Attributes: https://3v4l.org/rZILt
It seems properties from Traits are at the end of the properties list, so will not overwrite earlier properties from the parents.
This means that the attribute values come from the parent instead of the trait used by the child.
TimWolla commentedon Mar 28, 2025
I believe this one is already implicitly resolved with PHP 8.4, since
readonlynow impliesprotectedinstead ofprivate.Bind traits before parent class