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);
|
$camelProp = $this->camelize($property);
|
||||||
$reflClass = new \ReflectionClass($object);
|
$reflClass = new \ReflectionClass($object);
|
||||||
$getter = 'get'.$camelProp;
|
$getter = 'get'.$camelProp;
|
||||||
|
$getter2 = lcfirst($camelProp);
|
||||||
$isser = 'is'.$camelProp;
|
$isser = 'is'.$camelProp;
|
||||||
$hasser = 'has'.$camelProp;
|
$hasser = 'has'.$camelProp;
|
||||||
$classHasProperty = $reflClass->hasProperty($property);
|
$classHasProperty = $reflClass->hasProperty($property);
|
||||||
|
|
||||||
if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
|
if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
|
||||||
$result[self::VALUE] = $object->$getter();
|
$result[self::VALUE] = $object->$getter();
|
||||||
|
} elseif ($this->isMethodAccessible($reflClass, $getter2, 0)) {
|
||||||
|
$result[self::VALUE] = $object->$getter2();
|
||||||
} elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) {
|
} elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) {
|
||||||
$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()) {
|
||||||
@ -341,7 +344,7 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
// we call the getter and hope the __call do the job
|
// we call the getter and hope the __call do the job
|
||||||
$result[self::VALUE] = $object->$getter();
|
$result[self::VALUE] = $object->$getter();
|
||||||
} else {
|
} else {
|
||||||
$methods = array($getter, $isser, $hasser, '__get');
|
$methods = array($getter, $getter2, $isser, $hasser, '__get');
|
||||||
if ($this->magicCall) {
|
if ($this->magicCall) {
|
||||||
$methods[] = '__call';
|
$methods[] = '__call';
|
||||||
}
|
}
|
||||||
@ -413,10 +416,13 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$setter = 'set'.$this->camelize($property);
|
$setter = 'set'.$this->camelize($property);
|
||||||
|
$setter2 = lcfirst($plural);
|
||||||
$classHasProperty = $reflClass->hasProperty($property);
|
$classHasProperty = $reflClass->hasProperty($property);
|
||||||
|
|
||||||
if ($this->isMethodAccessible($reflClass, $setter, 1)) {
|
if ($this->isMethodAccessible($reflClass, $setter, 1)) {
|
||||||
$object->$setter($value);
|
$object->$setter($value);
|
||||||
|
} elseif ($this->isMethodAccessible($reflClass, $setter2, 1)) {
|
||||||
|
$object->$setter2($value);
|
||||||
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
} elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
|
||||||
$object->$property = $value;
|
$object->$property = $value;
|
||||||
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
} elseif ($classHasProperty && $reflClass->getProperty($property)->isPublic()) {
|
||||||
@ -433,13 +439,14 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$object->$setter($value);
|
$object->$setter($value);
|
||||||
} else {
|
} else {
|
||||||
throw new NoSuchPropertyException(sprintf(
|
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".',
|
'"__set()" or "__call()" exist and have public access in class "%s".',
|
||||||
$property,
|
$property,
|
||||||
implode('', array_map(function ($singular) {
|
implode('', array_map(function ($singular) {
|
||||||
return '"add'.$singular.'()"/"remove'.$singular.'()", ';
|
return '"add'.$singular.'()"/"remove'.$singular.'()", ';
|
||||||
}, $singulars)),
|
}, $singulars)),
|
||||||
$setter,
|
$setter,
|
||||||
|
$setter2,
|
||||||
$reflClass->name
|
$reflClass->name
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -508,9 +515,11 @@ class PropertyAccessor implements PropertyAccessorInterface
|
|||||||
$reflClass = new \ReflectionClass($object);
|
$reflClass = new \ReflectionClass($object);
|
||||||
|
|
||||||
$setter = 'set'.$this->camelize($property);
|
$setter = 'set'.$this->camelize($property);
|
||||||
|
$setter2 = lcfirst($this->camelize($property));
|
||||||
$classHasProperty = $reflClass->hasProperty($property);
|
$classHasProperty = $reflClass->hasProperty($property);
|
||||||
|
|
||||||
if ($this->isMethodAccessible($reflClass, $setter, 1)
|
if ($this->isMethodAccessible($reflClass, $setter, 1)
|
||||||
|
|| $this->isMethodAccessible($reflClass, $setter2, 1)
|
||||||
|| $this->isMethodAccessible($reflClass, '__set', 2)
|
|| $this->isMethodAccessible($reflClass, '__set', 2)
|
||||||
|| ($classHasProperty && $reflClass->getProperty($property)->isPublic())
|
|| ($classHasProperty && $reflClass->getProperty($property)->isPublic())
|
||||||
|| (!$classHasProperty && property_exists($object, $property))
|
|| (!$classHasProperty && property_exists($object, $property))
|
||||||
|
@ -18,6 +18,8 @@ class TestClass
|
|||||||
private $privateProperty;
|
private $privateProperty;
|
||||||
|
|
||||||
private $publicAccessor;
|
private $publicAccessor;
|
||||||
|
private $publicMethodAccessor;
|
||||||
|
private $publicMethodMutator;
|
||||||
private $publicAccessorWithDefaultValue;
|
private $publicAccessorWithDefaultValue;
|
||||||
private $publicAccessorWithRequiredAndDefaultValue;
|
private $publicAccessorWithRequiredAndDefaultValue;
|
||||||
private $publicAccessorWithMoreRequiredParameters;
|
private $publicAccessorWithMoreRequiredParameters;
|
||||||
@ -28,6 +30,8 @@ class TestClass
|
|||||||
{
|
{
|
||||||
$this->publicProperty = $value;
|
$this->publicProperty = $value;
|
||||||
$this->publicAccessor = $value;
|
$this->publicAccessor = $value;
|
||||||
|
$this->publicMethodAccessor = $value;
|
||||||
|
$this->publicMethodMutator = $value;
|
||||||
$this->publicAccessorWithDefaultValue = $value;
|
$this->publicAccessorWithDefaultValue = $value;
|
||||||
$this->publicAccessorWithRequiredAndDefaultValue = $value;
|
$this->publicAccessorWithRequiredAndDefaultValue = $value;
|
||||||
$this->publicAccessorWithMoreRequiredParameters = $value;
|
$this->publicAccessorWithMoreRequiredParameters = $value;
|
||||||
@ -95,6 +99,21 @@ class TestClass
|
|||||||
return $this->publicHasAccessor;
|
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)
|
protected function setProtectedAccessor($value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ abstract class PropertyAccessorCollectionTest extends \PHPUnit_Framework_TestCas
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
|
* @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()
|
public function testSetValueFailsIfNoAdderNorRemoverFound()
|
||||||
{
|
{
|
||||||
|
@ -28,38 +28,23 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->propertyAccessor = new PropertyAccessor();
|
$this->propertyAccessor = new PropertyAccessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValidPropertyPaths()
|
public function getValidGetPropertyPaths()
|
||||||
{
|
{
|
||||||
return array(
|
return array_merge(
|
||||||
array(array('Bernhard', 'Schussek'), '[0]', 'Bernhard'),
|
array(
|
||||||
array(array('Bernhard', 'Schussek'), '[1]', 'Schussek'),
|
array(new TestClass('Bernhard'), 'publicMethodAccessor', 'Bernhard', 'Bernhard'),
|
||||||
array(array('firstName' => 'Bernhard'), '[firstName]', 'Bernhard'),
|
),
|
||||||
array(array('index' => array('firstName' => 'Bernhard')), '[index][firstName]', 'Bernhard'),
|
$this->getValidPropertyPaths()
|
||||||
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
|
public function getValidSetPropertyPaths()
|
||||||
array(new TestClass('Bernhard'), 'publicProperty', 'Bernhard'),
|
{
|
||||||
array(new TestClass('Bernhard'), 'publicAccessor', 'Bernhard'),
|
return array_merge(
|
||||||
array(new TestClass('Bernhard'), 'publicAccessorWithDefaultValue', 'Bernhard'),
|
array(
|
||||||
array(new TestClass('Bernhard'), 'publicAccessorWithRequiredAndDefaultValue', 'Bernhard'),
|
array(new TestClass('Bernhard'), 'publicMethodMutator', 'Bernhard', 'Bernhard'),
|
||||||
array(new TestClass('Bernhard'), 'publicIsAccessor', 'Bernhard'),
|
),
|
||||||
array(new TestClass('Bernhard'), 'publicHasAccessor', 'Bernhard'),
|
$this->getValidPropertyPaths()
|
||||||
|
|
||||||
// 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'),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +80,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getValidPropertyPaths
|
* @dataProvider getValidGetPropertyPaths
|
||||||
*/
|
*/
|
||||||
public function testGetValue($objectOrArray, $path, $value)
|
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)
|
public function testSetValue($objectOrArray, $path)
|
||||||
{
|
{
|
||||||
@ -312,7 +297,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getValidPropertyPaths
|
* @dataProvider getValidGetPropertyPaths
|
||||||
*/
|
*/
|
||||||
public function testIsReadable($objectOrArray, $path)
|
public function testIsReadable($objectOrArray, $path)
|
||||||
{
|
{
|
||||||
@ -380,7 +365,7 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dataProvider getValidPropertyPaths
|
* @dataProvider getValidSetPropertyPaths
|
||||||
*/
|
*/
|
||||||
public function testIsWritable($objectOrArray, $path)
|
public function testIsWritable($objectOrArray, $path)
|
||||||
{
|
{
|
||||||
@ -446,4 +431,39 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
$this->assertFalse($this->propertyAccessor->isWritable('', 'foobar', 'Updated'));
|
$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