* @author Nicolas Grekas
+ * @author Luis Ramón López
*/
class PropertyAccessor implements PropertyAccessorInterface
{
@@ -141,10 +144,16 @@ class PropertyAccessor implements PropertyAccessorInterface
* @var array
*/
private $writePropertyCache = array();
+
private static $previousErrorHandler = false;
private static $errorHandler = array(__CLASS__, 'handleError');
private static $resultProto = array(self::VALUE => null);
+ /**
+ * @var ClassMetadataFactoryInterface
+ */
+ private $classMetadataFactory;
+
/**
* @var array
*/
@@ -154,15 +163,17 @@ class PropertyAccessor implements PropertyAccessorInterface
* Should not be used by application code. Use
* {@link PropertyAccess::createPropertyAccessor()} instead.
*
- * @param bool $magicCall
- * @param bool $throwExceptionOnInvalidIndex
- * @param CacheItemPoolInterface $cacheItemPool
+ * @param bool $magicCall
+ * @param bool $throwExceptionOnInvalidIndex
+ * @param CacheItemPoolInterface $cacheItemPool
+ * @param ClassMetadataFactoryInterface $classMetadataFactory
*/
- public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null)
+ public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, MetadataFactoryInterface $classMetadataFactory = null)
{
$this->magicCall = $magicCall;
$this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex;
$this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value
+ $this->classMetadataFactory = $classMetadataFactory;
}
/**
@@ -544,17 +555,29 @@ private function getReadAccessInfo($class, $property)
}
}
+ /** @var $metadata */
+ $metadata = null;
$access = array();
$reflClass = new \ReflectionClass($class);
- $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
+ $hasProperty = $reflClass->hasProperty($property);
+ $access[self::ACCESS_HAS_PROPERTY] = $hasProperty;
+
+ if ($this->classMetadataFactory) {
+ $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertyMetadataCollection();
+ $metadata = isset($metadata[$property]) ? $metadata[$property] : null;
+ }
+
$camelProp = $this->camelize($property);
$getter = 'get'.$camelProp;
$getsetter = lcfirst($camelProp); // jQuery style, e.g. read: last(), write: last($item)
$isser = 'is'.$camelProp;
$hasser = 'has'.$camelProp;
- if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
+ if ($metadata && $metadata->getGetter()) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
+ $access[self::ACCESS_NAME] = $metadata->getGetter();
+ } elseif ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) {
$access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
$access[self::ACCESS_NAME] = $getter;
} elseif ($reflClass->hasMethod($getsetter) && $reflClass->getMethod($getsetter)->isPublic()) {
@@ -721,67 +744,93 @@ private function getWriteAccessInfo($class, $property, $value)
}
}
+ $metadata = null;
$access = array();
$reflClass = new \ReflectionClass($class);
- $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property);
- $camelized = $this->camelize($property);
- $singulars = (array) Inflector::singularize($camelized);
+ $hasProperty = $reflClass->hasProperty($property);
+ $access[self::ACCESS_HAS_PROPERTY] = $hasProperty;
+
+ $traversable = is_array($value) || $value instanceof \Traversable;
+ $done = false;
+
+ if ($this->classMetadataFactory) {
+ $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertyMetadataCollection();
+ $metadata = isset($metadata[$property]) ? $metadata[$property] : null;
+
+ if ($metadata) {
+ if ($traversable && $metadata->getAdder() && $metadata->getRemover()) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
+ $access[self::ACCESS_ADDER] = $metadata->getAdder();
+ $access[self::ACCESS_REMOVER] = $metadata->getRemover();
+ $done = true;
+ } elseif ($metadata->getSetter()) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
+ $access[self::ACCESS_NAME] = $metadata->getSetter();
+ $done = true;
+ }
+ }
+ }
- if (is_array($value) || $value instanceof \Traversable) {
- $methods = $this->findAdderAndRemover($reflClass, $singulars);
+ if (!$done) {
+ $camelized = $this->camelize($property);
+ $singulars = (array)Inflector::singularize($camelized);
- if (null !== $methods) {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
- $access[self::ACCESS_ADDER] = $methods[0];
- $access[self::ACCESS_REMOVER] = $methods[1];
+ if ($traversable) {
+ $methods = $this->findAdderAndRemover($reflClass, $singulars);
+
+ if (null !== $methods) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER;
+ $access[self::ACCESS_ADDER] = $methods[0];
+ $access[self::ACCESS_REMOVER] = $methods[1];
+ }
}
- }
- if (!isset($access[self::ACCESS_TYPE])) {
- $setter = 'set'.$camelized;
- $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item)
-
- if ($this->isMethodAccessible($reflClass, $setter, 1)) {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
- $access[self::ACCESS_NAME] = $setter;
- } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
- $access[self::ACCESS_NAME] = $getsetter;
- } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
- $access[self::ACCESS_NAME] = $property;
- } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
- $access[self::ACCESS_NAME] = $property;
- } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
- // we call the getter and hope the __call do the job
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
- $access[self::ACCESS_NAME] = $setter;
- } elseif (null !== $methods = $this->findAdderAndRemover($reflClass, $singulars)) {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
- $access[self::ACCESS_NAME] = sprintf(
- 'The property "%s" in class "%s" can be defined with the methods "%s()" but '.
- 'the new value must be an array or an instance of \Traversable, '.
- '"%s" given.',
- $property,
- $reflClass->name,
- implode('()", "', $methods),
- is_object($value) ? get_class($value) : gettype($value)
- );
- } else {
- $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
- $access[self::ACCESS_NAME] = sprintf(
- '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,
- $getsetter,
- $reflClass->name
- );
+ if (!isset($access[self::ACCESS_TYPE])) {
+ $setter = 'set' . $camelized;
+ $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item)
+
+ if ($this->isMethodAccessible($reflClass, $setter, 1)) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
+ $access[self::ACCESS_NAME] = $setter;
+ } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD;
+ $access[self::ACCESS_NAME] = $getsetter;
+ } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
+ $access[self::ACCESS_NAME] = $property;
+ } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY;
+ $access[self::ACCESS_NAME] = $property;
+ } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) {
+ // we call the getter and hope the __call do the job
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC;
+ $access[self::ACCESS_NAME] = $setter;
+ } elseif (null !== $methods = $this->findAdderAndRemover($reflClass, $singulars)) {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
+ $access[self::ACCESS_NAME] = sprintf(
+ 'The property "%s" in class "%s" can be defined with the methods "%s()" but ' .
+ 'the new value must be an array or an instance of \Traversable, ' .
+ '"%s" given.',
+ $property,
+ $reflClass->name,
+ implode('()", "', $methods),
+ is_object($value) ? get_class($value) : gettype($value)
+ );
+ } else {
+ $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND;
+ $access[self::ACCESS_NAME] = sprintf(
+ '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,
+ $getsetter,
+ $reflClass->name
+ );
+ }
}
}
diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php b/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php
index 3225cf9bc6b40..f34f34a947d6a 100644
--- a/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php
+++ b/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php
@@ -12,11 +12,13 @@
namespace Symfony\Component\PropertyAccess;
use Psr\Cache\CacheItemPoolInterface;
+use Symfony\Component\PropertyAccess\Mapping\Factory\MetadataFactoryInterface;
/**
* A configurable builder to create a PropertyAccessor.
*
* @author Jérémie Augustin
+ * @author Luis Ramón López
*/
class PropertyAccessorBuilder
{
@@ -35,6 +37,11 @@ class PropertyAccessorBuilder
*/
private $cacheItemPool;
+ /**
+ * @var MetadataFactoryInterface
+ */
+ private $metadataFactoryInterface = null;
+
/**
* Enables the use of "__call" by the PropertyAccessor.
*
@@ -128,6 +135,29 @@ public function getCacheItemPool()
return $this->cacheItemPool;
}
+ /**
+ * Allows to take into account metadata in order to override getter/setter/adder and remover method
+ * calls to properties.
+ *
+ * @param MetadataFactoryInterface|null $metadataFactoryInterface
+ *
+ * @return PropertyAccessorBuilder The builder object
+ */
+ public function setMetadataFactory(MetadataFactoryInterface $metadataFactoryInterface = null)
+ {
+ $this->metadataFactoryInterface = $metadataFactoryInterface;
+
+ return $this;
+ }
+
+ /**
+ * @return MetadataFactoryInterface|null the current object that retrieves metadata or null if not used
+ */
+ public function getMetadataFactory()
+ {
+ return $this->metadataFactoryInterface;
+ }
+
/**
* Builds and returns a new PropertyAccessor object.
*
@@ -135,6 +165,6 @@ public function getCacheItemPool()
*/
public function getPropertyAccessor()
{
- return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool);
+ return new PropertyAccessor($this->magicCall, $this->throwExceptionOnInvalidIndex, $this->cacheItemPool, $this->metadataFactoryInterface);
}
}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php
new file mode 100644
index 0000000000000..dc1ce60818ba5
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
+
+use Symfony\Component\PropertyAccess\Annotation\Property;
+use Symfony\Component\PropertyAccess\Annotation\PropertyGetter;
+
+/**
+ * Fixtures for testing metadata.
+ */
+class Dummy extends DummyParent
+{
+ /**
+ * @Property(getter="getter1", setter="setter1", adder="adder1", remover="remover1")
+ */
+ protected $foo;
+
+ /**
+ * @Property(getter="getter2")
+ */
+ protected $bar;
+
+ /**
+ * @return mixed
+ */
+ public function getter1()
+ {
+ return $this->foo;
+ }
+
+ /**
+ * @param mixed $foo
+ */
+ public function setter1($foo)
+ {
+ $this->foo = $foo;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getter2()
+ {
+ return $this->bar;
+ }
+
+ /**
+ * @param mixed $bar
+ */
+ public function setBar($bar)
+ {
+ $this->bar = $bar;
+ }
+
+ /**
+ * @PropertyGetter(property="test")
+ */
+ public function testChild()
+ {
+ return 'child';
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php
new file mode 100644
index 0000000000000..2475d14c96f82
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
+
+use Symfony\Component\PropertyAccess\Annotation\PropertyGetter;
+
+/**
+ * Fixtures for testing metadata.
+ */
+class DummyParent
+{
+ /**
+ * @PropertyGetter(property="test")
+ */
+ public function testParent()
+ {
+ return 'parent';
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
index e63af3a8bac5d..2276e03c4ffb8 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php
@@ -11,11 +11,14 @@
namespace Symfony\Component\PropertyAccess\Tests\Fixtures;
+use Symfony\Component\PropertyAccess\Annotation\Property;
+use Symfony\Component\PropertyAccess\Annotation\PropertyGetter;
+use Symfony\Component\PropertyAccess\Annotation\PropertySetter;
+
class TestClass
{
public $publicProperty;
protected $protectedProperty;
- private $privateProperty;
private $publicAccessor;
private $publicMethodAccessor;
@@ -28,7 +31,14 @@ class TestClass
private $publicGetter;
private $date;
- public function __construct($value)
+ private $quantity;
+
+ /**
+ * @Property(getter="customGetterTest", setter="customSetterTest")
+ */
+ private $customGetterSetter;
+
+ public function __construct($value, $quantity = 2, $pricePerUnit = 10)
{
$this->publicProperty = $value;
$this->publicAccessor = $value;
@@ -40,6 +50,9 @@ public function __construct($value)
$this->publicIsAccessor = $value;
$this->publicHasAccessor = $value;
$this->publicGetter = $value;
+ $this->customGetterSetter = $value;
+ $this->quantity = $quantity;
+ $this->pricePerUnit = $pricePerUnit;
}
public function setPublicAccessor($value)
@@ -184,4 +197,40 @@ public function getDate()
{
return $this->date;
}
+
+ public function customGetterTest()
+ {
+ return $this->customGetterSetter;
+ }
+
+ public function customSetterTest($value)
+ {
+ $this->customGetterSetter = $value;
+ }
+
+ /**
+ * @return int
+ */
+ public function getQuantity()
+ {
+ return $this->quantity;
+ }
+
+ /**
+ * @PropertyGetter(property="total")
+ */
+ public function getTotal()
+ {
+ return $this->quantity * $this->pricePerUnit;
+ }
+
+ /**
+ * @PropertySetter(property="total")
+ *
+ * @param mixed $total
+ */
+ public function setTotal($total)
+ {
+ $this->quantity = $total / $this->pricePerUnit;
+ }
}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/empty-mapping.yml b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/empty-mapping.yml
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/invalid-mapping.yml b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/invalid-mapping.yml
new file mode 100644
index 0000000000000..19102815663d2
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/invalid-mapping.yml
@@ -0,0 +1 @@
+foo
\ No newline at end of file
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.xml b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.xml
new file mode 100644
index 0000000000000..990b2ad9dfbc5
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml
new file mode 100644
index 0000000000000..4c78d1bc4be62
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml
@@ -0,0 +1,9 @@
+'Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy':
+ properties:
+ foo:
+ getter: getter1
+ setter: setter1
+ adder: adder1
+ remover: remover1
+ bar:
+ getter: getter2
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php
new file mode 100644
index 0000000000000..e54a392befa76
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping;
+
+use Symfony\Component\PropertyAccess\Mapping\ClassMetadata;
+
+/**
+ * @author Kévin Dunglas
+ */
+class ClassMetadataTest extends \PHPUnit_Framework_TestCase
+{
+ public function testInterface()
+ {
+ $classMetadata = new ClassMetadata('name');
+ $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\ClassMetadata', $classMetadata);
+ }
+
+ public function testAttributeMetadata()
+ {
+ $classMetadata = new ClassMetadata('c');
+
+ $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata');
+ $a1->method('getName')->willReturn('a1');
+
+ $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata');
+ $a2->method('getName')->willReturn('a2');
+
+ $classMetadata->addPropertyMetadata($a1);
+ $classMetadata->addPropertyMetadata($a2);
+
+ $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getPropertyMetadataCollection());
+ }
+
+ public function testSerialize()
+ {
+ $classMetadata = new ClassMetadata('a');
+
+ $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata');
+ $a1->method('getName')->willReturn('b1');
+ $a1->method('__sleep')->willReturn([]);
+
+ $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata');
+ $a2->method('getName')->willReturn('b2');
+ $a2->method('__sleep')->willReturn([]);
+
+ $classMetadata->addPropertyMetadata($a1);
+ $classMetadata->addPropertyMetadata($a2);
+
+ $serialized = serialize($classMetadata);
+ $this->assertEquals($classMetadata, unserialize($serialized));
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php
new file mode 100644
index 0000000000000..d49b37eae8496
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php
@@ -0,0 +1,33 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping\Factory;
+
+use Symfony\Component\PropertyAccess\Mapping\Factory\BlackHoleMetadataFactory;
+
+class BlackHoleMetadataFactoryTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @expectedException \LogicException
+ */
+ public function testGetMetadataForThrowsALogicException()
+ {
+ $metadataFactory = new BlackHoleMetadataFactory();
+ $metadataFactory->getMetadataFor('foo');
+ }
+
+ public function testHasMetadataForReturnsFalse()
+ {
+ $metadataFactory = new BlackHoleMetadataFactory();
+
+ $this->assertFalse($metadataFactory->hasMetadataFor('foo'));
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php
new file mode 100644
index 0000000000000..7549b652c0bad
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php
@@ -0,0 +1,141 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping\Factory;
+
+use Symfony\Component\PropertyAccess\Mapping\ClassMetadata;
+use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory;
+use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface;
+use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata;
+
+class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase
+{
+ const CLASSNAME = 'Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy';
+ const PARENTCLASS = 'Symfony\Component\PropertyAccess\Tests\Fixtures\DummyParent';
+
+ public function testLoadClassMetadata()
+ {
+ $factory = new LazyLoadingMetadataFactory(new TestLoader());
+ $metadata = $factory->getMetadataFor(self::PARENTCLASS);
+
+ $properties = array(
+ self::PARENTCLASS => new PropertyMetadata(self::PARENTCLASS),
+ );
+
+ $this->assertEquals($properties, $metadata->getPropertyMetadataCollection());
+ }
+
+ public function testMergeParentMetadata()
+ {
+ $factory = new LazyLoadingMetadataFactory(new TestLoader());
+ $metadata = $factory->getMetadataFor(self::CLASSNAME);
+
+ $properties = array(
+ self::PARENTCLASS => new PropertyMetadata(self::PARENTCLASS),
+ self::CLASSNAME => new PropertyMetadata(self::CLASSNAME),
+ );
+
+ $this->assertEquals($properties, $metadata->getPropertyMetadataCollection());
+ }
+
+ public function testWriteMetadataToCache()
+ {
+ $cache = $this->getMock('Psr\Cache\CacheItemPoolInterface');
+ $factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache);
+
+ $properties = array(
+ self::PARENTCLASS => new PropertyMetadata(self::PARENTCLASS),
+ );
+
+ $cacheItem = $this->getMock('Psr\Cache\CacheItemInterface');
+
+ $cache->expects($this->once())
+ ->method('getItem')
+ ->with($this->equalTo($this->escapeClassName(self::PARENTCLASS)))
+ ->will($this->returnValue($cacheItem));
+
+ $cacheItem->expects($this->once())
+ ->method('isHit')
+ ->will($this->returnValue(false));
+
+ $cacheItem->expects($this->once())
+ ->method('set')
+ ->will($this->returnCallback(function ($metadata) use ($properties) {
+ $this->assertEquals($properties, $metadata->getPropertyMetadataCollection());
+ }));
+
+ $cache->expects($this->once())
+ ->method('save')
+ ->with($this->equalTo($cacheItem))
+ ->will($this->returnValue(true));
+
+ $metadata = $factory->getMetadataFor(self::PARENTCLASS);
+
+ $this->assertEquals(self::PARENTCLASS, $metadata->getName());
+ $this->assertEquals($properties, $metadata->getPropertyMetadataCollection());
+ }
+
+ public function testReadMetadataFromCache()
+ {
+ $loader = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface');
+ $cache = $this->getMock('Psr\Cache\CacheItemPoolInterface');
+ $factory = new LazyLoadingMetadataFactory($loader, $cache);
+
+ $metadata = new ClassMetadata(self::PARENTCLASS);
+ $metadata->addPropertyMetadata(new PropertyMetadata());
+
+ $loader->expects($this->never())
+ ->method('loadClassMetadata');
+
+ $cacheItem = $this->getMock('Psr\Cache\CacheItemInterface');
+
+ $cache->expects($this->once())
+ ->method('getItem')
+ ->with($this->equalTo($this->escapeClassName(self::PARENTCLASS)))
+ ->will($this->returnValue($cacheItem));
+
+ $cacheItem->expects($this->once())
+ ->method('isHit')
+ ->will($this->returnValue(true));
+
+ $cacheItem->expects($this->once())
+ ->method('get')
+ ->will($this->returnValue($metadata));
+
+ $cacheItem->expects($this->never())
+ ->method('set');
+
+ $cache->expects($this->never())
+ ->method('save');
+
+ $this->assertEquals($metadata, $factory->getMetadataFor(self::PARENTCLASS));
+ }
+
+ /**
+ * Replaces backslashes by dots in a class name.
+ *
+ * @param string $class
+ *
+ * @return string
+ */
+ private function escapeClassName($class)
+ {
+ return str_replace('\\', '.', $class);
+ }
+}
+
+class TestLoader implements LoaderInterface
+{
+ public function loadClassMetadata(ClassMetadata $metadata)
+ {
+ $metadata->addPropertyMetadata(new PropertyMetadata($metadata->getName()));
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/AnnotationLoaderTest.php
new file mode 100644
index 0000000000000..0cfa2c1fbaf89
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/AnnotationLoaderTest.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping\Loader;
+
+use Doctrine\Common\Annotations\AnnotationReader;
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Symfony\Component\PropertyAccess\Mapping\ClassMetadata;
+use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader;
+use Symfony\Component\PropertyAccess\Tests\Mapping\TestClassMetadataFactory;
+
+/**
+ * @author Kévin Dunglas
+ * @author Luis Ramón López
+ */
+class AnnotationLoaderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var AnnotationLoader
+ */
+ private $loader;
+
+ protected function setUp()
+ {
+ $this->loader = new AnnotationLoader(new AnnotationReader());
+ }
+
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface', $this->loader);
+ }
+
+ public function testLoadClassMetadataReturnsTrueIfSuccessful()
+ {
+ $classMetadata = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy');
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..');
+ $this->assertTrue($this->loader->loadClassMetadata($classMetadata));
+ }
+
+ public function testLoadMetadata()
+ {
+ $classMetadata = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy');
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..');
+ $this->loader->loadClassMetadata($classMetadata);
+
+ $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata);
+ }
+
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/XmlFileLoaderTest.php
new file mode 100644
index 0000000000000..4162dec218808
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/XmlFileLoaderTest.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping\Loader;
+
+use Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader;
+use Symfony\Component\PropertyAccess\Mapping\ClassMetadata;
+use Symfony\Component\PropertyAccess\Tests\Mapping\TestClassMetadataFactory;
+
+/**
+ * @author Kévin Dunglas
+ * @author Luis Ramón López
+ */
+class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var XmlFileLoader
+ */
+ private $loader;
+ /**
+ * @var ClassMetadata
+ */
+ private $metadata;
+
+ protected function setUp()
+ {
+ $this->loader = new XmlFileLoader(__DIR__.'/../../Fixtures/property-access.xml');
+ $this->metadata = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy');
+ }
+
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface', $this->loader);
+ }
+
+ public function testLoadClassMetadataReturnsTrueIfSuccessful()
+ {
+ $this->assertTrue($this->loader->loadClassMetadata($this->metadata));
+ }
+
+ public function testLoadClassMetadata()
+ {
+ $this->loader->loadClassMetadata($this->metadata);
+
+ $this->assertEquals(TestClassMetadataFactory::createXmlClassMetadata(), $this->metadata);
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/YamlFileLoaderTest.php
new file mode 100644
index 0000000000000..63e299b2de76d
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/YamlFileLoaderTest.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping\Loader;
+
+use Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader;
+use Symfony\Component\PropertyAccess\Mapping\ClassMetadata;
+use Symfony\Component\PropertyAccess\Tests\Mapping\TestClassMetadataFactory;
+
+/**
+ * @author Kévin Dunglas
+ * @author Luis Ramón López
+ */
+class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * @var YamlFileLoader
+ */
+ private $loader;
+ /**
+ * @var ClassMetadata
+ */
+ private $metadata;
+
+ protected function setUp()
+ {
+ $this->loader = new YamlFileLoader(__DIR__.'/../../Fixtures/property-access.yml');
+ $this->metadata = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy');
+ }
+
+ public function testInterface()
+ {
+ $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface', $this->loader);
+ }
+
+ public function testLoadClassMetadataReturnsTrueIfSuccessful()
+ {
+ $this->assertTrue($this->loader->loadClassMetadata($this->metadata));
+ }
+
+ public function testLoadClassMetadataReturnsFalseWhenEmpty()
+ {
+ $loader = new YamlFileLoader(__DIR__.'/../../Fixtures/empty-mapping.yml');
+ $this->assertFalse($loader->loadClassMetadata($this->metadata));
+ }
+
+ /**
+ * @expectedException \Symfony\Component\PropertyAccess\Exception\MappingException
+ */
+ public function testLoadClassMetadataReturnsThrowsInvalidMapping()
+ {
+ $loader = new YamlFileLoader(__DIR__.'/../../Fixtures/invalid-mapping.yml');
+ $loader->loadClassMetadata($this->metadata);
+ }
+
+ public function testLoadClassMetadata()
+ {
+ $this->loader->loadClassMetadata($this->metadata);
+
+ $this->assertEquals(TestClassMetadataFactory::createXmlClassMetadata(), $this->metadata);
+ }
+
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/PropertyMetadataTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/PropertyMetadataTest.php
new file mode 100644
index 0000000000000..827d7c57169e1
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/PropertyMetadataTest.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping;
+
+use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata;
+
+/**
+ * @author Kévin Dunglas
+ */
+class PropertyMetadataTest extends \PHPUnit_Framework_TestCase
+{
+ public function testInterface()
+ {
+ $propertyMetadata = new PropertyMetadata('name');
+ $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata', $propertyMetadata);
+ }
+
+ public function testGetName()
+ {
+ $propertyMetadata = new PropertyMetadata('name');
+ $this->assertEquals('name', $propertyMetadata->getName());
+ }
+
+ public function testGetter()
+ {
+ $propertyMetadata = new PropertyMetadata('name');
+ $propertyMetadata->setGetter('one');
+
+ $this->assertEquals('one', $propertyMetadata->getGetter());
+ }
+
+ public function testSetter()
+ {
+ $propertyMetadata = new PropertyMetadata('name');
+ $propertyMetadata->setSetter('one');
+
+ $this->assertEquals('one', $propertyMetadata->getSetter());
+ }
+
+ public function testAdder()
+ {
+ $propertyMetadata = new PropertyMetadata('name');
+ $propertyMetadata->setAdder('one');
+
+ $this->assertEquals('one', $propertyMetadata->getAdder());
+ }
+
+ public function testRemover()
+ {
+ $propertyMetadata = new PropertyMetadata('name');
+ $propertyMetadata->setRemover('one');
+
+ $this->assertEquals('one', $propertyMetadata->getRemover());
+ }
+
+ public function testMerge()
+ {
+ $propertyMetadata1 = new PropertyMetadata('a1');
+ $propertyMetadata1->setGetter('a');
+ $propertyMetadata1->setSetter('b');
+
+ $propertyMetadata2 = new PropertyMetadata('a2');
+ $propertyMetadata2->setGetter('c');
+ $propertyMetadata2->setAdder('d');
+ $propertyMetadata2->setRemover('e');
+
+ $propertyMetadata1->merge($propertyMetadata2);
+
+ $this->assertEquals('a', $propertyMetadata1->getGetter());
+ $this->assertEquals('b', $propertyMetadata1->getSetter());
+ $this->assertEquals('d', $propertyMetadata1->getAdder());
+ $this->assertEquals('e', $propertyMetadata1->getRemover());
+ }
+
+ public function testSerialize()
+ {
+ $propertyMetadata = new PropertyMetadata('attribute');
+ $propertyMetadata->setGetter('a');
+ $propertyMetadata->setSetter('b');
+ $propertyMetadata->setAdder('c');
+ $propertyMetadata->setRemover('d');
+
+ $serialized = serialize($propertyMetadata);
+ $this->assertEquals($propertyMetadata, unserialize($serialized));
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php
new file mode 100644
index 0000000000000..251259031872c
--- /dev/null
+++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\PropertyAccess\Tests\Mapping;
+
+use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata;
+use Symfony\Component\PropertyAccess\Mapping\ClassMetadata;
+
+/**
+ * @author Kévin Dunglas
+ */
+class TestClassMetadataFactory
+{
+ public static function createClassMetadata()
+ {
+ $expected = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy');
+
+ $expected->getReflectionClass();
+
+ $foo = new PropertyMetadata('foo');
+ $foo->setGetter('getter1');
+ $foo->setSetter('setter1');
+ $foo->setAdder('adder1');
+ $foo->setRemover('remover1');
+ $expected->addPropertyMetadata($foo);
+
+ $bar = new PropertyMetadata('bar');
+ $bar->setGetter('getter2');
+ $expected->addPropertyMetadata($bar);
+
+ $test = new PropertyMetadata('test');
+ $test->setGetter('testChild');
+ $expected->addPropertyMetadata($test);
+
+ return $expected;
+ }
+
+ public static function createXMLClassMetadata()
+ {
+ $expected = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy');
+
+ $foo = new PropertyMetadata('foo');
+ $foo->setGetter('getter1');
+ $foo->setSetter('setter1');
+ $foo->setAdder('adder1');
+ $foo->setRemover('remover1');
+ $expected->addPropertyMetadata($foo);
+
+ $bar = new PropertyMetadata('bar');
+ $bar->setGetter('getter2');
+ $expected->addPropertyMetadata($bar);
+
+ return $expected;
+ }
+}
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php
index 2c65e6adf6ddd..7f53fe9880987 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php
@@ -49,6 +49,14 @@ public function testIsMagicCallEnable()
$this->assertFalse($this->builder->disableMagicCall()->isMagicCallEnabled());
}
+ public function testMetadataFactory()
+ {
+ $metadataFactory = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Factory\MetadataFactoryInterface');
+ $this->assertNull($this->builder->getMetadataFactory());
+ $this->assertSame($metadataFactory, $this->builder->setMetadataFactory($metadataFactory)->getMetadataFactory());
+ $this->assertNull($this->builder->setMetadataFactory(null)->getMetadataFactory());
+ }
+
public function testGetPropertyAccessor()
{
$this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor());
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
index 17518468ebad8..54f7f6642b912 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php
@@ -11,13 +11,28 @@
namespace Symfony\Component\PropertyAccess\Tests;
+use Doctrine\Common\Annotations\AnnotationReader;
+use Doctrine\Common\Annotations\AnnotationRegistry;
+use Symfony\Component\PropertyAccess\Annotation\PropertyAdder;
+use Symfony\Component\PropertyAccess\Annotation\PropertyGetter;
+use Symfony\Component\PropertyAccess\Annotation\PropertyRemover;
+use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory;
+use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader;
+use Symfony\Component\PropertyAccess\PropertyAccessor;
+
class PropertyAccessorCollectionTest_Car
{
private $axes;
+ /**
+ * @Symfony\Component\PropertyAccess\Annotation\Property(adder="addAxisTest", remover="removeAxisTest")
+ */
+ private $customAxes;
+
public function __construct($axes = null)
{
$this->axes = $axes;
+ $this->customAxes = $axes;
}
// In the test, use a name that StringUtil can't uniquely singularify
@@ -26,6 +41,16 @@ public function addAxis($axis)
$this->axes[] = $axis;
}
+ // In the test, use a name that StringUtil can't uniquely singularify
+ /**
+ * @PropertyAdder(property="customVirtualAxes")
+ * @param $axis
+ */
+ public function addAxisTest($axis)
+ {
+ $this->customAxes[] = $axis;
+ }
+
public function removeAxis($axis)
{
foreach ($this->axes as $key => $value) {
@@ -37,10 +62,34 @@ public function removeAxis($axis)
}
}
+ /**
+ * @PropertyRemover(property="customVirtualAxes")
+ * @param $axis
+ */
+ public function removeAxisTest($axis)
+ {
+ foreach ($this->customAxes as $key => $value) {
+ if ($value === $axis) {
+ unset($this->customAxes[$key]);
+
+ return;
+ }
+ }
+ }
+
public function getAxes()
{
return $this->axes;
}
+
+ /**
+ * @PropertyGetter(property="customVirtualAxes")
+ * @return null
+ */
+ public function getCustomAxes()
+ {
+ return $this->customAxes;
+ }
}
class PropertyAccessorCollectionTest_CarOnlyAdder
@@ -146,6 +195,50 @@ public function testSetValueCallsAdderAndRemoverForNestedCollections()
$this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter);
}
+ public function testSetValueCallsCustomAdderAndRemoverForCollections()
+ {
+ $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth', 4 => 'fifth'));
+ $axesMerged = $this->getContainer(array(1 => 'first', 2 => 'second', 3 => 'third'));
+ $axesAfter = $this->getContainer(array(1 => 'second', 5 => 'first', 6 => 'third'));
+ $axesMergedCopy = is_object($axesMerged) ? clone $axesMerged : $axesMerged;
+
+ // Don't use a mock in order to test whether the collections are
+ // modified while iterating them
+ $car = new PropertyAccessorCollectionTest_Car($axesBefore);
+
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..');
+ $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader())));
+
+ $this->propertyAccessor->setValue($car, 'customAxes', $axesMerged);
+
+ $this->assertEquals($axesAfter, $car->getCustomAxes());
+
+ // The passed collection was not modified
+ $this->assertEquals($axesMergedCopy, $axesMerged);
+ }
+
+ public function testSetValueCallsCustomAdderAndRemoverForCollectionsMethodAnnotation()
+ {
+ $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth', 4 => 'fifth'));
+ $axesMerged = $this->getContainer(array(1 => 'first', 2 => 'second', 3 => 'third'));
+ $axesAfter = $this->getContainer(array(1 => 'second', 5 => 'first', 6 => 'third'));
+ $axesMergedCopy = is_object($axesMerged) ? clone $axesMerged : $axesMerged;
+
+ // Don't use a mock in order to test whether the collections are
+ // modified while iterating them
+ $car = new PropertyAccessorCollectionTest_Car($axesBefore);
+
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..');
+ $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader())));
+
+ $this->propertyAccessor->setValue($car, 'customVirtualAxes', $axesMerged);
+
+ $this->assertEquals($axesAfter, $car->getCustomAxes());
+
+ // The passed collection was not modified
+ $this->assertEquals($axesMergedCopy, $axesMerged);
+ }
+
/**
* @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
* @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
diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
index a3a82b0b63cba..c96402b8c81ce 100644
--- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
+++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
@@ -11,8 +11,12 @@
namespace Symfony\Component\PropertyAccess\Tests;
+use Doctrine\Common\Annotations\AnnotationReader;
+use Doctrine\Common\Annotations\AnnotationRegistry;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
+use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory;
+use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass;
use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall;
@@ -198,6 +202,20 @@ public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p
$this->propertyAccessor->getValue($objectOrArray, $path);
}
+ public function testGetWithCustomGetter()
+ {
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..');
+ $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader())));
+ $this->assertSame('webmozart', $this->propertyAccessor->getValue(new TestClass('webmozart'), 'customGetterSetter'));
+ }
+
+ public function testGetWithCustomGetterMethodAnnotation()
+ {
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..');
+ $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader())));
+ $this->assertSame(200, $this->propertyAccessor->getValue(new TestClass('webmozart', 10, 20), 'total'));
+ }
+
/**
* @dataProvider getValidPropertyPaths
*/
@@ -298,6 +316,30 @@ public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p
$this->propertyAccessor->setValue($objectOrArray, $path, 'value');
}
+ public function testSetValueWithCustomSetter()
+ {
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..');
+ $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader())));
+
+ $custom = new TestClass('webmozart');
+
+ $this->propertyAccessor->setValue($custom, 'customGetterSetter', 'it works!');
+
+ $this->assertEquals('it works!', $custom->customGetterTest());
+ }
+
+ public function testSetValueWithCustomSetterMethodAnnotation()
+ {
+ AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..');
+ $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader())));
+
+ $custom = new TestClass('webmozart', 10, 20);
+
+ $this->propertyAccessor->setValue($custom, 'total', 5);
+
+ $this->assertEquals(5, $custom->getTotal());
+ }
+
public function testGetValueWhenArrayValueIsNull()
{
$this->propertyAccessor = new PropertyAccessor(false, true);
diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json
index e095cbe35fe91..6b594d2c65263 100644
--- a/src/Symfony/Component/PropertyAccess/composer.json
+++ b/src/Symfony/Component/PropertyAccess/composer.json
@@ -21,7 +21,11 @@
"symfony/inflector": "~3.1"
},
"require-dev": {
- "symfony/cache": "~3.1"
+ "doctrine/cache": "~1.0",
+ "doctrine/annotations": "~1.2",
+ "symfony/cache": "~3.1",
+ "symfony/config": "~2.8|~3.0",
+ "symfony/yaml": "~2.8|~3.0"
},
"suggest": {
"psr/cache-implementation": "To cache access methods."
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy