This commit is contained in:
umpirsky 2014-05-09 17:28:08 +02:00
parent ff9f06b978
commit 0cc9746f87
4 changed files with 85 additions and 37 deletions

View File

@ -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))

View File

@ -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)
{
}

View File

@ -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()
{

View File

@ -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'),
);
}
}