This PR was merged into the 2.5-dev branch.
Discussion
----------
Fix issue #10867
| Q | A
| ------------- | ---
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #10867
| License | MIT
Commits
-------
0cc9746
Fix issue #10867
This commit is contained in:
commit
825867208e
@ -314,12 +314,15 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
$camelProp = $this->camelize($property);
|
||||
$reflClass = new \ReflectionClass($object);
|
||||
$getter = 'get'.$camelProp;
|
||||
$getter2 = lcfirst($camelProp);
|
||||
$isser = 'is'.$camelProp;
|
||||
$hasser = 'has'.$camelProp;
|
||||
$classHasProperty = $reflClass->hasProperty($property);
|
||||
|
||||
if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
|
||||
$result[self::VALUE] = $object->$getter();
|
||||
} elseif ($this->isMethodAccessible($reflClass, $getter2, 0)) {
|
||||
$result[self::VALUE] = $object->$getter2();
|
||||
} elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) {
|
||||
$result[self::VALUE] = $object->$isser();
|
||||
} elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) {
|
||||
@ -341,7 +344,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
// we call the getter and hope the __call do the job
|
||||
$result[self::VALUE] = $object->$getter();
|
||||
} else {
|
||||
$methods = array($getter, $isser, $hasser, '__get');
|
||||
$methods = array($getter, $getter2, $isser, $hasser, '__get');
|
||||
if ($this->magicCall) {
|
||||
$methods[] = '__call';
|
||||
}
|
||||
@ -413,10 +416,13 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
}
|
||||
|
||||
$setter = 'set'.$this->camelize($property);
|
||||
$setter2 = lcfirst($plural);
|
||||
$classHasProperty = $reflClass->hasProperty($property);
|
||||
|
||||
if ($this->isMethodAccessible($reflClass, $setter, 1)) {
|
||||
$object->$setter($value);
|
||||
} elseif ($this->isMethodAccessible($reflClass, $setter2, 1)) {
|
||||
$object->$setter2($value);
|
||||
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
||||
$object->$property = $value;
|
||||
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
||||
@ -433,13 +439,14 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
$object->$setter($value);
|
||||
} else {
|
||||
throw new NoSuchPropertyException(sprintf(
|
||||
'Neither the property "%s" nor one of the methods %s"%s()", '.
|
||||
'Neither the property "%s" nor one of the methods %s"%s()", "%s()", '.
|
||||
'"__set()" or "__call()" exist and have public access in class "%s".',
|
||||
$property,
|
||||
implode('', array_map(function ($singular) {
|
||||
return '"add'.$singular.'()"/"remove'.$singular.'()", ';
|
||||
}, $singulars)),
|
||||
$setter,
|
||||
$setter2,
|
||||
$reflClass->name
|
||||
));
|
||||
}
|
||||
@ -508,9 +515,11 @@ class PropertyAccessor implements PropertyAccessorInterface
|
||||
$reflClass = new \ReflectionClass($object);
|
||||
|
||||
$setter = 'set'.$this->camelize($property);
|
||||
$setter2 = lcfirst($this->camelize($property));
|
||||
$classHasProperty = $reflClass->hasProperty($property);
|
||||
|
||||
if ($this->isMethodAccessible($reflClass, $setter, 1)
|
||||
|| $this->isMethodAccessible($reflClass, $setter2, 1)
|
||||
|| $this->isMethodAccessible($reflClass, '__set', 2)
|
||||
|| ($classHasProperty && $reflClass->getProperty($property)->isPublic())
|
||||
|| (!$classHasProperty && property_exists($object, $property))
|
||||
|
@ -18,6 +18,8 @@ class TestClass
|
||||
private $privateProperty;
|
||||
|
||||
private $publicAccessor;
|
||||
private $publicMethodAccessor;
|
||||
private $publicMethodMutator;
|
||||
private $publicAccessorWithDefaultValue;
|
||||
private $publicAccessorWithRequiredAndDefaultValue;
|
||||
private $publicAccessorWithMoreRequiredParameters;
|
||||
@ -28,6 +30,8 @@ class TestClass
|
||||
{
|
||||
$this->publicProperty = $value;
|
||||
$this->publicAccessor = $value;
|
||||
$this->publicMethodAccessor = $value;
|
||||
$this->publicMethodMutator = $value;
|
||||
$this->publicAccessorWithDefaultValue = $value;
|
||||
$this->publicAccessorWithRequiredAndDefaultValue = $value;
|
||||
$this->publicAccessorWithMoreRequiredParameters = $value;
|
||||
@ -95,6 +99,21 @@ class TestClass
|
||||
return $this->publicHasAccessor;
|
||||
}
|
||||
|
||||
public function publicMethodAccessor()
|
||||
{
|
||||
return $this->publicMethodAccessor;
|
||||
}
|
||||
|
||||
public function publicMethodMutator($value)
|
||||
{
|
||||
$this->publicMethodMutator = $value;
|
||||
}
|
||||
|
||||
public function getPublicMethodMutator()
|
||||
{
|
||||
return $this->publicMethodMutator;
|
||||
}
|
||||
|
||||
protected function setProtectedAccessor($value)
|
||||
{
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
||||
* @expectedExceptionMessage Neither the property "axes" nor one of the methods "addAx()"/"removeAx()", "addAxe()"/"removeAxe()", "addAxis()"/"removeAxis()", "setAxes()", "__set()" or "__call()" exist and have public access in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover
|
||||
* @expectedExceptionMessage Neither the property "axes" nor one of the methods "addAx()"/"removeAx()", "addAxe()"/"removeAxe()", "addAxis()"/"removeAxis()", "setAxes()", "axes()", "__set()" or "__call()" exist and have public access in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover
|
||||
*/
|
||||
public function testSetValueFailsIfNoAdderNorRemoverFound()
|
||||
{
|
||||
|
@ -28,38 +28,23 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->propertyAccessor = new PropertyAccessor();
|
||||
}
|
||||
|
||||
public function getValidPropertyPaths()
|
||||
public function getValidGetPropertyPaths()
|
||||
{
|
||||
return array(
|
||||
array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'),
|
||||
array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'),
|
||||
array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'),
|
||||
array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'),
|
||||
array((object) array('firstName' => 'Bernhard'), 'firstName', 'Bernhard'),
|
||||
array((object) array('property' => array('firstName' => 'Bernhard')), 'property[firstName]', 'Bernhard'),
|
||||
array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].firstName', 'Bernhard'),
|
||||
array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.firstName', 'Bernhard'),
|
||||
return array_merge(
|
||||
array(
|
||||
array(new TestClass('Bernhard'), 'publicMethodAccessor', 'Bernhard', 'Bernhard'),
|
||||
),
|
||||
$this->getValidPropertyPaths()
|
||||
);
|
||||
}
|
||||
|
||||
// Accessor methods
|
||||
array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'),
|
||||
|
||||
// Methods are camelized
|
||||
array(new TestClass('Bernhard'), 'public_accessor', 'Bernhard'),
|
||||
|
||||
// Missing indices
|
||||
array(array('index' => array()), '[index][firstName]', null),
|
||||
array(array('root' => array('index' => array())), '[root][index][firstName]', null),
|
||||
|
||||
// Special chars
|
||||
array(array('%!@$§.' => 'Bernhard'), '[%!@$§.]', 'Bernhard'),
|
||||
array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'),
|
||||
array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'),
|
||||
array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'),
|
||||
public function getValidSetPropertyPaths()
|
||||
{
|
||||
return array_merge(
|
||||
array(
|
||||
array(new TestClass('Bernhard'), 'publicMethodMutator', 'Bernhard', 'Bernhard'),
|
||||
),
|
||||
$this->getValidPropertyPaths()
|
||||
);
|
||||
}
|
||||
|
||||
@ -95,7 +80,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
* @dataProvider getValidGetPropertyPaths
|
||||
*/
|
||||
public function testGetValue($objectOrArray, $path, $value)
|
||||
{
|
||||
@ -196,7 +181,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
* @dataProvider getValidSetPropertyPaths
|
||||
*/
|
||||
public function testSetValue($objectOrArray, $path)
|
||||
{
|
||||
@ -312,7 +297,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
* @dataProvider getValidGetPropertyPaths
|
||||
*/
|
||||
public function testIsReadable($objectOrArray, $path)
|
||||
{
|
||||
@ -380,7 +365,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getValidPropertyPaths
|
||||
* @dataProvider getValidSetPropertyPaths
|
||||
*/
|
||||
public function testIsWritable($objectOrArray, $path)
|
||||
{
|
||||
@ -446,4 +431,39 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
$this->assertFalse($this->propertyAccessor->isWritable('', 'foobar', 'Updated'));
|
||||
}
|
||||
|
||||
private function getValidPropertyPaths()
|
||||
{
|
||||
return array(
|
||||
array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'),
|
||||
array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'),
|
||||
array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'),
|
||||
array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'),
|
||||
array((object) array('firstName' => 'Bernhard'), 'firstName', 'Bernhard'),
|
||||
array((object) array('property' => array('firstName' => 'Bernhard')), 'property[firstName]', 'Bernhard'),
|
||||
array(array('index' => (object) array('firstName' => 'Bernhard')), '[index].firstName', 'Bernhard'),
|
||||
array((object) array('property' => (object) array('firstName' => 'Bernhard')), 'property.firstName', 'Bernhard'),
|
||||
|
||||
// Accessor methods
|
||||
array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'),
|
||||
array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'),
|
||||
|
||||
// Methods are camelized
|
||||
array(new TestClass('Bernhard'), 'public_accessor', 'Bernhard'),
|
||||
|
||||
// Missing indices
|
||||
array(array('index' => array()), '[index][firstName]', null),
|
||||
array(array('root' => array('index' => array())), '[root][index][firstName]', null),
|
||||
|
||||
// Special chars
|
||||
array(array('%!@$§.' => 'Bernhard'), '[%!@$§.]', 'Bernhard'),
|
||||
array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'),
|
||||
array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'),
|
||||
array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user