bug #14605 [PropertyAccess] Fix setting public property on a class having a magic getter (lolautruche)
This PR was merged into the 2.6 branch. Discussion ---------- [PropertyAccess] Fix setting public property on a class having a magic getter | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | N/A | License | MIT | Doc PR | N/A When using PropertyAccessor with an object having both a public property and a magic getter, and one wants to update this property, PropertyAccessor may lose the property reference. This occurs when the public property value is a hash: ```php class Foo { /** * Example: $this->someProperty['foo']['bar'] = 'baz' * @var array */ public $someProperty; public function __get($name) { // ... } } $obj = new Foo(); $obj->someProperty = ['foo' => ['bar' => 'some_value']]; $propertyAccessor->setValue($obj, 'someProperty[foo][bar]', 'another_value'); echo $obj->someProperty['foo']['bar']; // Before this patch: 'some_value' => fail // After this patch: 'another_value' => correct ``` Furthermore, public properties are always used before `__get()` by PHP. This bug is visible since v2.6.5 asd733a887
changed the way `setValue()` works. Commits -------8b8feff
[PropertyAccess] Fix setting public property on a class having a magic getter
This commit is contained in:
commit
49ea81b2ff
@ -314,11 +314,11 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$result[self::VALUE] = $object->$isser();
|
$result[self::VALUE] = $object->$isser();
|
||||||
} elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) {
|
} elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) {
|
||||||
$result[self::VALUE] = $object->$hasser();
|
$result[self::VALUE] = $object->$hasser();
|
||||||
} elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) {
|
|
||||||
$result[self::VALUE] = $object->$property;
|
|
||||||
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
||||||
$result[self::VALUE] = &$object->$property;
|
$result[self::VALUE] = &$object->$property;
|
||||||
$result[self::IS_REF] = true;
|
$result[self::IS_REF] = true;
|
||||||
|
} elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) {
|
||||||
|
$result[self::VALUE] = $object->$property;
|
||||||
} elseif (!$classHasProperty && property_exists($object, $property)) {
|
} elseif (!$classHasProperty && property_exists($object, $property)) {
|
||||||
// Needed to support \stdClass instances. We need to explicitly
|
// Needed to support \stdClass instances. We need to explicitly
|
||||||
// exclude $classHasProperty, otherwise if in the previous clause
|
// exclude $classHasProperty, otherwise if in the previous clause
|
||||||
@ -410,10 +410,10 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$object->$setter($value);
|
$object->$setter($value);
|
||||||
} elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
|
} elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
|
||||||
$object->$getsetter($value);
|
$object->$getsetter($value);
|
||||||
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
|
||||||
$object->$property = $value;
|
|
||||||
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
||||||
$object->$property = $value;
|
$object->$property = $value;
|
||||||
|
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
||||||
|
$object->$property = $value;
|
||||||
} elseif (!$classHasProperty && property_exists($object, $property)) {
|
} elseif (!$classHasProperty && property_exists($object, $property)) {
|
||||||
// Needed to support \stdClass instances. We need to explicitly
|
// Needed to support \stdClass instances. We need to explicitly
|
||||||
// exclude $classHasProperty, otherwise if in the previous clause
|
// exclude $classHasProperty, otherwise if in the previous clause
|
||||||
|
@ -15,6 +15,8 @@ class TestClassMagicGet
|
|||||||
{
|
{
|
||||||
private $magicProperty;
|
private $magicProperty;
|
||||||
|
|
||||||
|
public $publicProperty;
|
||||||
|
|
||||||
public function __construct($value)
|
public function __construct($value)
|
||||||
{
|
{
|
||||||
$this->magicProperty = $value;
|
$this->magicProperty = $value;
|
||||||
|
@ -438,4 +438,12 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertEquals('foobar', $object->getProperty());
|
$this->assertEquals('foobar', $object->getProperty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetValueDeepWithMagicGetter()
|
||||||
|
{
|
||||||
|
$obj = new TestClassMagicGet('foo');
|
||||||
|
$obj->publicProperty = array('foo' => array('bar' => 'some_value'));
|
||||||
|
$this->propertyAccessor->setValue($obj, 'publicProperty[foo][bar]', 'Updated');
|
||||||
|
$this->assertSame('Updated', $obj->publicProperty['foo']['bar']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user