From af3a824990fa1fd0a08ae3c1cbce9ada16003360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sat, 5 Mar 2016 00:26:57 +0100 Subject: [PATCH 01/18] [PropertyAccess] Allow custom methods on property accesses --- .../Resources/config/property_access.xml | 1 + .../Annotation/ConfigurationAnnotation.php | 31 ++++ .../Annotation/PropertyAccessor.php | 70 ++++++++ .../PropertyAccess/PropertyAccessor.php | 161 +++++++++++------- .../Tests/Fixtures/TestClass.php | 19 +++ .../Tests/PropertyAccessorCollectionTest.php | 54 ++++++ .../Tests/PropertyAccessorTest.php | 21 +++ .../Component/PropertyAccess/composer.json | 3 +- 8 files changed, 301 insertions(+), 59 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php create mode 100644 src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml index d9e381c4806b8..aee4ee74ee01f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml @@ -9,6 +9,7 @@ + diff --git a/src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php b/src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php new file mode 100644 index 0000000000000..30756c004699a --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Annotation; + +/** + * Base configuration annotation. + * + * @author Johannes M. Schmitt + */ +abstract class ConfigurationAnnotation +{ + public function __construct(array $values) + { + foreach ($values as $k => $v) { + if (!method_exists($this, $name = 'set'.$k)) { + throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this))); + } + + $this->$name($v); + } + } +} diff --git a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php new file mode 100644 index 0000000000000..631d642509db3 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php @@ -0,0 +1,70 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Annotation; + +/** + * Property accessor configuration annotation. + * + * @Annotation + * + * @author Luis Ramón López + */ +class PropertyAccessor extends ConfigurationAnnotation +{ + protected $setter; + + protected $getter; + + protected $adder; + + protected $remover; + + public function getSetter() + { + return $this->setter; + } + + public function setSetter($setter) + { + $this->setter = $setter; + } + + public function getGetter() + { + return $this->getter; + } + + public function setGetter($getter) + { + $this->getter = $getter; + } + + public function getAdder() + { + return $this->adder; + } + + public function setAdder($adder) + { + $this->adder = $adder; + } + + public function getRemover() + { + return $this->remover; + } + + public function setRemover($remover) + { + $this->remover = $remover; + } +} diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index efd6f4653c252..e5a47f5a1f74c 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -11,6 +11,7 @@ namespace Symfony\Component\PropertyAccess; +use Doctrine\Common\Annotations\AnnotationReader; use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; @@ -29,6 +30,7 @@ * @author Bernhard Schussek * @author Kévin Dunglas * @author Nicolas Grekas + * @author Luis Ramón López */ class PropertyAccessor implements PropertyAccessorInterface { @@ -145,6 +147,11 @@ class PropertyAccessor implements PropertyAccessorInterface private static $errorHandler = array(__CLASS__, 'handleError'); private static $resultProto = array(self::VALUE => null); + /** + * @var AnnotationReader + */ + private $reader; + /** * @var array */ @@ -158,11 +165,12 @@ class PropertyAccessor implements PropertyAccessorInterface * @param bool $throwExceptionOnInvalidIndex * @param CacheItemPoolInterface $cacheItemPool */ - public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null) + public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, AnnotationReader $reader = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value + $this->reader = $reader; } /** @@ -544,17 +552,29 @@ private function getReadAccessInfo($class, $property) } } + $annotation = 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 ($hasProperty && $this->reader) { + $annotation = $this->reader->getPropertyAnnotation($reflClass->getProperty($property), + 'Symfony\Component\PropertyAccess\Annotation\PropertyAccessor'); + + } + $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 ($annotation && $annotation->getGetter()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $annotation->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,69 +741,94 @@ private function getWriteAccessInfo($class, $property, $value) } } + $annotation = 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; + + $transversable = is_array($value) || $value instanceof \Traversable; + $done = false; + + if ($hasProperty && $this->reader) { + $annotation = $this->reader->getPropertyAnnotation($reflClass->getProperty($property), + 'Symfony\Component\PropertyAccess\Annotation\PropertyAccessor'); + + if ($annotation) { + if ($transversable && $annotation->getAdder() && $annotation->getRemover()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; + $access[self::ACCESS_ADDER] = $annotation->getAdder(); + $access[self::ACCESS_REMOVER] = $annotation->getRemover(); + $done = true; + } elseif ($annotation->getSetter()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $annotation->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 + ); + } } - } if (isset($item)) { $this->cacheItemPool->save($item->set($access)); diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php index e63af3a8bac5d..b31bc4790e2e5 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php @@ -11,6 +11,9 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; +use Doctrine\ORM\Mapping\Column; +use Symfony\Component\PropertyAccess\Annotation\PropertyAccessor; + class TestClass { public $publicProperty; @@ -28,6 +31,11 @@ class TestClass private $publicGetter; private $date; + /** + * @PropertyAccessor(getter="customGetterTest", setter="customSetterTest") + */ + private $customGetterSetter; + public function __construct($value) { $this->publicProperty = $value; @@ -40,6 +48,7 @@ public function __construct($value) $this->publicIsAccessor = $value; $this->publicHasAccessor = $value; $this->publicGetter = $value; + $this->customGetterSetter = $value; } public function setPublicAccessor($value) @@ -184,4 +193,14 @@ public function getDate() { return $this->date; } + + public function customGetterTest() + { + return $this->customGetterSetter; + } + + public function customSetterTest($value) + { + $this->customGetterSetter = $value; + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index 17518468ebad8..d3ea7f8da14f8 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -11,13 +11,23 @@ namespace Symfony\Component\PropertyAccess\Tests; +use Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Component\PropertyAccess\PropertyAccessor; + class PropertyAccessorCollectionTest_Car { private $axes; + /** + * @Symfony\Component\PropertyAccess\Annotation\PropertyAccessor(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 +36,12 @@ public function addAxis($axis) $this->axes[] = $axis; } + // In the test, use a name that StringUtil can't uniquely singularify + public function addAxisTest($axis) + { + $this->customAxes[] = $axis; + } + public function removeAxis($axis) { foreach ($this->axes as $key => $value) { @@ -37,10 +53,26 @@ public function removeAxis($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; } + + public function getCustomAxes() + { + return $this->customAxes; + } } class PropertyAccessorCollectionTest_CarOnlyAdder @@ -146,6 +178,28 @@ 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, new AnnotationReader()); + + $this->propertyAccessor->setValue($car, 'customAxes', $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..b3cc3ff21d017 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -11,6 +11,8 @@ 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\PropertyAccessor; @@ -198,6 +200,13 @@ 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, new AnnotationReader()); + $this->assertSame('webmozart', $this->propertyAccessor->getValue(new TestClass('webmozart'), 'customGetterSetter')); + } + /** * @dataProvider getValidPropertyPaths */ @@ -298,6 +307,18 @@ 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, new AnnotationReader()); + + $custom = new TestClass('webmozart'); + + $this->propertyAccessor->setValue($custom, 'customGetterSetter', 'it works!'); + + $this->assertEquals('it works!', $custom->customGetterTest()); + } + 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..7ab214165f5da 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -21,6 +21,7 @@ "symfony/inflector": "~3.1" }, "require-dev": { + "doctrine/annotations": "~1.2", "symfony/cache": "~3.1" }, "suggest": { @@ -35,7 +36,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.1-dev" } } } From 5b82237b825195dabb5fb91dfb150d6f19122c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sun, 6 Mar 2016 03:37:50 +0100 Subject: [PATCH 02/18] Added support for YAML and XML mapping --- .../FrameworkExtension.php | 81 +++++++- .../Resources/config/property_access.xml | 21 ++- .../DependencyInjection/ConfigurationTest.php | 1 + .../Fixtures/php/property_accessor.php | 1 + .../Fixtures/yml/property_accessor.yml | 1 + .../Annotation/PropertyAccessor.php | 1 + .../Exception/MappingException.php | 21 +++ .../Mapping/AttributeMetadata.php | 177 ++++++++++++++++++ .../Mapping/AttributeMetadataInterface.php | 84 +++++++++ .../PropertyAccess/Mapping/ClassMetadata.php | 116 ++++++++++++ .../Mapping/ClassMetadataInterface.php | 61 ++++++ .../Factory/CacheClassMetadataFactory.php | 68 +++++++ .../Mapping/Factory/ClassMetadataFactory.php | 109 +++++++++++ .../Factory/ClassMetadataFactoryInterface.php | 53 ++++++ .../Mapping/Factory/ClassResolverTrait.php | 50 +++++ .../Mapping/Loader/AnnotationLoader.php | 73 ++++++++ .../Mapping/Loader/FileLoader.php | 47 +++++ .../Mapping/Loader/LoaderChain.php | 66 +++++++ .../Mapping/Loader/LoaderInterface.php | 31 +++ .../Mapping/Loader/XmlFileLoader.php | 104 ++++++++++ .../Mapping/Loader/YamlFileLoader.php | 117 ++++++++++++ .../property-access-mapping-1.0.xsd | 57 ++++++ .../PropertyAccess/PropertyAccessor.php | 43 ++--- .../PropertyAccess/Tests/Fixtures/Dummy.php | 62 ++++++ .../Tests/Fixtures/empty-mapping.yml | 0 .../Tests/Fixtures/invalid-mapping.yml | 1 + .../Tests/Fixtures/property-access.xml | 12 ++ .../Tests/Fixtures/property-access.yml | 9 + .../Tests/Mapping/AttributeMetadataTest.php | 95 ++++++++++ .../Tests/Mapping/ClassMetadataTest.php | 59 ++++++ .../Factory/CacheMetadataFactoryTest.php | 67 +++++++ .../Factory/ClassMetadataFactoryTest.php | 79 ++++++++ .../Mapping/Loader/AnnotationLoaderTest.php | 57 ++++++ .../Mapping/Loader/XmlFileLoaderTest.php | 55 ++++++ .../Mapping/Loader/YamlFileLoaderTest.php | 71 +++++++ .../Mapping/TestClassMetadataFactory.php | 59 ++++++ .../Tests/PropertyAccessorCollectionTest.php | 4 +- .../Tests/PropertyAccessorTest.php | 6 +- 38 files changed, 1992 insertions(+), 27 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Exception/MappingException.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/FileLoader.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/empty-mapping.yml create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/invalid-mapping.yml create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.xml create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/AnnotationLoaderTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/XmlFileLoaderTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/YamlFileLoaderTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 8cd1fea64a0fa..9c28ce1d27648 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -138,7 +138,7 @@ public function load(array $configs, ContainerBuilder $container) } $this->registerAnnotationsConfiguration($config['annotations'], $container, $loader); - $this->registerPropertyAccessConfiguration($config['property_access'], $container); + $this->registerPropertyAccessConfiguration($config['property_access'], $container, $loader); if ($this->isConfigEnabled($container, $config['serializer'])) { $this->registerSerializerConfiguration($config['serializer'], $container, $loader); @@ -915,13 +915,90 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde } } - private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container) + /** + * Loads the PropertyAccess configuration. + * + * @param array $config A serializer configuration array + * @param ContainerBuilder $container A ContainerBuilder instance + * @param XmlFileLoader $loader An XmlFileLoader instance + */ + private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { $container ->getDefinition('property_accessor') ->replaceArgument(0, $config['magic_call']) ->replaceArgument(1, $config['throw_exception_on_invalid_index']) ; + + if (!$this->isConfigEnabled($container, $config)) { + return; + } + + $loader->load('property_access.xml'); + $chainLoader = $container->getDefinition('property_access.mapping.chain_loader'); + + $serializerLoaders = array(); + if (isset($config['enable_annotations']) && $config['enable_annotations']) { + $annotationLoader = new Definition( + 'Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader', + array(new Reference('annotation_reader')) + ); + $annotationLoader->setPublic(false); + + $serializerLoaders[] = $annotationLoader; + } + + $bundles = $container->getParameter('kernel.bundles'); + foreach ($bundles as $bundle) { + $reflection = new \ReflectionClass($bundle); + $dirname = dirname($reflection->getFileName()); + + if (is_file($file = $dirname.'/Resources/config/property_access.xml')) { + $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader', array(realpath($file))); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + $container->addResource(new FileResource($file)); + } + + if (is_file($file = $dirname.'/Resources/config/property_access.yml')) { + $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader', array(realpath($file))); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + $container->addResource(new FileResource($file)); + } + + if (is_dir($dir = $dirname.'/Resources/config/property_access')) { + foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { + $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader', array($file->getRealpath())); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + } + foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { + $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader', array($file->getRealpath())); + $definition->setPublic(false); + + $serializerLoaders[] = $definition; + } + + $container->addResource(new DirectoryResource($dir)); + } + } + + $chainLoader->replaceArgument(0, $serializerLoaders); + + if (isset($config['cache']) && $config['cache']) { + $container->setParameter( + 'property_access.mapping.cache.prefix', + 'property_access_'.$this->getKernelRootHash($container) + ); + + $container->getDefinition('property_access.mapping.class_metadata_factory')->replaceArgument( + 1, new Reference($config['cache']) + ); + } } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml index aee4ee74ee01f..4deb071f4cefe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml @@ -5,11 +5,30 @@ xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + + + + + + + + + null + + + + + + %property_access.mapping.cache.prefix% + + + - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index 94b6e315b8c20..11216d10c35ed 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -225,6 +225,7 @@ protected static function getBundleDefaultConfig() 'property_access' => array( 'magic_call' => false, 'throw_exception_on_invalid_index' => false, + 'enable_annotations' => false, ), 'property_info' => array( 'enabled' => false, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php index 4340e61fc0961..d553eff165476 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/property_accessor.php @@ -4,5 +4,6 @@ 'property_access' => array( 'magic_call' => true, 'throw_exception_on_invalid_index' => true, + 'enable_annotations' => false, ), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml index b5fd2718ab112..155060fe6b924 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml @@ -2,3 +2,4 @@ framework: property_access: magic_call: true throw_exception_on_invalid_index: true + enable_annotations: true diff --git a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php index 631d642509db3..cffc4184058ec 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php @@ -15,6 +15,7 @@ * Property accessor configuration annotation. * * @Annotation + * @Target({"PROPERTY"}) * * @author Luis Ramón López */ diff --git a/src/Symfony/Component/PropertyAccess/Exception/MappingException.php b/src/Symfony/Component/PropertyAccess/Exception/MappingException.php new file mode 100644 index 0000000000000..bbbd1c370ac16 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Exception/MappingException.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * MappingException. + * + * @author Kévin Dunglas + */ +class MappingException extends RuntimeException +{ +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php b/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php new file mode 100644 index 0000000000000..d893ed4db9b25 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php @@ -0,0 +1,177 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping; + +/** + * {@inheritdoc} + * + * @author Kévin Dunglas + */ +class AttributeMetadata implements AttributeMetadataInterface +{ + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getName()} instead. + */ + public $name; + + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getGetter()} instead. + */ + public $getter; + + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getSetter()} instead. + */ + public $setter; + + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getAdder()} instead. + */ + public $adder; + + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getRemover()} instead. + */ + public $remover; + + /** + * Constructs a metadata for the given attribute. + * + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getSetter() + { + return $this->setter; + } + + /** + * {@inheritdoc} + */ + public function setSetter($setter) + { + $this->setter = $setter; + } + + /** + * {@inheritdoc} + */ + public function getGetter() + { + return $this->getter; + } + + /** + * {@inheritdoc} + */ + public function setGetter($getter) + { + $this->getter = $getter; + } + + /** + * {@inheritdoc} + */ + public function getAdder() + { + return $this->adder; + } + + /** + * {@inheritdoc} + */ + public function setAdder($adder) + { + $this->adder = $adder; + } + + /** + * {@inheritdoc} + */ + public function getRemover() + { + return $this->remover; + } + + /** + * {@inheritdoc} + */ + public function setRemover($remover) + { + $this->remover = $remover; + } + + /** + * {@inheritdoc} + */ + public function merge(AttributeMetadataInterface $attributeMetadata) + { + // Overwrite only if not defined + if (null === $this->getter) { + $this->getter = $attributeMetadata->getGetter(); + } + if (null === $this->setter) { + $this->setter = $attributeMetadata->getSetter(); + } + if (null === $this->adder) { + $this->adder = $attributeMetadata->getAdder(); + } + if (null === $this->remover) { + $this->remover = $attributeMetadata->getRemover(); + } + } + + /** + * Returns the names of the properties that should be serialized. + * + * @return string[] + */ + public function __sleep() + { + return array('name', 'getter', 'setter', 'adder', 'remover'); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php new file mode 100644 index 0000000000000..d0aab2af2ff16 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping; + +/** + * Stores metadata needed for overriding attributes access methods. + * + * @internal + * + * @author Luis Ramón López + */ +interface AttributeMetadataInterface +{ + /** + * Gets the attribute name. + * + * @return string + */ + public function getName(); + + /** + * Gets the setter method name. + * + * @return string + */ + public function getSetter(); + + /** + * Sets the setter method name. + */ + public function setSetter($setter); + + /** + * Gets the getter method name. + * + * @return string + */ + public function getGetter(); + + /** + * Sets the getter method name. + */ + public function setGetter($getter); + + /** + * Gets the adder method name. + * + * @return string + */ + public function getAdder(); + + /** + * Sets the adder method name. + */ + public function setAdder($adder); + + /** + * Gets the remover method name. + * + * @return string + */ + public function getRemover(); + + /** + * Sets the remover method name. + */ + public function setRemover($remover); + + /** + * Merges an {@see AttributeMetadataInterface} with in the current one. + * + * @param AttributeMetadataInterface $attributeMetadata + */ + public function merge(AttributeMetadataInterface $attributeMetadata); +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php new file mode 100644 index 0000000000000..c309e9fdf2006 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php @@ -0,0 +1,116 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping; + +/** + * {@inheritdoc} + * + * @author Kévin Dunglas + */ +class ClassMetadata implements ClassMetadataInterface +{ + /** + * @var string + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getName()} instead. + */ + public $name; + + /** + * @var AttributeMetadataInterface[] + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getAttributesMetadata()} instead. + */ + public $attributesMetadata = array(); + + /** + * @var \ReflectionClass + */ + private $reflClass; + + /** + * Constructs a metadata for the given class. + * + * @param string $class + */ + public function __construct($class) + { + $this->name = $class; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata) + { + $this->attributesMetadata[$attributeMetadata->getName()] = $attributeMetadata; + } + + /** + * {@inheritdoc} + */ + public function getAttributesMetadata() + { + return $this->attributesMetadata; + } + + /** + * {@inheritdoc} + */ + public function merge(ClassMetadataInterface $classMetadata) + { + foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) { + if (isset($this->attributesMetadata[$attributeMetadata->getName()])) { + $this->attributesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata); + } else { + $this->addAttributeMetadata($attributeMetadata); + } + } + } + + /** + * {@inheritdoc} + */ + public function getReflectionClass() + { + if (!$this->reflClass) { + $this->reflClass = new \ReflectionClass($this->getName()); + } + + return $this->reflClass; + } + + /** + * Returns the names of the properties that should be serialized. + * + * @return string[] + */ + public function __sleep() + { + return array( + 'name', + 'attributesMetadata', + ); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php new file mode 100644 index 0000000000000..fcdfb325ea5c2 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.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\Mapping; + +/** + * Stores metadata needed for serializing and deserializing objects of specific class. + * + * Primarily, the metadata stores the set of attributes to serialize or deserialize. + * + * There may only exist one metadata for each attribute according to its name. + * + * @internal + * + * @author Kévin Dunglas + */ +interface ClassMetadataInterface +{ + /** + * Returns the name of the backing PHP class. + * + * @return string The name of the backing class. + */ + public function getName(); + + /** + * Adds an {@link AttributeMetadataInterface}. + * + * @param AttributeMetadataInterface $attributeMetadata + */ + public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata); + + /** + * Gets the list of {@link AttributeMetadataInterface}. + * + * @return AttributeMetadataInterface[] + */ + public function getAttributesMetadata(); + + /** + * Merges a {@link ClassMetadataInterface} in the current one. + * + * @param ClassMetadataInterface $classMetadata + */ + public function merge(ClassMetadataInterface $classMetadata); + + /** + * Returns a {@link \ReflectionClass} instance for this class. + * + * @return \ReflectionClass + */ + public function getReflectionClass(); +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php new file mode 100644 index 0000000000000..a9069f25f1a3a --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +use Psr\Cache\CacheItemPoolInterface; + +/** + * Caches metadata using a PSR-6 implementation. + * + * @author Kévin Dunglas + */ +class CacheClassMetadataFactory implements ClassMetadataFactoryInterface +{ + use ClassResolverTrait; + + /** + * @var ClassMetadataFactoryInterface + */ + private $decorated; + + /** + * @var CacheItemPoolInterface + */ + private $cacheItemPool; + + public function __construct(ClassMetadataFactoryInterface $decorated, CacheItemPoolInterface $cacheItemPool) + { + $this->decorated = $decorated; + $this->cacheItemPool = $cacheItemPool; + } + + /** + * {@inheritdoc} + */ + public function getMetadataFor($value) + { + $class = $this->getClass($value); + // Key cannot contain backslashes according to PSR-6 + $key = strtr($class, '\\', '_'); + + $item = $this->cacheItemPool->getItem($key); + if ($item->isHit()) { + return $item->get(); + } + + $metadata = $this->decorated->getMetadataFor($value); + $this->cacheItemPool->save($item->set($metadata)); + + return $metadata; + } + + /** + * {@inheritdoc} + */ + public function hasMetadataFor($value) + { + return $this->decorated->hasMetadataFor($value); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php new file mode 100644 index 0000000000000..9598f0c0eeb23 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php @@ -0,0 +1,109 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +use Doctrine\Common\Cache\Cache; +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; +use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface; + +/** + * Returns a {@link ClassMetadata}. + * + * @author Kévin Dunglas + */ +class ClassMetadataFactory implements ClassMetadataFactoryInterface +{ + use ClassResolverTrait; + + /** + * @var LoaderInterface + */ + private $loader; + + /** + * @var Cache + */ + private $cache; + + /** + * @var array + */ + private $loadedClasses; + + /** + * @param LoaderInterface $loader + * @param Cache|null $cache + */ + public function __construct(LoaderInterface $loader, Cache $cache = null) + { + $this->loader = $loader; + $this->cache = $cache; + + if (null !== $cache) { + @trigger_error(sprintf('Passing a Doctrine Cache instance as 2nd parameter of the "%s" constructor is deprecated. This parameter will be removed in Symfony 4.0. Use the "%s" class instead.', __CLASS__, CacheClassMetadataFactory::class), E_USER_DEPRECATED); + } + } + + /** + * {@inheritdoc} + */ + public function getMetadataFor($value) + { + $class = $this->getClass($value); + + if (isset($this->loadedClasses[$class])) { + return $this->loadedClasses[$class]; + } + + if ($this->cache && ($this->loadedClasses[$class] = $this->cache->fetch($class))) { + return $this->loadedClasses[$class]; + } + + $classMetadata = new ClassMetadata($class); + $this->loader->loadClassMetadata($classMetadata); + + $reflectionClass = $classMetadata->getReflectionClass(); + + // Include metadata from the parent class + if ($parent = $reflectionClass->getParentClass()) { + $classMetadata->merge($this->getMetadataFor($parent->name)); + } + + // Include metadata from all implemented interfaces + foreach ($reflectionClass->getInterfaces() as $interface) { + $classMetadata->merge($this->getMetadataFor($interface->name)); + } + + if ($this->cache) { + $this->cache->save($class, $classMetadata); + } + + return $this->loadedClasses[$class] = $classMetadata; + } + + /** + * {@inheritdoc} + */ + public function hasMetadataFor($value) + { + try { + $this->getClass($value); + + return true; + } catch (InvalidArgumentException $invalidArgumentException) { + // Return false in case of exception + } + + return false; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php new file mode 100644 index 0000000000000..86868f9607b60 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; + +/** + * Returns a {@see ClassMetadataInterface}. + * + * @author Kévin Dunglas + */ +interface ClassMetadataFactoryInterface +{ + /** + * If the method was called with the same class name (or an object of that + * class) before, the same metadata instance is returned. + * + * If the factory was configured with a cache, this method will first look + * for an existing metadata instance in the cache. If an existing instance + * is found, it will be returned without further ado. + * + * Otherwise, a new metadata instance is created. If the factory was + * configured with a loader, the metadata is passed to the + * {@link \Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface::loadClassMetadata()} method for further + * configuration. At last, the new object is returned. + * + * @param string|object $value + * + * @return ClassMetadataInterface + * + * @throws InvalidArgumentException + */ + public function getMetadataFor($value); + + /** + * Checks if class has metadata. + * + * @param mixed $value + * + * @return bool + */ + public function hasMetadataFor($value); +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php new file mode 100644 index 0000000000000..4b84caf986b05 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; + +/** + * Resolves a class name. + * + * @internal + * + * @author Kévin Dunglas + */ +trait ClassResolverTrait +{ + /** + * Gets a class name for a given class or instance. + * + * @param mixed $value + * + * @return string + * + * @throws InvalidArgumentException If the class does not exists + */ + private function getClass($value) + { + if (is_string($value)) { + if (!class_exists($value) && !interface_exists($value)) { + throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $value)); + } + + return ltrim($value, '\\'); + } + + if (!is_object($value)) { + throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value))); + } + + return get_class($value); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php new file mode 100644 index 0000000000000..cc05523c975b5 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Loader; + +use Doctrine\Common\Annotations\Reader; +use Symfony\Component\PropertyAccess\Annotation\PropertyAccessor; +use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; + +/** + * Annotation loader. + * + * @author Kévin Dunglas + * @author Luis Ramón López + */ +class AnnotationLoader implements LoaderInterface +{ + /** + * @var Reader + */ + private $reader; + + /** + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + /** + * {@inheritdoc} + */ + public function loadClassMetadata(ClassMetadataInterface $classMetadata) + { + $reflectionClass = $classMetadata->getReflectionClass(); + $className = $reflectionClass->name; + $loaded = false; + + $attributesMetadata = $classMetadata->getAttributesMetadata(); + + foreach ($reflectionClass->getProperties() as $property) { + if (!isset($attributesMetadata[$property->name])) { + $attributesMetadata[$property->name] = new AttributeMetadata($property->name); + $classMetadata->addAttributeMetadata($attributesMetadata[$property->name]); + } + + if ($property->getDeclaringClass()->name === $className) { + foreach ($this->reader->getPropertyAnnotations($property) as $annotation) { + if ($annotation instanceof PropertyAccessor) { + $attributesMetadata[$property->name]->setGetter($annotation->getGetter()); + $attributesMetadata[$property->name]->setSetter($annotation->getSetter()); + $attributesMetadata[$property->name]->setAdder($annotation->getAdder()); + $attributesMetadata[$property->name]->setRemover($annotation->getRemover()); + } + + $loaded = true; + } + } + } + + return $loaded; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/FileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/FileLoader.php new file mode 100644 index 0000000000000..d3dbef4e918a8 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/FileLoader.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Loader; + +use Symfony\Component\PropertyAccess\Exception\MappingException; + +/** + * Base class for all file based loaders. + * + * @author Kévin Dunglas + */ +abstract class FileLoader implements LoaderInterface +{ + /** + * @var string + */ + protected $file; + + /** + * Constructor. + * + * @param string $file The mapping file to load + * + * @throws MappingException if the mapping file does not exist or is not readable + */ + public function __construct($file) + { + if (!is_file($file)) { + throw new MappingException(sprintf('The mapping file %s does not exist', $file)); + } + + if (!is_readable($file)) { + throw new MappingException(sprintf('The mapping file %s is not readable', $file)); + } + + $this->file = $file; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php new file mode 100644 index 0000000000000..0595e1dc7596b --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php @@ -0,0 +1,66 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Loader; + +use Symfony\Component\PropertyAccess\Exception\MappingException; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; + +/** + * Calls multiple {@link LoaderInterface} instances in a chain. + * + * This class accepts multiple instances of LoaderInterface to be passed to the + * constructor. When {@link loadClassMetadata()} is called, the same method is called + * in all of these loaders, regardless of whether any of them was + * successful or not. + * + * @author Bernhard Schussek + * @author Kévin Dunglas + */ +class LoaderChain implements LoaderInterface +{ + /** + * @var LoaderInterface[] + */ + private $loaders; + + /** + * Accepts a list of LoaderInterface instances. + * + * @param LoaderInterface[] $loaders An array of LoaderInterface instances + * + * @throws MappingException If any of the loaders does not implement LoaderInterface + */ + public function __construct(array $loaders) + { + foreach ($loaders as $loader) { + if (!$loader instanceof LoaderInterface) { + throw new MappingException(sprintf('Class %s is expected to implement LoaderInterface', get_class($loader))); + } + } + + $this->loaders = $loaders; + } + + /** + * {@inheritdoc} + */ + public function loadClassMetadata(ClassMetadataInterface $metadata) + { + $success = false; + + foreach ($this->loaders as $loader) { + $success = $loader->loadClassMetadata($metadata) || $success; + } + + return $success; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php new file mode 100644 index 0000000000000..12c4ee09016d3 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Loader; + +use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; + +/** + * Loads {@link ClassMetadataInterface}. + * + * @author Kévin Dunglas + */ +interface LoaderInterface +{ + /** + * Load class metadata. + * + * @param ClassMetadataInterface $classMetadata A metadata + * + * @return bool + */ + public function loadClassMetadata(ClassMetadataInterface $classMetadata); +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php new file mode 100644 index 0000000000000..25b2ba13e2a94 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Loader; + +use Symfony\Component\Config\Util\XmlUtils; +use Symfony\Component\PropertyAccess\Exception\MappingException; +use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; + +/** + * Loads XML mapping files. + * + * @author Kévin Dunglas + */ +class XmlFileLoader extends FileLoader +{ + /** + * An array of {@class \SimpleXMLElement} instances. + * + * @var \SimpleXMLElement[]|null + */ + private $classes; + + /** + * {@inheritdoc} + */ + public function loadClassMetadata(ClassMetadataInterface $classMetadata) + { + if (null === $this->classes) { + $this->classes = array(); + $xml = $this->parseFile($this->file); + + foreach ($xml->class as $class) { + $this->classes[(string) $class['name']] = $class; + } + } + + $attributesMetadata = $classMetadata->getAttributesMetadata(); + + if (isset($this->classes[$classMetadata->getName()])) { + $xml = $this->classes[$classMetadata->getName()]; + + foreach ($xml->attribute as $attribute) { + $attributeName = (string) $attribute['name']; + + if (isset($attributesMetadata[$attributeName])) { + $attributeMetadata = $attributesMetadata[$attributeName]; + } else { + $attributeMetadata = new AttributeMetadata($attributeName); + $classMetadata->addAttributeMetadata($attributeMetadata); + } + + if (isset($attribute['getter'])) { + $attributeMetadata->setGetter($attribute['getter']); + } + + if (isset($attribute['setter'])) { + $attributeMetadata->setSetter($attribute['setter']); + } + + if (isset($attribute['adder'])) { + $attributeMetadata->setAdder($attribute['adder']); + } + + if (isset($attribute['remover'])) { + $attributeMetadata->setRemover($attribute['remover']); + } + } + + return true; + } + + return false; + } + + /** + * Parses a XML File. + * + * @param string $file Path of file + * + * @return \SimpleXMLElement + * + * @throws MappingException + */ + private function parseFile($file) + { + try { + $dom = XmlUtils::loadFile($file, __DIR__.'/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd'); + } catch (\Exception $e) { + throw new MappingException($e->getMessage(), $e->getCode(), $e); + } + + return simplexml_import_dom($dom); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php new file mode 100644 index 0000000000000..e65871bd2036d --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Loader; + +use Symfony\Component\PropertyAccess\Exception\MappingException; +use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\Yaml\Parser; + +/** + * YAML File Loader. + * + * @author Kévin Dunglas + * @author Luis Ramón López + */ +class YamlFileLoader extends FileLoader +{ + private $yamlParser; + + /** + * An array of YAML class descriptions. + * + * @var array + */ + private $classes = null; + + /** + * {@inheritdoc} + */ + public function loadClassMetadata(ClassMetadataInterface $classMetadata) + { + if (null === $this->classes) { + if (!stream_is_local($this->file)) { + throw new MappingException(sprintf('This is not a local file "%s".', $this->file)); + } + + if (null === $this->yamlParser) { + $this->yamlParser = new Parser(); + } + + $classes = $this->yamlParser->parse(file_get_contents($this->file)); + + if (empty($classes)) { + return false; + } + + // not an array + if (!is_array($classes)) { + throw new MappingException(sprintf('The file "%s" must contain a YAML array.', $this->file)); + } + + $this->classes = $classes; + } + + if (isset($this->classes[$classMetadata->getName()])) { + $yaml = $this->classes[$classMetadata->getName()]; + + if (isset($yaml['attributes']) && is_array($yaml['attributes'])) { + $attributesMetadata = $classMetadata->getAttributesMetadata(); + + foreach ($yaml['attributes'] as $attribute => $data) { + if (isset($attributesMetadata[$attribute])) { + $attributeMetadata = $attributesMetadata[$attribute]; + } else { + $attributeMetadata = new AttributeMetadata($attribute); + $classMetadata->addAttributeMetadata($attributeMetadata); + } + + if (isset($data['getter'])) { + if (!is_string($data['getter'])) { + throw new MappingException('The "getter" value must be a string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()); + } + + $attributeMetadata->setGetter($data['getter']); + } + + if (isset($data['setter'])) { + if (!is_string($data['setter'])) { + throw new MappingException('The "setter" value must be a string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()); + } + + $attributeMetadata->setSetter($data['setter']); + } + + if (isset($data['adder'])) { + if (!is_string($data['adder'])) { + throw new MappingException('The "adder" value must be a string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()); + } + + $attributeMetadata->setAdder($data['adder']); + } + + if (isset($data['remover'])) { + if (!is_string($data['remover'])) { + throw new MappingException('The "remover" value must be a string in "%s" for the attribute "%s" of the class "%s".', $this->file, $attribute, $classMetadata->getName()); + } + + $attributeMetadata->setRemover($data['remover']); + } + } + } + + return true; + } + + return false; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd b/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd new file mode 100644 index 0000000000000..842b845e2953a --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index e5a47f5a1f74c..d0d1d39924b02 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -23,6 +23,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; +use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; /** * Default implementation of {@link PropertyAccessorInterface}. @@ -148,9 +149,9 @@ class PropertyAccessor implements PropertyAccessorInterface private static $resultProto = array(self::VALUE => null); /** - * @var AnnotationReader + * @var ClassMetadataFactory */ - private $reader; + private $classMetadataFactory; /** * @var array @@ -164,13 +165,14 @@ class PropertyAccessor implements PropertyAccessorInterface * @param bool $magicCall * @param bool $throwExceptionOnInvalidIndex * @param CacheItemPoolInterface $cacheItemPool + * @param ClassMetadataFactory $classMetadataFactory */ - public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, AnnotationReader $reader = null) + public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, ClassMetadataFactory $classMetadataFactory = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value - $this->reader = $reader; + $this->classMetadataFactory = $classMetadataFactory; } /** @@ -552,17 +554,16 @@ private function getReadAccessInfo($class, $property) } } - $annotation = null; + $metadata = null; $access = array(); $reflClass = new \ReflectionClass($class); $hasProperty = $reflClass->hasProperty($property); $access[self::ACCESS_HAS_PROPERTY] = $hasProperty; - if ($hasProperty && $this->reader) { - $annotation = $this->reader->getPropertyAnnotation($reflClass->getProperty($property), - 'Symfony\Component\PropertyAccess\Annotation\PropertyAccessor'); - + if ($hasProperty && $this->classMetadataFactory) { + $metadata = $this->classMetadataFactory->getMetadataFor($object)->getAttributesMetadata(); + $metadata = isset($metadata[$property]) ? $metadata[$property] : null; } $camelProp = $this->camelize($property); @@ -571,9 +572,9 @@ private function getReadAccessInfo($class, $property) $isser = 'is'.$camelProp; $hasser = 'has'.$camelProp; - if ($annotation && $annotation->getGetter()) { + if ($metadata && $metadata->getGetter()) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; - $access[self::ACCESS_NAME] = $annotation->getGetter(); + $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; @@ -741,7 +742,7 @@ private function getWriteAccessInfo($class, $property, $value) } } - $annotation = null; + $metadata = null; $access = array(); $reflClass = new \ReflectionClass($class); @@ -751,19 +752,19 @@ private function getWriteAccessInfo($class, $property, $value) $transversable = is_array($value) || $value instanceof \Traversable; $done = false; - if ($hasProperty && $this->reader) { - $annotation = $this->reader->getPropertyAnnotation($reflClass->getProperty($property), - 'Symfony\Component\PropertyAccess\Annotation\PropertyAccessor'); + if ($hasProperty && $this->classMetadataFactory) { + $metadata = $this->classMetadataFactory->getMetadataFor($object)->getAttributesMetadata(); + $metadata = isset($metadata[$property]) ? $metadata[$property] : null; - if ($annotation) { - if ($transversable && $annotation->getAdder() && $annotation->getRemover()) { + if ($metadata) { + if ($transversable && $metadata->getAdder() && $metadata->getRemover()) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; - $access[self::ACCESS_ADDER] = $annotation->getAdder(); - $access[self::ACCESS_REMOVER] = $annotation->getRemover(); + $access[self::ACCESS_ADDER] = $metadata->getAdder(); + $access[self::ACCESS_REMOVER] = $metadata->getRemover(); $done = true; - } elseif ($annotation->getSetter()) { + } elseif ($metadata->getSetter()) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; - $access[self::ACCESS_NAME] = $annotation->getSetter(); + $access[self::ACCESS_NAME] = $metadata->getSetter(); $done = true; } } 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..f14546b58184a --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php @@ -0,0 +1,62 @@ + + * + * 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\PropertyAccessor; + +/** + * Fixtures for testing metadata. + */ +class Dummy +{ + /** + * @PropertyAccessor(getter="getter1", setter="setter1", adder="adder1", remover="remover1") + */ + protected $foo; + + /** + * @PropertyAccessor(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; + } +} 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..5c4bd79f6076b --- /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..ce544f5e1f581 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml @@ -0,0 +1,9 @@ +'Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy': + attributes: + foo: + getter: getter1 + setter: setter1 + adder: adder1 + remover: remover1 + bar: + getter: getter2 diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php new file mode 100644 index 0000000000000..15a08efcf6bd6 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.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\AttributeMetadata; + +/** + * @author Kévin Dunglas + */ +class AttributeMetadataTest extends \PHPUnit_Framework_TestCase +{ + public function testInterface() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface', $attributeMetadata); + } + + public function testGetName() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertEquals('name', $attributeMetadata->getName()); + } + + public function testGetter() + { + $attributeMetadata = new AttributeMetadata('name'); + $attributeMetadata->setGetter('one'); + + $this->assertEquals('one', $attributeMetadata->getGetter()); + } + + public function testSetter() + { + $attributeMetadata = new AttributeMetadata('name'); + $attributeMetadata->setSetter('one'); + + $this->assertEquals('one', $attributeMetadata->getSetter()); + } + + public function testAdder() + { + $attributeMetadata = new AttributeMetadata('name'); + $attributeMetadata->setAdder('one'); + + $this->assertEquals('one', $attributeMetadata->getAdder()); + } + + public function testRemover() + { + $attributeMetadata = new AttributeMetadata('name'); + $attributeMetadata->setRemover('one'); + + $this->assertEquals('one', $attributeMetadata->getRemover()); + } + + public function testMerge() + { + $attributeMetadata1 = new AttributeMetadata('a1'); + $attributeMetadata1->setGetter('a'); + $attributeMetadata1->setSetter('b'); + + $attributeMetadata2 = new AttributeMetadata('a2'); + $attributeMetadata2->setGetter('c'); + $attributeMetadata2->setAdder('d'); + $attributeMetadata2->setRemover('e'); + + $attributeMetadata1->merge($attributeMetadata2); + + $this->assertEquals('a', $attributeMetadata1->getGetter()); + $this->assertEquals('b', $attributeMetadata1->getSetter()); + $this->assertEquals('d', $attributeMetadata1->getAdder()); + $this->assertEquals('e', $attributeMetadata1->getRemover()); + } + + public function testSerialize() + { + $attributeMetadata = new AttributeMetadata('attribute'); + $attributeMetadata->setGetter('a'); + $attributeMetadata->setSetter('b'); + $attributeMetadata->setAdder('c'); + $attributeMetadata->setRemover('d'); + + $serialized = serialize($attributeMetadata); + $this->assertEquals($attributeMetadata, unserialize($serialized)); + } +} 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..62d11cc5d14c6 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php @@ -0,0 +1,59 @@ + + * + * 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\ClassMetadataInterface', $classMetadata); + } + + public function testAttributeMetadata() + { + $classMetadata = new ClassMetadata('c'); + + $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a1->method('getName')->willReturn('a1'); + + $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a2->method('getName')->willReturn('a2'); + + $classMetadata->addAttributeMetadata($a1); + $classMetadata->addAttributeMetadata($a2); + + $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getAttributesMetadata()); + } + + public function testSerialize() + { + $classMetadata = new ClassMetadata('a'); + + $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a1->method('getName')->willReturn('b1'); + + $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a2->method('getName')->willReturn('b2'); + + $classMetadata->addAttributeMetadata($a1); + $classMetadata->addAttributeMetadata($a2); + + $serialized = serialize($classMetadata); + $this->assertEquals($classMetadata, unserialize($serialized)); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php new file mode 100644 index 0000000000000..f491cf6e75504 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php @@ -0,0 +1,67 @@ + + * + * 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\Cache\Adapter\ArrayAdapter; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; +use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\PropertyAccess\Mapping\Factory\CacheClassMetadataFactory; +use Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy; + +/** + * @author Kévin Dunglas + */ +class CacheMetadataFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testGetMetadataFor() + { + $metadata = new ClassMetadata(Dummy::class); + + $decorated = $this->getMock(ClassMetadataFactoryInterface::class); + $decorated + ->expects($this->once()) + ->method('getMetadataFor') + ->will($this->returnValue($metadata)) + ; + + $factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter()); + + $this->assertEquals($metadata, $factory->getMetadataFor(Dummy::class)); + // The second call should retrieve the value from the cache + $this->assertEquals($metadata, $factory->getMetadataFor(Dummy::class)); + } + + public function testHasMetadataFor() + { + $decorated = $this->getMock(ClassMetadataFactoryInterface::class); + $decorated + ->expects($this->once()) + ->method('hasMetadataFor') + ->will($this->returnValue(true)) + ; + + $factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter()); + + $this->assertTrue($factory->hasMetadataFor(Dummy::class)); + } + + /** + * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException + */ + public function testInvalidClassThrowsException() + { + $decorated = $this->getMock(ClassMetadataFactoryInterface::class); + $factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter()); + + $factory->getMetadataFor('Not\Exist'); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php new file mode 100644 index 0000000000000..a8e22853f354f --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php @@ -0,0 +1,79 @@ + + * + * 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 Doctrine\Common\Annotations\AnnotationReader; +use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader; +use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderChain; +use Symfony\Component\PropertyAccess\Tests\Mapping\TestClassMetadataFactory; + +/** + * @author Kévin Dunglas + */ +class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase +{ + public function testInterface() + { + $classMetadata = new ClassMetadataFactory(new LoaderChain(array())); + $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactoryInterface', $classMetadata); + } + + public function testGetMetadataFor() + { + AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..'); + $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $classMetadata = $factory->getMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy'); + + $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata); + } + + public function testHasMetadataFor() + { + $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->assertTrue($factory->hasMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy')); + $this->assertFalse($factory->hasMetadataFor('Dunglas\Entity')); + } + + /** + * @group legacy + */ + public function testCacheExists() + { + $cache = $this->getMock('Doctrine\Common\Cache\Cache'); + $cache + ->expects($this->once()) + ->method('fetch') + ->will($this->returnValue('foo')) + ; + AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..'); + $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache); + $this->assertEquals('foo', $factory->getMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy')); + } + + /** + * @group legacy + */ + public function testCacheNotExists() + { + $cache = $this->getMock('Doctrine\Common\Cache\Cache'); + $cache->method('fetch')->will($this->returnValue(false)); + $cache->method('save'); + + AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..'); + $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache); + $metadata = $factory->getMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy'); + + $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $metadata); + } +} 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..9891fcfcef7fc --- /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'); + + $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/TestClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php new file mode 100644 index 0000000000000..d60337900323d --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php @@ -0,0 +1,59 @@ + + * + * 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\AttributeMetadata; +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 AttributeMetadata('foo'); + $foo->setGetter('getter1'); + $foo->setSetter('setter1'); + $foo->setAdder('adder1'); + $foo->setRemover('remover1'); + $expected->addAttributeMetadata($foo); + + $bar = new AttributeMetadata('bar'); + $bar->setGetter('getter2'); + $expected->addAttributeMetadata($bar); + + return $expected; + } + + public static function createXMLClassMetadata() + { + $expected = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy'); + + $foo = new AttributeMetadata('foo'); + $foo->setGetter('getter1'); + $foo->setSetter('setter1'); + $foo->setAdder('adder1'); + $foo->setRemover('remover1'); + $expected->addAttributeMetadata($foo); + + $bar = new AttributeMetadata('bar'); + $bar->setGetter('getter2'); + $expected->addAttributeMetadata($bar); + + return $expected; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index d3ea7f8da14f8..e279421b994a4 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -13,6 +13,8 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader; use Symfony\Component\PropertyAccess\PropertyAccessor; class PropertyAccessorCollectionTest_Car @@ -190,7 +192,7 @@ public function testSetValueCallsCustomAdderAndRemoverForCollections() $car = new PropertyAccessorCollectionTest_Car($axesBefore); AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new AnnotationReader()); + $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->propertyAccessor->setValue($car, 'customAxes', $axesMerged); diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index b3cc3ff21d017..3f6aac6537002 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -15,6 +15,8 @@ use Doctrine\Common\Annotations\AnnotationRegistry; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; +use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; +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; @@ -203,7 +205,7 @@ public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p public function testGetWithCustomGetter() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new AnnotationReader()); + $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->assertSame('webmozart', $this->propertyAccessor->getValue(new TestClass('webmozart'), 'customGetterSetter')); } @@ -310,7 +312,7 @@ public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p public function testSetValueWithCustomSetter() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new AnnotationReader()); + $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $custom = new TestClass('webmozart'); From 00a26eb53bf4087a4d192fe6c0ec5f7b57c08c20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Thu, 17 Mar 2016 00:56:07 +0100 Subject: [PATCH 03/18] Renamed Attribute to Property on most classes and methods --- .../FrameworkExtension.php | 10 +- .../Annotation/ConfigurationAnnotation.php | 31 ------ .../Annotation/PropertyAccessor.php | 76 ++++++--------- .../Exception/MappingException.php | 2 +- .../Mapping/AttributeMetadataInterface.php | 84 ---------------- .../PropertyAccess/Mapping/ClassMetadata.php | 50 ++++++---- .../Mapping/ClassMetadataInterface.php | 61 ------------ .../Factory/ClassMetadataFactoryInterface.php | 4 +- .../Mapping/Loader/AnnotationLoader.php | 22 ++--- .../Mapping/Loader/LoaderChain.php | 4 +- .../Mapping/Loader/LoaderInterface.php | 8 +- .../Mapping/Loader/XmlFileLoader.php | 12 +-- .../Mapping/Loader/YamlFileLoader.php | 12 +-- ...ibuteMetadata.php => PropertyMetadata.php} | 51 ++++++---- .../PropertyAccess/PropertyAccessor.php | 19 ++-- .../Tests/Mapping/AttributeMetadataTest.php | 95 ------------------- .../Tests/Mapping/ClassMetadataTest.php | 22 +++-- .../Tests/Mapping/PropertyMetadataTest.php | 95 +++++++++++++++++++ .../Mapping/TestClassMetadataFactory.php | 18 ++-- 19 files changed, 252 insertions(+), 424 deletions(-) delete mode 100644 src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php rename src/Symfony/Component/PropertyAccess/Mapping/{AttributeMetadata.php => PropertyMetadata.php} (74%) delete mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/PropertyMetadataTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 9c28ce1d27648..e505360ec7325 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -940,7 +940,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $serializerLoaders = array(); if (isset($config['enable_annotations']) && $config['enable_annotations']) { $annotationLoader = new Definition( - 'Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader', + \Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader::class, array(new Reference('annotation_reader')) ); $annotationLoader->setPublic(false); @@ -954,7 +954,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $dirname = dirname($reflection->getFileName()); if (is_file($file = $dirname.'/Resources/config/property_access.xml')) { - $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader', array(realpath($file))); + $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader::class, array(realpath($file))); $definition->setPublic(false); $serializerLoaders[] = $definition; @@ -962,7 +962,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui } if (is_file($file = $dirname.'/Resources/config/property_access.yml')) { - $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader', array(realpath($file))); + $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader::class, array(realpath($file))); $definition->setPublic(false); $serializerLoaders[] = $definition; @@ -971,13 +971,13 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui if (is_dir($dir = $dirname.'/Resources/config/property_access')) { foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader', array($file->getRealpath())); + $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader::class, array($file->getRealpath())); $definition->setPublic(false); $serializerLoaders[] = $definition; } foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { - $definition = new Definition('Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader', array($file->getRealpath())); + $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader::class, array($file->getRealpath())); $definition->setPublic(false); $serializerLoaders[] = $definition; diff --git a/src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php b/src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php deleted file mode 100644 index 30756c004699a..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Annotation/ConfigurationAnnotation.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Annotation; - -/** - * Base configuration annotation. - * - * @author Johannes M. Schmitt - */ -abstract class ConfigurationAnnotation -{ - public function __construct(array $values) - { - foreach ($values as $k => $v) { - if (!method_exists($this, $name = 'set'.$k)) { - throw new \RuntimeException(sprintf('Unknown key "%s" for annotation "@%s".', $k, get_class($this))); - } - - $this->$name($v); - } - } -} diff --git a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php index cffc4184058ec..58c6db174ee77 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php @@ -19,53 +19,33 @@ * * @author Luis Ramón López */ -class PropertyAccessor extends ConfigurationAnnotation +class PropertyAccessor { - protected $setter; - - protected $getter; - - protected $adder; - - protected $remover; - - public function getSetter() - { - return $this->setter; - } - - public function setSetter($setter) - { - $this->setter = $setter; - } - - public function getGetter() - { - return $this->getter; - } - - public function setGetter($getter) - { - $this->getter = $getter; - } - - public function getAdder() - { - return $this->adder; - } - - public function setAdder($adder) - { - $this->adder = $adder; - } - - public function getRemover() - { - return $this->remover; - } - - public function setRemover($remover) - { - $this->remover = $remover; - } + /** + * Custom setter method for the property + * + * @var string $setter + */ + public $setter; + + /** + * Custom getter method for the property + * + * @var string $setter + */ + public $getter; + + /** + * Custom adder method for the property + * + * @var string $setter + */ + public $adder; + + /** + * Custom remover method for the property + * + * @var string $setter + */ + public $remover; } diff --git a/src/Symfony/Component/PropertyAccess/Exception/MappingException.php b/src/Symfony/Component/PropertyAccess/Exception/MappingException.php index bbbd1c370ac16..d63d5a8364144 100644 --- a/src/Symfony/Component/PropertyAccess/Exception/MappingException.php +++ b/src/Symfony/Component/PropertyAccess/Exception/MappingException.php @@ -14,7 +14,7 @@ /** * MappingException. * - * @author Kévin Dunglas + * @author Luis Ramón López */ class MappingException extends RuntimeException { diff --git a/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php deleted file mode 100644 index d0aab2af2ff16..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadataInterface.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping; - -/** - * Stores metadata needed for overriding attributes access methods. - * - * @internal - * - * @author Luis Ramón López - */ -interface AttributeMetadataInterface -{ - /** - * Gets the attribute name. - * - * @return string - */ - public function getName(); - - /** - * Gets the setter method name. - * - * @return string - */ - public function getSetter(); - - /** - * Sets the setter method name. - */ - public function setSetter($setter); - - /** - * Gets the getter method name. - * - * @return string - */ - public function getGetter(); - - /** - * Sets the getter method name. - */ - public function setGetter($getter); - - /** - * Gets the adder method name. - * - * @return string - */ - public function getAdder(); - - /** - * Sets the adder method name. - */ - public function setAdder($adder); - - /** - * Gets the remover method name. - * - * @return string - */ - public function getRemover(); - - /** - * Sets the remover method name. - */ - public function setRemover($remover); - - /** - * Merges an {@see AttributeMetadataInterface} with in the current one. - * - * @param AttributeMetadataInterface $attributeMetadata - */ - public function merge(AttributeMetadataInterface $attributeMetadata); -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php index c309e9fdf2006..95c88c3469adf 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php @@ -14,9 +14,9 @@ /** * {@inheritdoc} * - * @author Kévin Dunglas + * @author Luis Ramón López */ -class ClassMetadata implements ClassMetadataInterface +class ClassMetadata { /** * @var string @@ -28,13 +28,13 @@ class ClassMetadata implements ClassMetadataInterface public $name; /** - * @var AttributeMetadataInterface[] + * @var PropertyMetadata[] * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use - * {@link getAttributesMetadata()} instead. + * {@link getPropertiesMetadata()} instead. */ - public $attributesMetadata = array(); + public $propertiesMetadata = array(); /** * @var \ReflectionClass @@ -52,7 +52,9 @@ public function __construct($class) } /** - * {@inheritdoc} + * Returns the name of the backing PHP class. + * + * @return string The name of the backing class. */ public function getName() { @@ -60,37 +62,45 @@ public function getName() } /** - * {@inheritdoc} + * Adds an {@link AttributeMetadataInterface}. + * + * @param PropertyMetadata $propertyMetadata */ - public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata) + public function addPropertyMetadata(PropertyMetadata $propertyMetadata) { - $this->attributesMetadata[$attributeMetadata->getName()] = $attributeMetadata; + $this->propertiesMetadata[$propertyMetadata->getName()] = $propertyMetadata; } /** - * {@inheritdoc} + * Gets the list of {@link PropertyMetadata}. + * + * @return PropertyMetadata[] */ - public function getAttributesMetadata() + public function getPropertiesMetadata() { - return $this->attributesMetadata; + return $this->propertiesMetadata; } /** - * {@inheritdoc} + * Merges a {@link ClassMetadata} into the current one. + * + * @param ClassMetadata $classMetadata */ - public function merge(ClassMetadataInterface $classMetadata) + public function merge(ClassMetadata $classMetadata) { - foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) { - if (isset($this->attributesMetadata[$attributeMetadata->getName()])) { - $this->attributesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata); + foreach ($classMetadata->getPropertiesMetadata() as $attributeMetadata) { + if (isset($this->propertiesMetadata[$attributeMetadata->getName()])) { + $this->propertiesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata); } else { - $this->addAttributeMetadata($attributeMetadata); + $this->addPropertyMetadata($attributeMetadata); } } } /** - * {@inheritdoc} + * Returns a {@link \ReflectionClass} instance for this class. + * + * @return \ReflectionClass */ public function getReflectionClass() { @@ -110,7 +120,7 @@ public function __sleep() { return array( 'name', - 'attributesMetadata', + 'propertiesMetadata', ); } } diff --git a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php deleted file mode 100644 index fcdfb325ea5c2..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadataInterface.php +++ /dev/null @@ -1,61 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping; - -/** - * Stores metadata needed for serializing and deserializing objects of specific class. - * - * Primarily, the metadata stores the set of attributes to serialize or deserialize. - * - * There may only exist one metadata for each attribute according to its name. - * - * @internal - * - * @author Kévin Dunglas - */ -interface ClassMetadataInterface -{ - /** - * Returns the name of the backing PHP class. - * - * @return string The name of the backing class. - */ - public function getName(); - - /** - * Adds an {@link AttributeMetadataInterface}. - * - * @param AttributeMetadataInterface $attributeMetadata - */ - public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata); - - /** - * Gets the list of {@link AttributeMetadataInterface}. - * - * @return AttributeMetadataInterface[] - */ - public function getAttributesMetadata(); - - /** - * Merges a {@link ClassMetadataInterface} in the current one. - * - * @param ClassMetadataInterface $classMetadata - */ - public function merge(ClassMetadataInterface $classMetadata); - - /** - * Returns a {@link \ReflectionClass} instance for this class. - * - * @return \ReflectionClass - */ - public function getReflectionClass(); -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php index 86868f9607b60..00b0af882356a 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php @@ -12,7 +12,7 @@ namespace Symfony\Component\PropertyAccess\Mapping\Factory; use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; /** * Returns a {@see ClassMetadataInterface}. @@ -36,7 +36,7 @@ interface ClassMetadataFactoryInterface * * @param string|object $value * - * @return ClassMetadataInterface + * @return ClassMetadata * * @throws InvalidArgumentException */ diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index cc05523c975b5..6170f50694b03 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -13,8 +13,8 @@ use Doctrine\Common\Annotations\Reader; use Symfony\Component\PropertyAccess\Annotation\PropertyAccessor; -use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; /** * Annotation loader. @@ -40,27 +40,27 @@ public function __construct(Reader $reader) /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadataInterface $classMetadata) + public function loadClassMetadata(ClassMetadata $classMetadata) { $reflectionClass = $classMetadata->getReflectionClass(); $className = $reflectionClass->name; $loaded = false; - $attributesMetadata = $classMetadata->getAttributesMetadata(); + $propertiesMetadata = $classMetadata->getPropertiesMetadata(); foreach ($reflectionClass->getProperties() as $property) { - if (!isset($attributesMetadata[$property->name])) { - $attributesMetadata[$property->name] = new AttributeMetadata($property->name); - $classMetadata->addAttributeMetadata($attributesMetadata[$property->name]); + if (!isset($propertiesMetadata[$property->name])) { + $propertiesMetadata[$property->name] = new PropertyMetadata($property->name); + $classMetadata->addPropertyMetadata($propertiesMetadata[$property->name]); } if ($property->getDeclaringClass()->name === $className) { foreach ($this->reader->getPropertyAnnotations($property) as $annotation) { if ($annotation instanceof PropertyAccessor) { - $attributesMetadata[$property->name]->setGetter($annotation->getGetter()); - $attributesMetadata[$property->name]->setSetter($annotation->getSetter()); - $attributesMetadata[$property->name]->setAdder($annotation->getAdder()); - $attributesMetadata[$property->name]->setRemover($annotation->getRemover()); + $propertiesMetadata[$property->name]->setGetter($annotation->getter); + $propertiesMetadata[$property->name]->setSetter($annotation->setter); + $propertiesMetadata[$property->name]->setAdder($annotation->adder); + $propertiesMetadata[$property->name]->setRemover($annotation->remover); } $loaded = true; diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php index 0595e1dc7596b..b9707bbb98547 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php @@ -12,7 +12,7 @@ namespace Symfony\Component\PropertyAccess\Mapping\Loader; use Symfony\Component\PropertyAccess\Exception\MappingException; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; /** * Calls multiple {@link LoaderInterface} instances in a chain. @@ -53,7 +53,7 @@ public function __construct(array $loaders) /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadataInterface $metadata) + public function loadClassMetadata(ClassMetadata $metadata) { $success = false; diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php index 12c4ee09016d3..e137f9c66028e 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderInterface.php @@ -11,21 +11,21 @@ namespace Symfony\Component\PropertyAccess\Mapping\Loader; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; /** * Loads {@link ClassMetadataInterface}. * - * @author Kévin Dunglas + * @author Luis Ramón López */ interface LoaderInterface { /** * Load class metadata. * - * @param ClassMetadataInterface $classMetadata A metadata + * @param ClassMetadata $classMetadata A metadata * * @return bool */ - public function loadClassMetadata(ClassMetadataInterface $classMetadata); + public function loadClassMetadata(ClassMetadata $classMetadata); } diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php index 25b2ba13e2a94..9703a4ff69eb1 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php @@ -13,8 +13,8 @@ use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\PropertyAccess\Exception\MappingException; -use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; /** * Loads XML mapping files. @@ -33,7 +33,7 @@ class XmlFileLoader extends FileLoader /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadataInterface $classMetadata) + public function loadClassMetadata(ClassMetadata $classMetadata) { if (null === $this->classes) { $this->classes = array(); @@ -44,7 +44,7 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) } } - $attributesMetadata = $classMetadata->getAttributesMetadata(); + $attributesMetadata = $classMetadata->getPropertiesMetadata(); if (isset($this->classes[$classMetadata->getName()])) { $xml = $this->classes[$classMetadata->getName()]; @@ -55,8 +55,8 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) if (isset($attributesMetadata[$attributeName])) { $attributeMetadata = $attributesMetadata[$attributeName]; } else { - $attributeMetadata = new AttributeMetadata($attributeName); - $classMetadata->addAttributeMetadata($attributeMetadata); + $attributeMetadata = new PropertyMetadata($attributeName); + $classMetadata->addPropertyMetadata($attributeMetadata); } if (isset($attribute['getter'])) { diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php index e65871bd2036d..ed02cd4c160c2 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php @@ -12,8 +12,8 @@ namespace Symfony\Component\PropertyAccess\Mapping\Loader; use Symfony\Component\PropertyAccess\Exception\MappingException; -use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface; +use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; use Symfony\Component\Yaml\Parser; /** @@ -36,7 +36,7 @@ class YamlFileLoader extends FileLoader /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadataInterface $classMetadata) + public function loadClassMetadata(ClassMetadata $classMetadata) { if (null === $this->classes) { if (!stream_is_local($this->file)) { @@ -65,14 +65,14 @@ public function loadClassMetadata(ClassMetadataInterface $classMetadata) $yaml = $this->classes[$classMetadata->getName()]; if (isset($yaml['attributes']) && is_array($yaml['attributes'])) { - $attributesMetadata = $classMetadata->getAttributesMetadata(); + $attributesMetadata = $classMetadata->getPropertiesMetadata(); foreach ($yaml['attributes'] as $attribute => $data) { if (isset($attributesMetadata[$attribute])) { $attributeMetadata = $attributesMetadata[$attribute]; } else { - $attributeMetadata = new AttributeMetadata($attribute); - $classMetadata->addAttributeMetadata($attributeMetadata); + $attributeMetadata = new PropertyMetadata($attribute); + $classMetadata->addPropertyMetadata($attributeMetadata); } if (isset($data['getter'])) { diff --git a/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php b/src/Symfony/Component/PropertyAccess/Mapping/PropertyMetadata.php similarity index 74% rename from src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php rename to src/Symfony/Component/PropertyAccess/Mapping/PropertyMetadata.php index d893ed4db9b25..ed613beadb0c4 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/AttributeMetadata.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/PropertyMetadata.php @@ -12,11 +12,11 @@ namespace Symfony\Component\PropertyAccess\Mapping; /** - * {@inheritdoc} + * Stores metadata needed for overriding properties access methods. * - * @author Kévin Dunglas + * @author Luis Ramón López */ -class AttributeMetadata implements AttributeMetadataInterface +class PropertyMetadata { /** * @var string @@ -68,13 +68,15 @@ class AttributeMetadata implements AttributeMetadataInterface * * @param string $name */ - public function __construct($name) + public function __construct($name = null) { $this->name = $name; } /** - * {@inheritdoc} + * Gets the attribute name. + * + * @return string */ public function getName() { @@ -82,7 +84,9 @@ public function getName() } /** - * {@inheritdoc} + * Gets the setter method name. + * + * @return string */ public function getSetter() { @@ -90,15 +94,16 @@ public function getSetter() } /** - * {@inheritdoc} + * Sets the setter method name. */ public function setSetter($setter) { $this->setter = $setter; } - /** - * {@inheritdoc} + * Gets the getter method name. + * + * @return string */ public function getGetter() { @@ -106,7 +111,7 @@ public function getGetter() } /** - * {@inheritdoc} + * Sets the getter method name. */ public function setGetter($getter) { @@ -114,7 +119,9 @@ public function setGetter($getter) } /** - * {@inheritdoc} + * Gets the adder method name. + * + * @return string */ public function getAdder() { @@ -122,7 +129,7 @@ public function getAdder() } /** - * {@inheritdoc} + * Sets the adder method name. */ public function setAdder($adder) { @@ -130,7 +137,9 @@ public function setAdder($adder) } /** - * {@inheritdoc} + * Gets the remover method name. + * + * @return string */ public function getRemover() { @@ -138,7 +147,7 @@ public function getRemover() } /** - * {@inheritdoc} + * Sets the remover method name. */ public function setRemover($remover) { @@ -146,22 +155,24 @@ public function setRemover($remover) } /** - * {@inheritdoc} + * Merges another PropertyMetadata with the current one. + * + * @param PropertyMetadata $propertyMetadata */ - public function merge(AttributeMetadataInterface $attributeMetadata) + public function merge(PropertyMetadata $propertyMetadata) { // Overwrite only if not defined if (null === $this->getter) { - $this->getter = $attributeMetadata->getGetter(); + $this->getter = $propertyMetadata->getGetter(); } if (null === $this->setter) { - $this->setter = $attributeMetadata->getSetter(); + $this->setter = $propertyMetadata->getSetter(); } if (null === $this->adder) { - $this->adder = $attributeMetadata->getAdder(); + $this->adder = $propertyMetadata->getAdder(); } if (null === $this->remover) { - $this->remover = $attributeMetadata->getRemover(); + $this->remover = $propertyMetadata->getRemover(); } } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index d0d1d39924b02..8902f5d7d8cf6 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -23,7 +23,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; -use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactoryInterface; /** * Default implementation of {@link PropertyAccessorInterface}. @@ -144,12 +144,13 @@ 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 ClassMetadataFactory + * @var ClassMetadataFactoryInterface */ private $classMetadataFactory; @@ -162,12 +163,12 @@ 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 ClassMetadataFactory $classMetadataFactory + * @param bool $magicCall + * @param bool $throwExceptionOnInvalidIndex + * @param CacheItemPoolInterface $cacheItemPool + * @param ClassMetadataFactoryInterface $classMetadataFactory */ - public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, ClassMetadataFactory $classMetadataFactory = null) + public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, ClassMetadataFactoryInterface $classMetadataFactory = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; @@ -562,7 +563,7 @@ private function getReadAccessInfo($class, $property) $access[self::ACCESS_HAS_PROPERTY] = $hasProperty; if ($hasProperty && $this->classMetadataFactory) { - $metadata = $this->classMetadataFactory->getMetadataFor($object)->getAttributesMetadata(); + $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertiesMetadata(); $metadata = isset($metadata[$property]) ? $metadata[$property] : null; } @@ -753,7 +754,7 @@ private function getWriteAccessInfo($class, $property, $value) $done = false; if ($hasProperty && $this->classMetadataFactory) { - $metadata = $this->classMetadataFactory->getMetadataFor($object)->getAttributesMetadata(); + $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertiesMetadata(); $metadata = isset($metadata[$property]) ? $metadata[$property] : null; if ($metadata) { diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php deleted file mode 100644 index 15a08efcf6bd6..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/AttributeMetadataTest.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * 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\AttributeMetadata; - -/** - * @author Kévin Dunglas - */ -class AttributeMetadataTest extends \PHPUnit_Framework_TestCase -{ - public function testInterface() - { - $attributeMetadata = new AttributeMetadata('name'); - $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface', $attributeMetadata); - } - - public function testGetName() - { - $attributeMetadata = new AttributeMetadata('name'); - $this->assertEquals('name', $attributeMetadata->getName()); - } - - public function testGetter() - { - $attributeMetadata = new AttributeMetadata('name'); - $attributeMetadata->setGetter('one'); - - $this->assertEquals('one', $attributeMetadata->getGetter()); - } - - public function testSetter() - { - $attributeMetadata = new AttributeMetadata('name'); - $attributeMetadata->setSetter('one'); - - $this->assertEquals('one', $attributeMetadata->getSetter()); - } - - public function testAdder() - { - $attributeMetadata = new AttributeMetadata('name'); - $attributeMetadata->setAdder('one'); - - $this->assertEquals('one', $attributeMetadata->getAdder()); - } - - public function testRemover() - { - $attributeMetadata = new AttributeMetadata('name'); - $attributeMetadata->setRemover('one'); - - $this->assertEquals('one', $attributeMetadata->getRemover()); - } - - public function testMerge() - { - $attributeMetadata1 = new AttributeMetadata('a1'); - $attributeMetadata1->setGetter('a'); - $attributeMetadata1->setSetter('b'); - - $attributeMetadata2 = new AttributeMetadata('a2'); - $attributeMetadata2->setGetter('c'); - $attributeMetadata2->setAdder('d'); - $attributeMetadata2->setRemover('e'); - - $attributeMetadata1->merge($attributeMetadata2); - - $this->assertEquals('a', $attributeMetadata1->getGetter()); - $this->assertEquals('b', $attributeMetadata1->getSetter()); - $this->assertEquals('d', $attributeMetadata1->getAdder()); - $this->assertEquals('e', $attributeMetadata1->getRemover()); - } - - public function testSerialize() - { - $attributeMetadata = new AttributeMetadata('attribute'); - $attributeMetadata->setGetter('a'); - $attributeMetadata->setSetter('b'); - $attributeMetadata->setAdder('c'); - $attributeMetadata->setRemover('d'); - - $serialized = serialize($attributeMetadata); - $this->assertEquals($attributeMetadata, unserialize($serialized)); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php index 62d11cc5d14c6..fd3ecaa0dcee0 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php @@ -21,37 +21,39 @@ class ClassMetadataTest extends \PHPUnit_Framework_TestCase public function testInterface() { $classMetadata = new ClassMetadata('name'); - $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\ClassMetadataInterface', $classMetadata); + $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\ClassMetadata', $classMetadata); } public function testAttributeMetadata() { $classMetadata = new ClassMetadata('c'); - $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata'); $a1->method('getName')->willReturn('a1'); - $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata'); $a2->method('getName')->willReturn('a2'); - $classMetadata->addAttributeMetadata($a1); - $classMetadata->addAttributeMetadata($a2); + $classMetadata->addPropertyMetadata($a1); + $classMetadata->addPropertyMetadata($a2); - $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getAttributesMetadata()); + $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getPropertiesMetadata()); } public function testSerialize() { $classMetadata = new ClassMetadata('a'); - $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a1 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata'); $a1->method('getName')->willReturn('b1'); + $a1->method('__sleep')->willReturn([]); - $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\AttributeMetadataInterface'); + $a2 = $this->getMock('Symfony\Component\PropertyAccess\Mapping\PropertyMetadata'); $a2->method('getName')->willReturn('b2'); + $a2->method('__sleep')->willReturn([]); - $classMetadata->addAttributeMetadata($a1); - $classMetadata->addAttributeMetadata($a2); + $classMetadata->addPropertyMetadata($a1); + $classMetadata->addPropertyMetadata($a2); $serialized = serialize($classMetadata); $this->assertEquals($classMetadata, unserialize($serialized)); 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 index d60337900323d..1adc35fb283cb 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php @@ -11,7 +11,7 @@ namespace Symfony\Component\PropertyAccess\Tests\Mapping; -use Symfony\Component\PropertyAccess\Mapping\AttributeMetadata; +use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; /** @@ -25,16 +25,16 @@ public static function createClassMetadata() $expected->getReflectionClass(); - $foo = new AttributeMetadata('foo'); + $foo = new PropertyMetadata('foo'); $foo->setGetter('getter1'); $foo->setSetter('setter1'); $foo->setAdder('adder1'); $foo->setRemover('remover1'); - $expected->addAttributeMetadata($foo); + $expected->addPropertyMetadata($foo); - $bar = new AttributeMetadata('bar'); + $bar = new PropertyMetadata('bar'); $bar->setGetter('getter2'); - $expected->addAttributeMetadata($bar); + $expected->addPropertyMetadata($bar); return $expected; } @@ -43,16 +43,16 @@ public static function createXMLClassMetadata() { $expected = new ClassMetadata('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy'); - $foo = new AttributeMetadata('foo'); + $foo = new PropertyMetadata('foo'); $foo->setGetter('getter1'); $foo->setSetter('setter1'); $foo->setAdder('adder1'); $foo->setRemover('remover1'); - $expected->addAttributeMetadata($foo); + $expected->addPropertyMetadata($foo); - $bar = new AttributeMetadata('bar'); + $bar = new PropertyMetadata('bar'); $bar->setGetter('getter2'); - $expected->addAttributeMetadata($bar); + $expected->addPropertyMetadata($bar); return $expected; } From e4aaac903a7bea8b30b83c509d3ddca76b9a4d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Thu, 17 Mar 2016 01:04:10 +0100 Subject: [PATCH 04/18] Annotation renamed to @Property --- .../{PropertyAccessor.php => Property.php} | 18 +++++++++--------- .../Mapping/Loader/AnnotationLoader.php | 4 ++-- .../PropertyAccess/Tests/Fixtures/Dummy.php | 6 +++--- .../Tests/Fixtures/TestClass.php | 6 ++---- .../Tests/PropertyAccessorCollectionTest.php | 2 +- 5 files changed, 17 insertions(+), 19 deletions(-) rename src/Symfony/Component/PropertyAccess/Annotation/{PropertyAccessor.php => Property.php} (66%) diff --git a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/Annotation/Property.php similarity index 66% rename from src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php rename to src/Symfony/Component/PropertyAccess/Annotation/Property.php index 58c6db174ee77..a59031cfa1ed5 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/Property.php @@ -19,33 +19,33 @@ * * @author Luis Ramón López */ -class PropertyAccessor +class Property { /** - * Custom setter method for the property + * Custom setter method for the property. * - * @var string $setter + * @var string */ public $setter; /** - * Custom getter method for the property + * Custom getter method for the property. * - * @var string $setter + * @var string */ public $getter; /** - * Custom adder method for the property + * Custom adder method for the property. * - * @var string $setter + * @var string */ public $adder; /** - * Custom remover method for the property + * Custom remover method for the property. * - * @var string $setter + * @var string */ public $remover; } diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index 6170f50694b03..e226b2e646982 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -12,7 +12,7 @@ namespace Symfony\Component\PropertyAccess\Mapping\Loader; use Doctrine\Common\Annotations\Reader; -use Symfony\Component\PropertyAccess\Annotation\PropertyAccessor; +use Symfony\Component\PropertyAccess\Annotation\Property; use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; @@ -56,7 +56,7 @@ public function loadClassMetadata(ClassMetadata $classMetadata) if ($property->getDeclaringClass()->name === $className) { foreach ($this->reader->getPropertyAnnotations($property) as $annotation) { - if ($annotation instanceof PropertyAccessor) { + if ($annotation instanceof Property) { $propertiesMetadata[$property->name]->setGetter($annotation->getter); $propertiesMetadata[$property->name]->setSetter($annotation->setter); $propertiesMetadata[$property->name]->setAdder($annotation->adder); diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php index f14546b58184a..f2755b503133c 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php @@ -11,7 +11,7 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; -use Symfony\Component\PropertyAccess\Annotation\PropertyAccessor; +use Symfony\Component\PropertyAccess\Annotation\Property; /** * Fixtures for testing metadata. @@ -19,12 +19,12 @@ class Dummy { /** - * @PropertyAccessor(getter="getter1", setter="setter1", adder="adder1", remover="remover1") + * @Property(getter="getter1", setter="setter1", adder="adder1", remover="remover1") */ protected $foo; /** - * @PropertyAccessor(getter="getter2") + * @Property(getter="getter2") */ protected $bar; diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php index b31bc4790e2e5..444819dff8401 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php @@ -11,14 +11,12 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; -use Doctrine\ORM\Mapping\Column; -use Symfony\Component\PropertyAccess\Annotation\PropertyAccessor; +use Symfony\Component\PropertyAccess\Annotation\Property; class TestClass { public $publicProperty; protected $protectedProperty; - private $privateProperty; private $publicAccessor; private $publicMethodAccessor; @@ -32,7 +30,7 @@ class TestClass private $date; /** - * @PropertyAccessor(getter="customGetterTest", setter="customSetterTest") + * @Property(getter="customGetterTest", setter="customSetterTest") */ private $customGetterSetter; diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index e279421b994a4..29287b2453cdd 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -22,7 +22,7 @@ class PropertyAccessorCollectionTest_Car private $axes; /** - * @Symfony\Component\PropertyAccess\Annotation\PropertyAccessor(adder="addAxisTest", remover="removeAxisTest") + * @Symfony\Component\PropertyAccess\Annotation\Property(adder="addAxisTest", remover="removeAxisTest") */ private $customAxes; From e46df9834db8811660ba22cac18bffe9929e1c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Mon, 21 Mar 2016 16:03:30 +0100 Subject: [PATCH 05/18] Added method annotation for virtual properties --- .../PropertyAccess/Annotation/Adder.php | 30 +++++++++++++ .../PropertyAccess/Annotation/Getter.php | 30 +++++++++++++ .../PropertyAccess/Annotation/Remover.php | 30 +++++++++++++ .../PropertyAccess/Annotation/Setter.php | 30 +++++++++++++ .../Mapping/Loader/AnnotationLoader.php | 42 +++++++++++++++++++ .../PropertyAccess/PropertyAccessor.php | 4 +- .../Tests/Fixtures/TestClass.php | 34 ++++++++++++++- .../Tests/PropertyAccessorCollectionTest.php | 37 ++++++++++++++++ .../Tests/PropertyAccessorTest.php | 19 +++++++++ 9 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Annotation/Adder.php create mode 100644 src/Symfony/Component/PropertyAccess/Annotation/Getter.php create mode 100644 src/Symfony/Component/PropertyAccess/Annotation/Remover.php create mode 100644 src/Symfony/Component/PropertyAccess/Annotation/Setter.php diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Adder.php b/src/Symfony/Component/PropertyAccess/Annotation/Adder.php new file mode 100644 index 0000000000000..186af7e5ac45f --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Annotation/Adder.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Annotation; + +/** + * Property accessor adder configuration annotation. + * + * @Annotation + * @Target({"METHOD"}) + * + * @author Luis Ramón López + */ +class Adder +{ + /** + * Associates this method to the setter of this property. + * + * @var string + */ + public $property; +} diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Getter.php b/src/Symfony/Component/PropertyAccess/Annotation/Getter.php new file mode 100644 index 0000000000000..0b054a1306461 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Annotation/Getter.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Annotation; + +/** + * Property accessor getter configuration annotation. + * + * @Annotation + * @Target({"METHOD"}) + * + * @author Luis Ramón López + */ +class Getter +{ + /** + * Associates this method to the getter of this property. + * + * @var string + */ + public $property; +} diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Remover.php b/src/Symfony/Component/PropertyAccess/Annotation/Remover.php new file mode 100644 index 0000000000000..f407acc6109ec --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Annotation/Remover.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Annotation; + +/** + * Property accessor remover configuration annotation. + * + * @Annotation + * @Target({"METHOD"}) + * + * @author Luis Ramón López + */ +class Remover +{ + /** + * Associates this method to the setter of this property. + * + * @var string + */ + public $property; +} diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Setter.php b/src/Symfony/Component/PropertyAccess/Annotation/Setter.php new file mode 100644 index 0000000000000..5bdd978391654 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Annotation/Setter.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Annotation; + +/** + * Property accessor setter configuration annotation. + * + * @Annotation + * @Target({"METHOD"}) + * + * @author Luis Ramón López + */ +class Setter +{ + /** + * Associates this method to the setter of this property. + * + * @var string + */ + public $property; +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index e226b2e646982..940fb553fceb7 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -12,7 +12,11 @@ namespace Symfony\Component\PropertyAccess\Mapping\Loader; use Doctrine\Common\Annotations\Reader; +use Symfony\Component\PropertyAccess\Annotation\Adder; +use Symfony\Component\PropertyAccess\Annotation\Getter; use Symfony\Component\PropertyAccess\Annotation\Property; +use Symfony\Component\PropertyAccess\Annotation\Remover; +use Symfony\Component\PropertyAccess\Annotation\Setter; use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; @@ -68,6 +72,44 @@ public function loadClassMetadata(ClassMetadata $classMetadata) } } + foreach ($reflectionClass->getMethods() as $method) { + if ($method->getDeclaringClass()->name === $className) { + + foreach ($this->reader->getMethodAnnotations($method) as $annotation) { + if ($annotation instanceof Getter) { + if (!isset($propertiesMetadata[$annotation->property])) { + $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); + $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); + } + $propertiesMetadata[$annotation->property]->setGetter($method->getName()); + } + if ($annotation instanceof Setter) { + if (!isset($propertiesMetadata[$annotation->property])) { + $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); + $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); + } + $propertiesMetadata[$annotation->property]->setSetter($method->getName()); + } + if ($annotation instanceof Adder) { + if (!isset($propertiesMetadata[$annotation->property])) { + $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); + $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); + } + $propertiesMetadata[$annotation->property]->setAdder($method->getName()); + } + if ($annotation instanceof Remover) { + if (!isset($propertiesMetadata[$annotation->property])) { + $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); + $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); + } + $propertiesMetadata[$annotation->property]->setRemover($method->getName()); + } + + $loaded = true; + } + } + } + return $loaded; } } diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 8902f5d7d8cf6..f377996a2c2f4 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -562,7 +562,7 @@ private function getReadAccessInfo($class, $property) $hasProperty = $reflClass->hasProperty($property); $access[self::ACCESS_HAS_PROPERTY] = $hasProperty; - if ($hasProperty && $this->classMetadataFactory) { + if ($this->classMetadataFactory) { $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertiesMetadata(); $metadata = isset($metadata[$property]) ? $metadata[$property] : null; } @@ -753,7 +753,7 @@ private function getWriteAccessInfo($class, $property, $value) $transversable = is_array($value) || $value instanceof \Traversable; $done = false; - if ($hasProperty && $this->classMetadataFactory) { + if ($this->classMetadataFactory) { $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertiesMetadata(); $metadata = isset($metadata[$property]) ? $metadata[$property] : null; diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php index 444819dff8401..ee0971e2d0966 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php @@ -12,6 +12,8 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; use Symfony\Component\PropertyAccess\Annotation\Property; +use Symfony\Component\PropertyAccess\Annotation\Getter; +use Symfony\Component\PropertyAccess\Annotation\Setter; class TestClass { @@ -29,12 +31,14 @@ class TestClass private $publicGetter; private $date; + private $quantity; + /** * @Property(getter="customGetterTest", setter="customSetterTest") */ private $customGetterSetter; - public function __construct($value) + public function __construct($value, $quantity = 2, $pricePerUnit = 10) { $this->publicProperty = $value; $this->publicAccessor = $value; @@ -47,6 +51,8 @@ public function __construct($value) $this->publicHasAccessor = $value; $this->publicGetter = $value; $this->customGetterSetter = $value; + $this->quantity = $quantity; + $this->pricePerUnit = $pricePerUnit; } public function setPublicAccessor($value) @@ -201,4 +207,30 @@ public function customSetterTest($value) { $this->customGetterSetter = $value; } + + /** + * @return int + */ + public function getQuantity() + { + return $this->quantity; + } + + /** + * @Getter(property="total") + */ + public function getTotal() + { + return $this->quantity * $this->pricePerUnit; + } + + /** + * @Setter(property="total") + * + * @param mixed $total + */ + public function setTotal($total) + { + $this->quantity = $total / $this->pricePerUnit; + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index 29287b2453cdd..c0399f06fa463 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -13,6 +13,9 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; +use Symfony\Component\PropertyAccess\Annotation\Adder; +use Symfony\Component\PropertyAccess\Annotation\Getter; +use Symfony\Component\PropertyAccess\Annotation\Remover; use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -39,6 +42,10 @@ public function addAxis($axis) } // In the test, use a name that StringUtil can't uniquely singularify + /** + * @Adder(property="customVirtualAxes") + * @param $axis + */ public function addAxisTest($axis) { $this->customAxes[] = $axis; @@ -55,6 +62,10 @@ public function removeAxis($axis) } } + /** + * @Remover(property="customVirtualAxes") + * @param $axis + */ public function removeAxisTest($axis) { foreach ($this->customAxes as $key => $value) { @@ -71,6 +82,10 @@ public function getAxes() return $this->axes; } + /** + * @Getter(property="customVirtualAxes") + * @return null + */ public function getCustomAxes() { return $this->customAxes; @@ -202,6 +217,28 @@ public function testSetValueCallsCustomAdderAndRemoverForCollections() $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, new ClassMetadataFactory(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 3f6aac6537002..31d3404825f0d 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -209,6 +209,13 @@ public function testGetWithCustomGetter() $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, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->assertSame(200, $this->propertyAccessor->getValue(new TestClass('webmozart', 10, 20), 'total')); + } + /** * @dataProvider getValidPropertyPaths */ @@ -321,6 +328,18 @@ public function testSetValueWithCustomSetter() $this->assertEquals('it works!', $custom->customGetterTest()); } + public function testSetValueWithCustomSetterMethodAnnotation() + { + AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); + $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(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); From 5f206e6a10262b6991f2884375bfeb78a21b0c7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Mon, 21 Mar 2016 16:16:48 +0100 Subject: [PATCH 06/18] Renamed "attribute" to "property" in XSD and YAML --- .../Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php | 2 +- .../PropertyAccess/Mapping/Loader/YamlFileLoader.php | 4 ++-- .../property-access-mapping/property-access-mapping-1.0.xsd | 4 ++-- .../PropertyAccess/Tests/Fixtures/property-access.xml | 4 ++-- .../PropertyAccess/Tests/Fixtures/property-access.yml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php index 9703a4ff69eb1..5807ff062da1f 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php @@ -49,7 +49,7 @@ public function loadClassMetadata(ClassMetadata $classMetadata) if (isset($this->classes[$classMetadata->getName()])) { $xml = $this->classes[$classMetadata->getName()]; - foreach ($xml->attribute as $attribute) { + foreach ($xml->property as $attribute) { $attributeName = (string) $attribute['name']; if (isset($attributesMetadata[$attributeName])) { diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php index ed02cd4c160c2..d055d4aa395ce 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php @@ -64,10 +64,10 @@ public function loadClassMetadata(ClassMetadata $classMetadata) if (isset($this->classes[$classMetadata->getName()])) { $yaml = $this->classes[$classMetadata->getName()]; - if (isset($yaml['attributes']) && is_array($yaml['attributes'])) { + if (isset($yaml['properties']) && is_array($yaml['properties'])) { $attributesMetadata = $classMetadata->getPropertiesMetadata(); - foreach ($yaml['attributes'] as $attribute => $data) { + foreach ($yaml['properties'] as $attribute => $data) { if (isset($attributesMetadata[$attribute])) { $attributeMetadata = $attributesMetadata[$attribute]; } else { diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd b/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd index 842b845e2953a..027c3d27d0496 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/schema/dic/property-access-mapping/property-access-mapping-1.0.xsd @@ -36,12 +36,12 @@ ]]> - + - + - - + + diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml index ce544f5e1f581..4c78d1bc4be62 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/property-access.yml @@ -1,5 +1,5 @@ 'Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy': - attributes: + properties: foo: getter: getter1 setter: setter1 From 94e6157440c88c8ba49f462dfc2019cf0b66c46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Mon, 21 Mar 2016 20:57:12 +0100 Subject: [PATCH 07/18] Fixed the metadata caching mess --- .../DependencyInjection/Configuration.php | 2 + .../FrameworkExtension.php | 7 +- .../Resources/config/property_access.xml | 19 ++- .../Resources/config/schema/symfony-1.0.xsd | 2 + .../DependencyInjection/Fixtures/php/full.php | 6 + .../DependencyInjection/Fixtures/xml/full.xml | 1 + .../DependencyInjection/Fixtures/yml/full.yml | 5 + .../Exception/NoSuchMetadataException.php | 19 +++ .../Mapping/Cache/CacheInterface.php | 45 ++++++ .../Mapping/Cache/DoctrineCache.php | 69 ++++++++ .../Mapping/Cache/Psr6Cache.php | 78 +++++++++ .../Factory/BlackHoleMetadataFactory.php | 40 +++++ .../Factory/CacheClassMetadataFactory.php | 68 -------- .../Mapping/Factory/ClassMetadataFactory.php | 109 ------------- .../Factory/ClassMetadataFactoryInterface.php | 53 ------- .../Mapping/Factory/ClassResolverTrait.php | 50 ------ .../Factory/LazyLoadingMetadataFactory.php | 150 ++++++++++++++++++ .../Factory/MetadataFactoryInterface.php | 45 ++++++ .../PropertyAccess/PropertyAccessor.php | 4 +- .../PropertyAccess/Tests/Fixtures/Dummy.php | 11 +- .../Tests/Fixtures/DummyParent.php | 28 ++++ .../Tests/Mapping/Cache/AbstractCacheTest.php | 78 +++++++++ .../Tests/Mapping/Cache/DoctrineCacheTest.php | 23 +++ .../Tests/Mapping/Cache/Psr6CacheTest.php | 26 +++ .../Factory/BlackHoleMetadataFactoryTest.php | 33 ++++ .../Factory/CacheMetadataFactoryTest.php | 67 -------- .../Factory/ClassMetadataFactoryTest.php | 79 --------- .../LazyLoadingMetadataFactoryTest.php | 104 ++++++++++++ .../Mapping/Loader/AnnotationLoaderTest.php | 2 +- .../Mapping/TestClassMetadataFactory.php | 4 + .../Tests/PropertyAccessorCollectionTest.php | 6 +- .../Tests/PropertyAccessorTest.php | 10 +- 32 files changed, 794 insertions(+), 449 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/Exception/NoSuchMetadataException.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/BlackHoleMetadataFactory.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php create mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/BlackHoleMetadataFactoryTest.php delete mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php delete mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index a8ac7051086dd..1b4d03a1b8471 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -628,6 +628,8 @@ private function addPropertyAccessSection(ArrayNodeDefinition $rootNode) ->addDefaultsIfNotSet() ->info('Property access configuration') ->children() + ->scalarNode('cache')->end() + ->booleanNode('enable_annotations')->defaultFalse()->end() ->booleanNode('magic_call')->defaultFalse()->end() ->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index e505360ec7325..cd756cba25a15 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -924,17 +924,14 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde */ private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { + $loader->load('property_access.xml'); + $container ->getDefinition('property_accessor') ->replaceArgument(0, $config['magic_call']) ->replaceArgument(1, $config['throw_exception_on_invalid_index']) ; - if (!$this->isConfigEnabled($container, $config)) { - return; - } - - $loader->load('property_access.xml'); $chainLoader = $container->getDefinition('property_access.mapping.chain_loader'); $serializerLoaders = array(); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml index 4deb071f4cefe..2ff06b1ce9626 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml @@ -4,6 +4,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> + + + + @@ -12,16 +16,19 @@ - + null - - - - %property_access.mapping.cache.prefix% - + + + + + %property_access.mapping.cache.prefix% + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index f78174961ccd9..daa99ca7efa3d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -192,7 +192,9 @@ + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php index 6849f8fbd42eb..80eac1209e1eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -63,6 +63,12 @@ 'debug' => true, 'file_cache_dir' => '%kernel.cache_dir%/annotations', ), + 'property_access' => array( + 'magic_call' => false, + 'throw_exception_on_invalid_index' => false, + 'enable_annotations' => true, + 'cache' => 'property_access.mapping.cache.doctrine.apc', + ), 'serializer' => array( 'enabled' => true, 'enable_annotations' => true, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml index e8e34d6e9c0de..bf97152ac3464 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml @@ -41,5 +41,6 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml index d345174e8b134..86ab7f1efd0ba 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -53,6 +53,11 @@ framework: enabled: true enable_annotations: true name_converter: serializer.name_converter.camel_case_to_snake_case + property_access: + enable_annotations: true + magic_call: false + throw_exception_on_invalid_index: false + cache: property_access.mapping.cache.doctrine.apc ide: file%%link%%format request: formats: diff --git a/src/Symfony/Component/PropertyAccess/Exception/NoSuchMetadataException.php b/src/Symfony/Component/PropertyAccess/Exception/NoSuchMetadataException.php new file mode 100644 index 0000000000000..9ac9a835ba6bb --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Exception/NoSuchMetadataException.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Exception; + +/** + * @author Luis Ramón López + */ +class NoSuchMetadataException extends AccessException +{ +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php new file mode 100644 index 0000000000000..963183f71a267 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Cache; + +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; + +/** + * Persists ClassMetadata instances in a cache. + * + * @author Luis Ramón López + */ +interface CacheInterface +{ + /** + * Returns whether metadata for the given class exists in the cache. + * + * @param string $class + */ + public function has($class); + + /** + * Returns the metadata for the given class from the cache. + * + * @param string $class Class Name + * + * @return ClassMetadata|false A ClassMetadata instance or false on miss + */ + public function read($class); + + /** + * Stores a class metadata in the cache. + * + * @param ClassMetadata $metadata A Class Metadata + */ + public function write(ClassMetadata $metadata); +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php b/src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php new file mode 100644 index 0000000000000..642ba8c55ef2e --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Cache; + +use Doctrine\Common\Cache\Cache; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; + +/** + * Adapts a Doctrine cache to a CacheInterface. + * + * @author Luis Ramón López + */ +final class DoctrineCache implements CacheInterface +{ + private $cache; + + /** + * Creates a new Doctrine cache. + * + * @param Cache $cache The cache to adapt + */ + public function __construct(Cache $cache) + { + $this->cache = $cache; + } + + /** + * Sets the cache to adapt. + * + * @param Cache $cache The cache to adapt + */ + public function setCache(Cache $cache) + { + $this->cache = $cache; + } + + /** + * {@inheritdoc} + */ + public function has($class) + { + return $this->cache->contains($class); + } + + /** + * {@inheritdoc} + */ + public function read($class) + { + return $this->cache->fetch($class); + } + + /** + * {@inheritdoc} + */ + public function write(ClassMetadata $metadata) + { + $this->cache->save($metadata->getName(), $metadata); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php b/src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php new file mode 100644 index 0000000000000..5abd0372deea8 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php @@ -0,0 +1,78 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Cache; + +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; + +/** + * PSR-6 adapter. + * + * @author Luis Ramón López + */ +class Psr6Cache implements CacheInterface +{ + /** + * @var CacheItemPoolInterface + */ + private $cacheItemPool; + + public function __construct(CacheItemPoolInterface $cacheItemPool) + { + $this->cacheItemPool = $cacheItemPool; + } + + /** + * {@inheritdoc} + */ + public function has($class) + { + return $this->cacheItemPool->hasItem($this->escapeClassName($class)); + } + + /** + * {@inheritdoc} + */ + public function read($class) + { + $item = $this->cacheItemPool->getItem($this->escapeClassName($class)); + + if (!$item->isHit()) { + return false; + } + + return $item->get(); + } + + /** + * {@inheritdoc} + */ + public function write(ClassMetadata $metadata) + { + $item = $this->cacheItemPool->getItem($this->escapeClassName($metadata->getName())); + $item->set($metadata); + + $this->cacheItemPool->save($item); + } + + /** + * Replaces backslashes by dots in a class name. + * + * @param string $class + * + * @return string + */ + private function escapeClassName($class) + { + return str_replace('\\', '.', $class); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/BlackHoleMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/BlackHoleMetadataFactory.php new file mode 100644 index 0000000000000..ea31be334f66e --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/BlackHoleMetadataFactory.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +/** + * Metadata factory that does not store metadata. + * + * This implementation is useful if you want to validate values against + * constraints only and you don't need to add constraints to classes and + * properties. + * + * @author Luis Ramón López + */ +class BlackHoleMetadataFactory implements MetadataFactoryInterface +{ + /** + * {@inheritdoc} + */ + public function getMetadataFor($value) + { + throw new \LogicException('This class does not support metadata.'); + } + + /** + * {@inheritdoc} + */ + public function hasMetadataFor($value) + { + return false; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php deleted file mode 100644 index a9069f25f1a3a..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/CacheClassMetadataFactory.php +++ /dev/null @@ -1,68 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Factory; - -use Psr\Cache\CacheItemPoolInterface; - -/** - * Caches metadata using a PSR-6 implementation. - * - * @author Kévin Dunglas - */ -class CacheClassMetadataFactory implements ClassMetadataFactoryInterface -{ - use ClassResolverTrait; - - /** - * @var ClassMetadataFactoryInterface - */ - private $decorated; - - /** - * @var CacheItemPoolInterface - */ - private $cacheItemPool; - - public function __construct(ClassMetadataFactoryInterface $decorated, CacheItemPoolInterface $cacheItemPool) - { - $this->decorated = $decorated; - $this->cacheItemPool = $cacheItemPool; - } - - /** - * {@inheritdoc} - */ - public function getMetadataFor($value) - { - $class = $this->getClass($value); - // Key cannot contain backslashes according to PSR-6 - $key = strtr($class, '\\', '_'); - - $item = $this->cacheItemPool->getItem($key); - if ($item->isHit()) { - return $item->get(); - } - - $metadata = $this->decorated->getMetadataFor($value); - $this->cacheItemPool->save($item->set($metadata)); - - return $metadata; - } - - /** - * {@inheritdoc} - */ - public function hasMetadataFor($value) - { - return $this->decorated->hasMetadataFor($value); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php deleted file mode 100644 index 9598f0c0eeb23..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactory.php +++ /dev/null @@ -1,109 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Factory; - -use Doctrine\Common\Cache\Cache; -use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; -use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface; - -/** - * Returns a {@link ClassMetadata}. - * - * @author Kévin Dunglas - */ -class ClassMetadataFactory implements ClassMetadataFactoryInterface -{ - use ClassResolverTrait; - - /** - * @var LoaderInterface - */ - private $loader; - - /** - * @var Cache - */ - private $cache; - - /** - * @var array - */ - private $loadedClasses; - - /** - * @param LoaderInterface $loader - * @param Cache|null $cache - */ - public function __construct(LoaderInterface $loader, Cache $cache = null) - { - $this->loader = $loader; - $this->cache = $cache; - - if (null !== $cache) { - @trigger_error(sprintf('Passing a Doctrine Cache instance as 2nd parameter of the "%s" constructor is deprecated. This parameter will be removed in Symfony 4.0. Use the "%s" class instead.', __CLASS__, CacheClassMetadataFactory::class), E_USER_DEPRECATED); - } - } - - /** - * {@inheritdoc} - */ - public function getMetadataFor($value) - { - $class = $this->getClass($value); - - if (isset($this->loadedClasses[$class])) { - return $this->loadedClasses[$class]; - } - - if ($this->cache && ($this->loadedClasses[$class] = $this->cache->fetch($class))) { - return $this->loadedClasses[$class]; - } - - $classMetadata = new ClassMetadata($class); - $this->loader->loadClassMetadata($classMetadata); - - $reflectionClass = $classMetadata->getReflectionClass(); - - // Include metadata from the parent class - if ($parent = $reflectionClass->getParentClass()) { - $classMetadata->merge($this->getMetadataFor($parent->name)); - } - - // Include metadata from all implemented interfaces - foreach ($reflectionClass->getInterfaces() as $interface) { - $classMetadata->merge($this->getMetadataFor($interface->name)); - } - - if ($this->cache) { - $this->cache->save($class, $classMetadata); - } - - return $this->loadedClasses[$class] = $classMetadata; - } - - /** - * {@inheritdoc} - */ - public function hasMetadataFor($value) - { - try { - $this->getClass($value); - - return true; - } catch (InvalidArgumentException $invalidArgumentException) { - // Return false in case of exception - } - - return false; - } -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php deleted file mode 100644 index 00b0af882356a..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassMetadataFactoryInterface.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Factory; - -use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; - -/** - * Returns a {@see ClassMetadataInterface}. - * - * @author Kévin Dunglas - */ -interface ClassMetadataFactoryInterface -{ - /** - * If the method was called with the same class name (or an object of that - * class) before, the same metadata instance is returned. - * - * If the factory was configured with a cache, this method will first look - * for an existing metadata instance in the cache. If an existing instance - * is found, it will be returned without further ado. - * - * Otherwise, a new metadata instance is created. If the factory was - * configured with a loader, the metadata is passed to the - * {@link \Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface::loadClassMetadata()} method for further - * configuration. At last, the new object is returned. - * - * @param string|object $value - * - * @return ClassMetadata - * - * @throws InvalidArgumentException - */ - public function getMetadataFor($value); - - /** - * Checks if class has metadata. - * - * @param mixed $value - * - * @return bool - */ - public function hasMetadataFor($value); -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php deleted file mode 100644 index 4b84caf986b05..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/ClassResolverTrait.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Factory; - -use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; - -/** - * Resolves a class name. - * - * @internal - * - * @author Kévin Dunglas - */ -trait ClassResolverTrait -{ - /** - * Gets a class name for a given class or instance. - * - * @param mixed $value - * - * @return string - * - * @throws InvalidArgumentException If the class does not exists - */ - private function getClass($value) - { - if (is_string($value)) { - if (!class_exists($value) && !interface_exists($value)) { - throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $value)); - } - - return ltrim($value, '\\'); - } - - if (!is_object($value)) { - throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value))); - } - - return get_class($value); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php new file mode 100644 index 0000000000000..3d4615ba185bc --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +use Symfony\Component\PropertyAccess\Exception\NoSuchMetadataException; +use Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; +use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface; + +/** + * Creates new {@link ClassMetadataInterface} instances. + * + * Whenever {@link getMetadataFor()} is called for the first time with a given + * class name or object of that class, a new metadata instance is created and + * returned. On subsequent requests for the same class, the same metadata + * instance will be returned. + * + * You can optionally pass a {@link LoaderInterface} instance to the constructor. + * Whenever a new metadata instance is created, it is passed to the loader, + * which can configure the metadata based on configuration loaded from the + * filesystem or a database. If you want to use multiple loaders, wrap them in a + * {@link LoaderChain}. + * + * You can also optionally pass a {@link CacheInterface} instance to the + * constructor. This cache will be used for persisting the generated metadata + * between multiple PHP requests. + * + * @author Luis Ramón López + */ +class LazyLoadingMetadataFactory implements MetadataFactoryInterface +{ + /** + * The loader for loading the class metadata. + * + * @var LoaderInterface|null + */ + protected $loader; + + /** + * The cache for caching class metadata. + * + * @var CacheInterface|null + */ + protected $cache; + + /** + * The loaded metadata, indexed by class name. + * + * @var ClassMetadata[] + */ + protected $loadedClasses = array(); + + /** + * Creates a new metadata factory. + * + * @param LoaderInterface|null $loader The loader for configuring new metadata + * @param CacheInterface|null $cache The cache for persisting metadata + * between multiple PHP requests + */ + public function __construct(LoaderInterface $loader = null, CacheInterface $cache = null) + { + $this->loader = $loader; + $this->cache = $cache; + } + + /** + * {@inheritdoc} + * + * If the method was called with the same class name (or an object of that + * class) before, the same metadata instance is returned. + * + * If the factory was configured with a cache, this method will first look + * for an existing metadata instance in the cache. If an existing instance + * is found, it will be returned without further ado. + * + * Otherwise, a new metadata instance is created. If the factory was + * configured with a loader, the metadata is passed to the + * {@link LoaderInterface::loadClassMetadata()} method for further + * configuration. At last, the new object is returned. + */ + public function getMetadataFor($value) + { + if (!is_object($value) && !is_string($value)) { + throw new NoSuchMetadataException(sprintf('Cannot create metadata for non-objects. Got: %s', gettype($value))); + } + + $class = ltrim(is_object($value) ? get_class($value) : $value, '\\'); + + if (isset($this->loadedClasses[$class])) { + return $this->loadedClasses[$class]; + } + + if (null !== $this->cache && false !== ($this->loadedClasses[$class] = $this->cache->read($class))) { + return $this->loadedClasses[$class]; + } + + if (!class_exists($class) && !interface_exists($class)) { + throw new NoSuchMetadataException(sprintf('The class or interface "%s" does not exist.', $class)); + } + + $metadata = new ClassMetadata($class); + + // Include metadata from the parent class + if ($parent = $metadata->getReflectionClass()->getParentClass()) { + $metadata->merge($this->getMetadataFor($parent->name)); + } + + // Include metadata from all implemented interfaces + foreach ($metadata->getReflectionClass()->getInterfaces() as $interface) { + $metadata->merge($this->getMetadataFor($interface->name)); + } + + if (null !== $this->loader) { + $this->loader->loadClassMetadata($metadata); + } + + if (null !== $this->cache) { + $this->cache->write($metadata); + } + + return $this->loadedClasses[$class] = $metadata; + } + + /** + * {@inheritdoc} + */ + public function hasMetadataFor($value) + { + if (!is_object($value) && !is_string($value)) { + return false; + } + + $class = ltrim(is_object($value) ? get_class($value) : $value, '\\'); + + if (class_exists($class) || interface_exists($class)) { + return true; + } + + return false; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php new file mode 100644 index 0000000000000..3ba28c5a525a4 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Mapping\Factory; + +use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; +use Symfony\Component\PropertyAccess\Exception; + +/** + * Returns {@link \Symfony\Component\PropertyAccess\Mapping\MetadataInterface} instances for values. + * + * @since 3.1 + * + * @author Luis Ramón López + */ +interface MetadataFactoryInterface +{ + /** + * Returns the metadata for the given value. + * + * @param mixed $value Some value + * + * @return PropertyMetadata The metadata for the value + * + * @throws Exception\NoSuchPropertyException If no metadata exists for the given value + */ + public function getMetadataFor($value); + + /** + * Returns whether the class is able to return metadata for the given value. + * + * @param mixed $value Some value + * + * @return bool Whether metadata can be returned for that value + */ + public function hasMetadataFor($value); +} diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index f377996a2c2f4..b9bb2c28cdda4 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -23,7 +23,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; -use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\PropertyAccess\Mapping\Factory\MetadataFactoryInterface; /** * Default implementation of {@link PropertyAccessorInterface}. @@ -168,7 +168,7 @@ class PropertyAccessor implements PropertyAccessorInterface * @param CacheItemPoolInterface $cacheItemPool * @param ClassMetadataFactoryInterface $classMetadataFactory */ - public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, ClassMetadataFactoryInterface $classMetadataFactory = null) + public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, MetadataFactoryInterface $classMetadataFactory = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php index f2755b503133c..0a859730d1a2c 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php @@ -12,11 +12,12 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; use Symfony\Component\PropertyAccess\Annotation\Property; +use Symfony\Component\PropertyAccess\Annotation\Getter; /** * Fixtures for testing metadata. */ -class Dummy +class Dummy extends DummyParent { /** * @Property(getter="getter1", setter="setter1", adder="adder1", remover="remover1") @@ -59,4 +60,12 @@ public function setBar($bar) { $this->bar = $bar; } + + /** + * @Getter(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..32f43484d3c6f --- /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\Getter; + +/** + * Fixtures for testing metadata. + */ +class DummyParent +{ + /** + * @Getter(property="test") + */ + public function testParent() + { + return 'parent'; + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php new file mode 100644 index 0000000000000..39db8675faa87 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php @@ -0,0 +1,78 @@ + + * + * 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\Cache; + +use Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface; +use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; + +abstract class AbstractCacheTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var CacheInterface + */ + protected $cache; + + public function testWrite() + { + $meta = $this->getMockBuilder(ClassMetadata::class) + ->disableOriginalConstructor() + ->setMethods(array('getName')) + ->getMock(); + + $meta->expects($this->once()) + ->method('getName') + ->will($this->returnValue('Foo\\Bar')); + + $this->cache->write($meta); + + $this->assertInstanceOf( + ClassMetadata::class, + $this->cache->read('Foo\\Bar'), + 'write() stores metadata' + ); + } + + public function testHas() + { + $meta = $this->getMockBuilder(ClassMetadata::class) + ->disableOriginalConstructor() + ->setMethods(array('getName')) + ->getMock(); + + $meta->expects($this->once()) + ->method('getName') + ->will($this->returnValue('Foo\\Bar')); + + $this->assertFalse($this->cache->has('Foo\\Bar'), 'has() returns false when there is no entry'); + + $this->cache->write($meta); + $this->assertTrue($this->cache->has('Foo\\Bar'), 'has() returns true when the is an entry'); + } + + public function testRead() + { + $meta = $this->getMockBuilder(ClassMetadata::class) + ->disableOriginalConstructor() + ->setMethods(array('getName')) + ->getMock(); + + $meta->expects($this->once()) + ->method('getName') + ->will($this->returnValue('Foo\\Bar')); + + $this->assertFalse($this->cache->read('Foo\\Bar'), 'read() returns false when there is no entry'); + + $this->cache->write($meta); + + $this->assertInstanceOf(ClassMetadata::class, $this->cache->read('Foo\\Bar'), 'read() returns metadata'); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php new file mode 100644 index 0000000000000..6699f74a9efc9 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php @@ -0,0 +1,23 @@ + + * + * 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\Cache; + +use Doctrine\Common\Cache\ArrayCache; +use Symfony\Component\PropertyAccess\Mapping\Cache\DoctrineCache; + +class DoctrineCacheTest extends AbstractCacheTest +{ + protected function setUp() + { + $this->cache = new DoctrineCache(new ArrayCache()); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php new file mode 100644 index 0000000000000..33ed0667aaf1e --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php @@ -0,0 +1,26 @@ + + */ +class Psr6CacheTest extends AbstractCacheTest +{ + protected function setUp() + { + $this->cache = new Psr6Cache(new ArrayAdapter()); + } + + public function testNameCollision() + { + $metadata = new ClassMetadata('Foo\\Bar'); + + $this->cache->write($metadata); + $this->assertFalse($this->cache->has('Foo_Bar')); + } +} 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/CacheMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php deleted file mode 100644 index f491cf6e75504..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/CacheMetadataFactoryTest.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * 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\Cache\Adapter\ArrayAdapter; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; -use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactoryInterface; -use Symfony\Component\PropertyAccess\Mapping\Factory\CacheClassMetadataFactory; -use Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy; - -/** - * @author Kévin Dunglas - */ -class CacheMetadataFactoryTest extends \PHPUnit_Framework_TestCase -{ - public function testGetMetadataFor() - { - $metadata = new ClassMetadata(Dummy::class); - - $decorated = $this->getMock(ClassMetadataFactoryInterface::class); - $decorated - ->expects($this->once()) - ->method('getMetadataFor') - ->will($this->returnValue($metadata)) - ; - - $factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter()); - - $this->assertEquals($metadata, $factory->getMetadataFor(Dummy::class)); - // The second call should retrieve the value from the cache - $this->assertEquals($metadata, $factory->getMetadataFor(Dummy::class)); - } - - public function testHasMetadataFor() - { - $decorated = $this->getMock(ClassMetadataFactoryInterface::class); - $decorated - ->expects($this->once()) - ->method('hasMetadataFor') - ->will($this->returnValue(true)) - ; - - $factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter()); - - $this->assertTrue($factory->hasMetadataFor(Dummy::class)); - } - - /** - * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException - */ - public function testInvalidClassThrowsException() - { - $decorated = $this->getMock(ClassMetadataFactoryInterface::class); - $factory = new CacheClassMetadataFactory($decorated, new ArrayAdapter()); - - $factory->getMetadataFor('Not\Exist'); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php deleted file mode 100644 index a8e22853f354f..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/ClassMetadataFactoryTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * 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 Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\AnnotationRegistry; -use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; -use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader; -use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderChain; -use Symfony\Component\PropertyAccess\Tests\Mapping\TestClassMetadataFactory; - -/** - * @author Kévin Dunglas - */ -class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase -{ - public function testInterface() - { - $classMetadata = new ClassMetadataFactory(new LoaderChain(array())); - $this->assertInstanceOf('Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactoryInterface', $classMetadata); - } - - public function testGetMetadataFor() - { - AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..'); - $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - $classMetadata = $factory->getMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy'); - - $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata); - } - - public function testHasMetadataFor() - { - $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - $this->assertTrue($factory->hasMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy')); - $this->assertFalse($factory->hasMetadataFor('Dunglas\Entity')); - } - - /** - * @group legacy - */ - public function testCacheExists() - { - $cache = $this->getMock('Doctrine\Common\Cache\Cache'); - $cache - ->expects($this->once()) - ->method('fetch') - ->will($this->returnValue('foo')) - ; - AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..'); - $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache); - $this->assertEquals('foo', $factory->getMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy')); - } - - /** - * @group legacy - */ - public function testCacheNotExists() - { - $cache = $this->getMock('Doctrine\Common\Cache\Cache'); - $cache->method('fetch')->will($this->returnValue(false)); - $cache->method('save'); - - AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../../../..'); - $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()), $cache); - $metadata = $factory->getMetadataFor('Symfony\Component\PropertyAccess\Tests\Fixtures\Dummy'); - - $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $metadata); - } -} 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..b0480d7b3cb5e --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -0,0 +1,104 @@ + + * + * 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->getPropertiesMetadata()); + } + + 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->getPropertiesMetadata()); + } + + public function testWriteMetadataToCache() + { + $cache = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface'); + $factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache); + + $properties = array( + self::PARENTCLASS => new PropertyMetadata(self::PARENTCLASS), + ); + + $cache->expects($this->never()) + ->method('has'); + $cache->expects($this->once()) + ->method('read') + ->with($this->equalTo(self::PARENTCLASS)) + ->will($this->returnValue(false)); + $cache->expects($this->once()) + ->method('write') + ->will($this->returnCallback(function ($metadata) use ($properties) { + $this->assertEquals($properties, $metadata->getPropertiesMetadata()); + })); + + $metadata = $factory->getMetadataFor(self::PARENTCLASS); + + $this->assertEquals(self::PARENTCLASS, $metadata->getName()); + $this->assertEquals($properties, $metadata->getPropertiesMetadata()); + } + + public function testReadMetadataFromCache() + { + $loader = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface'); + $cache = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface'); + $factory = new LazyLoadingMetadataFactory($loader, $cache); + + $metadata = new ClassMetadata(self::PARENTCLASS); + $metadata->addPropertyMetadata(new PropertyMetadata()); + + $loader->expects($this->never()) + ->method('loadClassMetadata'); + + $cache->expects($this->never()) + ->method('has'); + $cache->expects($this->once()) + ->method('read') + ->will($this->returnValue($metadata)); + + $this->assertEquals($metadata, $factory->getMetadataFor(self::PARENTCLASS)); + } +} + +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 index 9891fcfcef7fc..0cfa2c1fbaf89 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -41,7 +41,7 @@ public function testInterface() 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)); } diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php index 1adc35fb283cb..251259031872c 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/TestClassMetadataFactory.php @@ -36,6 +36,10 @@ public static function createClassMetadata() $bar->setGetter('getter2'); $expected->addPropertyMetadata($bar); + $test = new PropertyMetadata('test'); + $test->setGetter('testChild'); + $expected->addPropertyMetadata($test); + return $expected; } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index c0399f06fa463..1630428459c3f 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -16,7 +16,7 @@ use Symfony\Component\PropertyAccess\Annotation\Adder; use Symfony\Component\PropertyAccess\Annotation\Getter; use Symfony\Component\PropertyAccess\Annotation\Remover; -use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\PropertyAccess\Mapping\Factory\LazyLoadingMetadataFactory; use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader; use Symfony\Component\PropertyAccess\PropertyAccessor; @@ -207,7 +207,7 @@ public function testSetValueCallsCustomAdderAndRemoverForCollections() $car = new PropertyAccessorCollectionTest_Car($axesBefore); AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->propertyAccessor->setValue($car, 'customAxes', $axesMerged); @@ -229,7 +229,7 @@ public function testSetValueCallsCustomAdderAndRemoverForCollectionsMethodAnnota $car = new PropertyAccessorCollectionTest_Car($axesBefore); AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->propertyAccessor->setValue($car, 'customVirtualAxes', $axesMerged); diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 31d3404825f0d..bea99f9e010bd 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -15,7 +15,7 @@ use Doctrine\Common\Annotations\AnnotationRegistry; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; -use Symfony\Component\PropertyAccess\Mapping\Factory\ClassMetadataFactory; +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; @@ -205,14 +205,14 @@ public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p public function testGetWithCustomGetter() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, 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, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->assertSame(200, $this->propertyAccessor->getValue(new TestClass('webmozart', 10, 20), 'total')); } @@ -319,7 +319,7 @@ public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p public function testSetValueWithCustomSetter() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $custom = new TestClass('webmozart'); @@ -331,7 +331,7 @@ public function testSetValueWithCustomSetter() public function testSetValueWithCustomSetterMethodAnnotation() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $custom = new TestClass('webmozart', 10, 20); From 09707add7aa25cd52910e9197d357a01bcedf988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Tue, 22 Mar 2016 12:36:02 +0100 Subject: [PATCH 08/18] Replaced metadata caching with standard PSR-6 cache pool --- CONTRIBUTORS.md | 1 + .../FrameworkExtension.php | 5 -- .../Resources/config/property_access.xml | 15 ---- .../DependencyInjection/Fixtures/php/full.php | 1 - .../DependencyInjection/Fixtures/yml/full.yml | 1 - .../Component/PropertyAccess/CHANGELOG.md | 4 + .../Mapping/Cache/CacheInterface.php | 45 ----------- .../Mapping/Cache/DoctrineCache.php | 69 ---------------- .../Mapping/Cache/Psr6Cache.php | 78 ------------------- .../Factory/LazyLoadingMetadataFactory.php | 32 ++++++-- .../Tests/Mapping/Cache/AbstractCacheTest.php | 78 ------------------- .../Tests/Mapping/Cache/DoctrineCacheTest.php | 23 ------ .../Tests/Mapping/Cache/Psr6CacheTest.php | 26 ------- .../LazyLoadingMetadataFactoryTest.php | 65 ++++++++++++---- .../Component/PropertyAccess/composer.json | 5 +- 15 files changed, 84 insertions(+), 364 deletions(-) delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php delete mode 100644 src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php delete mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php delete mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php delete mode 100644 src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6f73ccb150471..fbdc5a470a437 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1538,3 +1538,4 @@ Symfony is the result of the work of many people who made the code better - Erik Saunier (snickers) - Matej Žilák (teo_sk) - Vladislav Vlastovskiy (vlastv) + - Luis Ramón López (lrlopez) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index cd756cba25a15..17bd58d3a2d30 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -987,11 +987,6 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $chainLoader->replaceArgument(0, $serializerLoaders); if (isset($config['cache']) && $config['cache']) { - $container->setParameter( - 'property_access.mapping.cache.prefix', - 'property_access_'.$this->getKernelRootHash($container) - ); - $container->getDefinition('property_access.mapping.class_metadata_factory')->replaceArgument( 1, new Reference($config['cache']) ); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml index 2ff06b1ce9626..43765b37af72d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/property_access.xml @@ -4,12 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> - - - - - @@ -21,16 +16,6 @@ null - - - - - %property_access.mapping.cache.prefix% - - - - - diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php index 80eac1209e1eb..b18a95aba1a90 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php @@ -67,7 +67,6 @@ 'magic_call' => false, 'throw_exception_on_invalid_index' => false, 'enable_annotations' => true, - 'cache' => 'property_access.mapping.cache.doctrine.apc', ), 'serializer' => array( 'enabled' => true, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml index 86ab7f1efd0ba..10c8f407df436 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml @@ -57,7 +57,6 @@ framework: enable_annotations: true magic_call: false throw_exception_on_invalid_index: false - cache: property_access.mapping.cache.doctrine.apc ide: file%%link%%format request: formats: diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index 574106e521075..c7aead7bfe00a 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -1,6 +1,10 @@ CHANGELOG ========= +3.1.0 +------ + * added custom method calling for properties. + 2.7.0 ------ diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php deleted file mode 100644 index 963183f71a267..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Cache/CacheInterface.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Cache; - -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; - -/** - * Persists ClassMetadata instances in a cache. - * - * @author Luis Ramón López - */ -interface CacheInterface -{ - /** - * Returns whether metadata for the given class exists in the cache. - * - * @param string $class - */ - public function has($class); - - /** - * Returns the metadata for the given class from the cache. - * - * @param string $class Class Name - * - * @return ClassMetadata|false A ClassMetadata instance or false on miss - */ - public function read($class); - - /** - * Stores a class metadata in the cache. - * - * @param ClassMetadata $metadata A Class Metadata - */ - public function write(ClassMetadata $metadata); -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php b/src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php deleted file mode 100644 index 642ba8c55ef2e..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Cache/DoctrineCache.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Cache; - -use Doctrine\Common\Cache\Cache; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; - -/** - * Adapts a Doctrine cache to a CacheInterface. - * - * @author Luis Ramón López - */ -final class DoctrineCache implements CacheInterface -{ - private $cache; - - /** - * Creates a new Doctrine cache. - * - * @param Cache $cache The cache to adapt - */ - public function __construct(Cache $cache) - { - $this->cache = $cache; - } - - /** - * Sets the cache to adapt. - * - * @param Cache $cache The cache to adapt - */ - public function setCache(Cache $cache) - { - $this->cache = $cache; - } - - /** - * {@inheritdoc} - */ - public function has($class) - { - return $this->cache->contains($class); - } - - /** - * {@inheritdoc} - */ - public function read($class) - { - return $this->cache->fetch($class); - } - - /** - * {@inheritdoc} - */ - public function write(ClassMetadata $metadata) - { - $this->cache->save($metadata->getName(), $metadata); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php b/src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php deleted file mode 100644 index 5abd0372deea8..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Mapping/Cache/Psr6Cache.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\PropertyAccess\Mapping\Cache; - -use Psr\Cache\CacheItemPoolInterface; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; - -/** - * PSR-6 adapter. - * - * @author Luis Ramón López - */ -class Psr6Cache implements CacheInterface -{ - /** - * @var CacheItemPoolInterface - */ - private $cacheItemPool; - - public function __construct(CacheItemPoolInterface $cacheItemPool) - { - $this->cacheItemPool = $cacheItemPool; - } - - /** - * {@inheritdoc} - */ - public function has($class) - { - return $this->cacheItemPool->hasItem($this->escapeClassName($class)); - } - - /** - * {@inheritdoc} - */ - public function read($class) - { - $item = $this->cacheItemPool->getItem($this->escapeClassName($class)); - - if (!$item->isHit()) { - return false; - } - - return $item->get(); - } - - /** - * {@inheritdoc} - */ - public function write(ClassMetadata $metadata) - { - $item = $this->cacheItemPool->getItem($this->escapeClassName($metadata->getName())); - $item->set($metadata); - - $this->cacheItemPool->save($item); - } - - /** - * Replaces backslashes by dots in a class name. - * - * @param string $class - * - * @return string - */ - private function escapeClassName($class) - { - return str_replace('\\', '.', $class); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php index 3d4615ba185bc..1dc34ee9e1d85 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -11,8 +11,8 @@ namespace Symfony\Component\PropertyAccess\Mapping\Factory; +use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\PropertyAccess\Exception\NoSuchMetadataException; -use Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface; use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; use Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface; @@ -62,11 +62,11 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface /** * Creates a new metadata factory. * - * @param LoaderInterface|null $loader The loader for configuring new metadata - * @param CacheInterface|null $cache The cache for persisting metadata - * between multiple PHP requests + * @param LoaderInterface|null $loader The loader for configuring new metadata + * @param CacheItemPoolInterface|null $cache The PSR-6 cache for persisting metadata + * between multiple PHP requests */ - public function __construct(LoaderInterface $loader = null, CacheInterface $cache = null) + public function __construct(LoaderInterface $loader = null, CacheItemPoolInterface $cache = null) { $this->loader = $loader; $this->cache = $cache; @@ -99,8 +99,11 @@ public function getMetadataFor($value) return $this->loadedClasses[$class]; } - if (null !== $this->cache && false !== ($this->loadedClasses[$class] = $this->cache->read($class))) { - return $this->loadedClasses[$class]; + if (null !== $this->cache) { + $item = $this->cache->getItem($this->escapeClassName($class)); + if ($item->isHit()) { + return $this->loadedClasses[$class] = $item->get(); + } } if (!class_exists($class) && !interface_exists($class)) { @@ -124,7 +127,8 @@ public function getMetadataFor($value) } if (null !== $this->cache) { - $this->cache->write($metadata); + $item->set($metadata); + $this->cache->save($item); } return $this->loadedClasses[$class] = $metadata; @@ -147,4 +151,16 @@ public function hasMetadataFor($value) return false; } + + /** + * Replaces backslashes by dots in a class name. + * + * @param string $class + * + * @return string + */ + private function escapeClassName($class) + { + return str_replace('\\', '.', $class); + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php deleted file mode 100644 index 39db8675faa87..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/AbstractCacheTest.php +++ /dev/null @@ -1,78 +0,0 @@ - - * - * 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\Cache; - -use Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface; -use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; - -abstract class AbstractCacheTest extends \PHPUnit_Framework_TestCase -{ - /** - * @var CacheInterface - */ - protected $cache; - - public function testWrite() - { - $meta = $this->getMockBuilder(ClassMetadata::class) - ->disableOriginalConstructor() - ->setMethods(array('getName')) - ->getMock(); - - $meta->expects($this->once()) - ->method('getName') - ->will($this->returnValue('Foo\\Bar')); - - $this->cache->write($meta); - - $this->assertInstanceOf( - ClassMetadata::class, - $this->cache->read('Foo\\Bar'), - 'write() stores metadata' - ); - } - - public function testHas() - { - $meta = $this->getMockBuilder(ClassMetadata::class) - ->disableOriginalConstructor() - ->setMethods(array('getName')) - ->getMock(); - - $meta->expects($this->once()) - ->method('getName') - ->will($this->returnValue('Foo\\Bar')); - - $this->assertFalse($this->cache->has('Foo\\Bar'), 'has() returns false when there is no entry'); - - $this->cache->write($meta); - $this->assertTrue($this->cache->has('Foo\\Bar'), 'has() returns true when the is an entry'); - } - - public function testRead() - { - $meta = $this->getMockBuilder(ClassMetadata::class) - ->disableOriginalConstructor() - ->setMethods(array('getName')) - ->getMock(); - - $meta->expects($this->once()) - ->method('getName') - ->will($this->returnValue('Foo\\Bar')); - - $this->assertFalse($this->cache->read('Foo\\Bar'), 'read() returns false when there is no entry'); - - $this->cache->write($meta); - - $this->assertInstanceOf(ClassMetadata::class, $this->cache->read('Foo\\Bar'), 'read() returns metadata'); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php deleted file mode 100644 index 6699f74a9efc9..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/DoctrineCacheTest.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * 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\Cache; - -use Doctrine\Common\Cache\ArrayCache; -use Symfony\Component\PropertyAccess\Mapping\Cache\DoctrineCache; - -class DoctrineCacheTest extends AbstractCacheTest -{ - protected function setUp() - { - $this->cache = new DoctrineCache(new ArrayCache()); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php deleted file mode 100644 index 33ed0667aaf1e..0000000000000 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Cache/Psr6CacheTest.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class Psr6CacheTest extends AbstractCacheTest -{ - protected function setUp() - { - $this->cache = new Psr6Cache(new ArrayAdapter()); - } - - public function testNameCollision() - { - $metadata = new ClassMetadata('Foo\\Bar'); - - $this->cache->write($metadata); - $this->assertFalse($this->cache->has('Foo_Bar')); - } -} diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index b0480d7b3cb5e..89a5baa4bffc1 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -48,25 +48,35 @@ public function testMergeParentMetadata() public function testWriteMetadataToCache() { - $cache = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface'); + $cache = $this->getMock('Psr\Cache\CacheItemPoolInterface'); $factory = new LazyLoadingMetadataFactory(new TestLoader(), $cache); $properties = array( self::PARENTCLASS => new PropertyMetadata(self::PARENTCLASS), ); - $cache->expects($this->never()) - ->method('has'); - $cache->expects($this->once()) - ->method('read') - ->with($this->equalTo(self::PARENTCLASS)) - ->will($this->returnValue(false)); + $cacheItem = $this->getMock('Psr\Cache\CacheItemInterface'); + $cache->expects($this->once()) - ->method('write') - ->will($this->returnCallback(function ($metadata) use ($properties) { + ->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->getPropertiesMetadata()); })); + $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()); @@ -76,7 +86,7 @@ public function testWriteMetadataToCache() public function testReadMetadataFromCache() { $loader = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Loader\LoaderInterface'); - $cache = $this->getMock('Symfony\Component\PropertyAccess\Mapping\Cache\CacheInterface'); + $cache = $this->getMock('Psr\Cache\CacheItemPoolInterface'); $factory = new LazyLoadingMetadataFactory($loader, $cache); $metadata = new ClassMetadata(self::PARENTCLASS); @@ -85,14 +95,41 @@ public function testReadMetadataFromCache() $loader->expects($this->never()) ->method('loadClassMetadata'); - $cache->expects($this->never()) - ->method('has'); + $cacheItem = $this->getMock('Psr\Cache\CacheItemInterface'); + $cache->expects($this->once()) - ->method('read') - ->will($this->returnValue($metadata)); + ->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 diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index 7ab214165f5da..b97870aae6894 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -21,8 +21,11 @@ "symfony/inflector": "~3.1" }, "require-dev": { + "doctrine/cache": "~1.0", "doctrine/annotations": "~1.2", - "symfony/cache": "~3.1" + "symfony/cache": "~3.1", + "symfony/config": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0", }, "suggest": { "psr/cache-implementation": "To cache access methods." From d02d93a93a5de5088938458192a62f6212bb621a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sat, 26 Mar 2016 00:40:34 +0100 Subject: [PATCH 09/18] Added support to metadata factories in PropertyAccesorBuilder --- .../PropertyAccessorBuilder.php | 32 ++++++++++++++++++- .../Tests/PropertyAccessorBuilderTest.php | 8 +++++ 2 files changed, 39 insertions(+), 1 deletion(-) 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/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()); From 748b894f01c6174d644e6cbd656c81d6540c3294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Tue, 29 Mar 2016 23:32:59 +0200 Subject: [PATCH 10/18] Fix outdated type hint in LazyLoadingMetadataFactory --- CONTRIBUTORS.md | 1 - .../Mapping/Factory/LazyLoadingMetadataFactory.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index fbdc5a470a437..6f73ccb150471 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1538,4 +1538,3 @@ Symfony is the result of the work of many people who made the code better - Erik Saunier (snickers) - Matej Žilák (teo_sk) - Vladislav Vlastovskiy (vlastv) - - Luis Ramón López (lrlopez) diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php index 1dc34ee9e1d85..551b0f9e6b2ac 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -48,7 +48,7 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface /** * The cache for caching class metadata. * - * @var CacheInterface|null + * @var CacheItemPoolInterface|null */ protected $cache; From 688cbdab73bfa73928b33b7c6eea5a26e39e78f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sat, 2 Apr 2016 01:37:05 +0200 Subject: [PATCH 11/18] Fixed typo in variable name --- src/Symfony/Component/PropertyAccess/PropertyAccessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index b9bb2c28cdda4..3b9fc3ed2aafe 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -750,7 +750,7 @@ private function getWriteAccessInfo($class, $property, $value) $hasProperty = $reflClass->hasProperty($property); $access[self::ACCESS_HAS_PROPERTY] = $hasProperty; - $transversable = is_array($value) || $value instanceof \Traversable; + $traversable = is_array($value) || $value instanceof \Traversable; $done = false; if ($this->classMetadataFactory) { @@ -758,7 +758,7 @@ private function getWriteAccessInfo($class, $property, $value) $metadata = isset($metadata[$property]) ? $metadata[$property] : null; if ($metadata) { - if ($transversable && $metadata->getAdder() && $metadata->getRemover()) { + 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(); From 003efdb2aec1a91d57d9c248883f7d50eef5c366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sat, 2 Apr 2016 02:01:21 +0200 Subject: [PATCH 12/18] Added 'Property' prefix to method annotations --- .../Annotation/{Adder.php => PropertyAdder.php} | 4 ++-- .../{Getter.php => PropertyGetter.php} | 2 +- .../{Remover.php => PropertyRemover.php} | 4 ++-- .../{Setter.php => PropertySetter.php} | 2 +- .../Mapping/Loader/AnnotationLoader.php | 16 ++++++++-------- .../PropertyAccess/Tests/Fixtures/Dummy.php | 4 ++-- .../Tests/Fixtures/DummyParent.php | 4 ++-- .../PropertyAccess/Tests/Fixtures/TestClass.php | 8 ++++---- .../Tests/PropertyAccessorCollectionTest.php | 12 ++++++------ 9 files changed, 28 insertions(+), 28 deletions(-) rename src/Symfony/Component/PropertyAccess/Annotation/{Adder.php => PropertyAdder.php} (86%) rename src/Symfony/Component/PropertyAccess/Annotation/{Getter.php => PropertyGetter.php} (96%) rename src/Symfony/Component/PropertyAccess/Annotation/{Remover.php => PropertyRemover.php} (85%) rename src/Symfony/Component/PropertyAccess/Annotation/{Setter.php => PropertySetter.php} (96%) diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Adder.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAdder.php similarity index 86% rename from src/Symfony/Component/PropertyAccess/Annotation/Adder.php rename to src/Symfony/Component/PropertyAccess/Annotation/PropertyAdder.php index 186af7e5ac45f..71c638e4c40e6 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/Adder.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertyAdder.php @@ -19,10 +19,10 @@ * * @author Luis Ramón López */ -class Adder +class PropertyAdder { /** - * Associates this method to the setter of this property. + * Associates this method to the adder of this property. * * @var string */ diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Getter.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertyGetter.php similarity index 96% rename from src/Symfony/Component/PropertyAccess/Annotation/Getter.php rename to src/Symfony/Component/PropertyAccess/Annotation/PropertyGetter.php index 0b054a1306461..1e3d921633001 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/Getter.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertyGetter.php @@ -19,7 +19,7 @@ * * @author Luis Ramón López */ -class Getter +class PropertyGetter { /** * Associates this method to the getter of this property. diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Remover.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertyRemover.php similarity index 85% rename from src/Symfony/Component/PropertyAccess/Annotation/Remover.php rename to src/Symfony/Component/PropertyAccess/Annotation/PropertyRemover.php index f407acc6109ec..81a99f835aaed 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/Remover.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertyRemover.php @@ -19,10 +19,10 @@ * * @author Luis Ramón López */ -class Remover +class PropertyRemover { /** - * Associates this method to the setter of this property. + * Associates this method to the remover of this property. * * @var string */ diff --git a/src/Symfony/Component/PropertyAccess/Annotation/Setter.php b/src/Symfony/Component/PropertyAccess/Annotation/PropertySetter.php similarity index 96% rename from src/Symfony/Component/PropertyAccess/Annotation/Setter.php rename to src/Symfony/Component/PropertyAccess/Annotation/PropertySetter.php index 5bdd978391654..120ad587bc614 100644 --- a/src/Symfony/Component/PropertyAccess/Annotation/Setter.php +++ b/src/Symfony/Component/PropertyAccess/Annotation/PropertySetter.php @@ -19,7 +19,7 @@ * * @author Luis Ramón López */ -class Setter +class PropertySetter { /** * Associates this method to the setter of this property. diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index 940fb553fceb7..432e28c649def 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -12,11 +12,11 @@ namespace Symfony\Component\PropertyAccess\Mapping\Loader; use Doctrine\Common\Annotations\Reader; -use Symfony\Component\PropertyAccess\Annotation\Adder; -use Symfony\Component\PropertyAccess\Annotation\Getter; +use Symfony\Component\PropertyAccess\Annotation\PropertyAdder; +use Symfony\Component\PropertyAccess\Annotation\PropertyGetter; use Symfony\Component\PropertyAccess\Annotation\Property; -use Symfony\Component\PropertyAccess\Annotation\Remover; -use Symfony\Component\PropertyAccess\Annotation\Setter; +use Symfony\Component\PropertyAccess\Annotation\PropertyRemover; +use Symfony\Component\PropertyAccess\Annotation\PropertySetter; use Symfony\Component\PropertyAccess\Mapping\PropertyMetadata; use Symfony\Component\PropertyAccess\Mapping\ClassMetadata; @@ -76,28 +76,28 @@ public function loadClassMetadata(ClassMetadata $classMetadata) if ($method->getDeclaringClass()->name === $className) { foreach ($this->reader->getMethodAnnotations($method) as $annotation) { - if ($annotation instanceof Getter) { + if ($annotation instanceof PropertyGetter) { if (!isset($propertiesMetadata[$annotation->property])) { $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); } $propertiesMetadata[$annotation->property]->setGetter($method->getName()); } - if ($annotation instanceof Setter) { + if ($annotation instanceof PropertySetter) { if (!isset($propertiesMetadata[$annotation->property])) { $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); } $propertiesMetadata[$annotation->property]->setSetter($method->getName()); } - if ($annotation instanceof Adder) { + if ($annotation instanceof PropertyAdder) { if (!isset($propertiesMetadata[$annotation->property])) { $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); } $propertiesMetadata[$annotation->property]->setAdder($method->getName()); } - if ($annotation instanceof Remover) { + if ($annotation instanceof PropertyRemover) { if (!isset($propertiesMetadata[$annotation->property])) { $propertiesMetadata[$annotation->property] = new PropertyMetadata($annotation->property); $classMetadata->addPropertyMetadata($propertiesMetadata[$annotation->property]); diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php index 0a859730d1a2c..dc1ce60818ba5 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/Dummy.php @@ -12,7 +12,7 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; use Symfony\Component\PropertyAccess\Annotation\Property; -use Symfony\Component\PropertyAccess\Annotation\Getter; +use Symfony\Component\PropertyAccess\Annotation\PropertyGetter; /** * Fixtures for testing metadata. @@ -62,7 +62,7 @@ public function setBar($bar) } /** - * @Getter(property="test") + * @PropertyGetter(property="test") */ public function testChild() { diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php index 32f43484d3c6f..2475d14c96f82 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/DummyParent.php @@ -11,7 +11,7 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; -use Symfony\Component\PropertyAccess\Annotation\Getter; +use Symfony\Component\PropertyAccess\Annotation\PropertyGetter; /** * Fixtures for testing metadata. @@ -19,7 +19,7 @@ class DummyParent { /** - * @Getter(property="test") + * @PropertyGetter(property="test") */ public function testParent() { diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php index ee0971e2d0966..2276e03c4ffb8 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php @@ -12,8 +12,8 @@ namespace Symfony\Component\PropertyAccess\Tests\Fixtures; use Symfony\Component\PropertyAccess\Annotation\Property; -use Symfony\Component\PropertyAccess\Annotation\Getter; -use Symfony\Component\PropertyAccess\Annotation\Setter; +use Symfony\Component\PropertyAccess\Annotation\PropertyGetter; +use Symfony\Component\PropertyAccess\Annotation\PropertySetter; class TestClass { @@ -217,7 +217,7 @@ public function getQuantity() } /** - * @Getter(property="total") + * @PropertyGetter(property="total") */ public function getTotal() { @@ -225,7 +225,7 @@ public function getTotal() } /** - * @Setter(property="total") + * @PropertySetter(property="total") * * @param mixed $total */ diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index 1630428459c3f..7fdb2e82f1fcc 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -13,9 +13,9 @@ use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\Common\Annotations\AnnotationRegistry; -use Symfony\Component\PropertyAccess\Annotation\Adder; -use Symfony\Component\PropertyAccess\Annotation\Getter; -use Symfony\Component\PropertyAccess\Annotation\Remover; +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; @@ -43,7 +43,7 @@ public function addAxis($axis) // In the test, use a name that StringUtil can't uniquely singularify /** - * @Adder(property="customVirtualAxes") + * @PropertyAdder(property="customVirtualAxes") * @param $axis */ public function addAxisTest($axis) @@ -63,7 +63,7 @@ public function removeAxis($axis) } /** - * @Remover(property="customVirtualAxes") + * @PropertyRemover(property="customVirtualAxes") * @param $axis */ public function removeAxisTest($axis) @@ -83,7 +83,7 @@ public function getAxes() } /** - * @Getter(property="customVirtualAxes") + * @PropertyGetter(property="customVirtualAxes") * @return null */ public function getCustomAxes() From dc5cacbee6ea9b3d08946295efd77c092dfd5b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sat, 2 Apr 2016 02:33:09 +0200 Subject: [PATCH 13/18] Renamed 'propertiesMetadata' to 'propertyMetadataCollection' in 'ClassMetadata' --- .../PropertyAccess/Mapping/ClassMetadata.php | 18 +++++++++--------- .../Mapping/Loader/AnnotationLoader.php | 2 +- .../Mapping/Loader/XmlFileLoader.php | 2 +- .../Mapping/Loader/YamlFileLoader.php | 2 +- .../Tests/Mapping/ClassMetadataTest.php | 2 +- .../Factory/LazyLoadingMetadataFactoryTest.php | 8 ++++---- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php index 95c88c3469adf..b3675f21f4b45 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/ClassMetadata.php @@ -32,9 +32,9 @@ class ClassMetadata * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use - * {@link getPropertiesMetadata()} instead. + * {@link getPropertyMetadataCollection()} instead. */ - public $propertiesMetadata = array(); + public $propertyMetadataCollection = array(); /** * @var \ReflectionClass @@ -68,7 +68,7 @@ public function getName() */ public function addPropertyMetadata(PropertyMetadata $propertyMetadata) { - $this->propertiesMetadata[$propertyMetadata->getName()] = $propertyMetadata; + $this->propertyMetadataCollection[$propertyMetadata->getName()] = $propertyMetadata; } /** @@ -76,9 +76,9 @@ public function addPropertyMetadata(PropertyMetadata $propertyMetadata) * * @return PropertyMetadata[] */ - public function getPropertiesMetadata() + public function getPropertyMetadataCollection() { - return $this->propertiesMetadata; + return $this->propertyMetadataCollection; } /** @@ -88,9 +88,9 @@ public function getPropertiesMetadata() */ public function merge(ClassMetadata $classMetadata) { - foreach ($classMetadata->getPropertiesMetadata() as $attributeMetadata) { - if (isset($this->propertiesMetadata[$attributeMetadata->getName()])) { - $this->propertiesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata); + foreach ($classMetadata->getPropertyMetadataCollection() as $attributeMetadata) { + if (isset($this->propertyMetadataCollection[$attributeMetadata->getName()])) { + $this->propertyMetadataCollection[$attributeMetadata->getName()]->merge($attributeMetadata); } else { $this->addPropertyMetadata($attributeMetadata); } @@ -120,7 +120,7 @@ public function __sleep() { return array( 'name', - 'propertiesMetadata', + 'propertyMetadataCollection', ); } } diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index 432e28c649def..a1264b4908277 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -50,7 +50,7 @@ public function loadClassMetadata(ClassMetadata $classMetadata) $className = $reflectionClass->name; $loaded = false; - $propertiesMetadata = $classMetadata->getPropertiesMetadata(); + $propertiesMetadata = $classMetadata->getPropertyMetadataCollection(); foreach ($reflectionClass->getProperties() as $property) { if (!isset($propertiesMetadata[$property->name])) { diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php index 5807ff062da1f..8b69d2dec5d0c 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/XmlFileLoader.php @@ -44,7 +44,7 @@ public function loadClassMetadata(ClassMetadata $classMetadata) } } - $attributesMetadata = $classMetadata->getPropertiesMetadata(); + $attributesMetadata = $classMetadata->getPropertyMetadataCollection(); if (isset($this->classes[$classMetadata->getName()])) { $xml = $this->classes[$classMetadata->getName()]; diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php index d055d4aa395ce..19499a405c087 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/YamlFileLoader.php @@ -65,7 +65,7 @@ public function loadClassMetadata(ClassMetadata $classMetadata) $yaml = $this->classes[$classMetadata->getName()]; if (isset($yaml['properties']) && is_array($yaml['properties'])) { - $attributesMetadata = $classMetadata->getPropertiesMetadata(); + $attributesMetadata = $classMetadata->getPropertyMetadataCollection(); foreach ($yaml['properties'] as $attribute => $data) { if (isset($attributesMetadata[$attribute])) { diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php index fd3ecaa0dcee0..e54a392befa76 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/ClassMetadataTest.php @@ -37,7 +37,7 @@ public function testAttributeMetadata() $classMetadata->addPropertyMetadata($a1); $classMetadata->addPropertyMetadata($a2); - $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getPropertiesMetadata()); + $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getPropertyMetadataCollection()); } public function testSerialize() diff --git a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php index 89a5baa4bffc1..7549b652c0bad 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Mapping/Factory/LazyLoadingMetadataFactoryTest.php @@ -30,7 +30,7 @@ public function testLoadClassMetadata() self::PARENTCLASS => new PropertyMetadata(self::PARENTCLASS), ); - $this->assertEquals($properties, $metadata->getPropertiesMetadata()); + $this->assertEquals($properties, $metadata->getPropertyMetadataCollection()); } public function testMergeParentMetadata() @@ -43,7 +43,7 @@ public function testMergeParentMetadata() self::CLASSNAME => new PropertyMetadata(self::CLASSNAME), ); - $this->assertEquals($properties, $metadata->getPropertiesMetadata()); + $this->assertEquals($properties, $metadata->getPropertyMetadataCollection()); } public function testWriteMetadataToCache() @@ -69,7 +69,7 @@ public function testWriteMetadataToCache() $cacheItem->expects($this->once()) ->method('set') ->will($this->returnCallback(function ($metadata) use ($properties) { - $this->assertEquals($properties, $metadata->getPropertiesMetadata()); + $this->assertEquals($properties, $metadata->getPropertyMetadataCollection()); })); $cache->expects($this->once()) @@ -80,7 +80,7 @@ public function testWriteMetadataToCache() $metadata = $factory->getMetadataFor(self::PARENTCLASS); $this->assertEquals(self::PARENTCLASS, $metadata->getName()); - $this->assertEquals($properties, $metadata->getPropertiesMetadata()); + $this->assertEquals($properties, $metadata->getPropertyMetadataCollection()); } public function testReadMetadataFromCache() From 1a35d4538c9304f969813c93af28e3b5f28b2186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sun, 3 Jul 2016 13:29:59 +0200 Subject: [PATCH 14/18] Minor rebase fixes --- .../PropertyAccess/PropertyAccessor.php | 24 ++++++++++--------- .../Tests/PropertyAccessorCollectionTest.php | 4 ++-- .../Tests/PropertyAccessorTest.php | 8 +++---- .../Component/PropertyAccess/composer.json | 2 +- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index 3b9fc3ed2aafe..a6ab9c54c7206 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -11,7 +11,6 @@ namespace Symfony\Component\PropertyAccess; -use Doctrine\Common\Annotations\AnnotationReader; use Psr\Cache\CacheItemPoolInterface; use Psr\Log\LoggerInterface; use Symfony\Component\Cache\Adapter\AdapterInterface; @@ -24,6 +23,7 @@ use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException; use Symfony\Component\PropertyAccess\Mapping\Factory\MetadataFactoryInterface; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; /** * Default implementation of {@link PropertyAccessorInterface}. @@ -168,7 +168,7 @@ class PropertyAccessor implements PropertyAccessorInterface * @param CacheItemPoolInterface $cacheItemPool * @param ClassMetadataFactoryInterface $classMetadataFactory */ - public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, MetadataFactoryInterface $classMetadataFactory = null) + public function __construct($magicCall = false, $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, MetadataFactoryInterface $classMetadataFactory = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; @@ -555,6 +555,7 @@ private function getReadAccessInfo($class, $property) } } + /** @var $metadata */ $metadata = null; $access = array(); @@ -563,7 +564,7 @@ private function getReadAccessInfo($class, $property) $access[self::ACCESS_HAS_PROPERTY] = $hasProperty; if ($this->classMetadataFactory) { - $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertiesMetadata(); + $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertyMetadataCollection(); $metadata = isset($metadata[$property]) ? $metadata[$property] : null; } @@ -754,7 +755,7 @@ private function getWriteAccessInfo($class, $property, $value) $done = false; if ($this->classMetadataFactory) { - $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertiesMetadata(); + $metadata = $this->classMetadataFactory->getMetadataFor($class)->getPropertyMetadataCollection(); $metadata = isset($metadata[$property]) ? $metadata[$property] : null; if ($metadata) { @@ -773,11 +774,11 @@ private function getWriteAccessInfo($class, $property, $value) if (!$done) { $camelized = $this->camelize($property); - $singulars = (array) Inflector::singularize($camelized); + $singulars = (array)Inflector::singularize($camelized); 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]; @@ -786,7 +787,7 @@ private function getWriteAccessInfo($class, $property, $value) } if (!isset($access[self::ACCESS_TYPE])) { - $setter = 'set'.$camelized; + $setter = 'set' . $camelized; $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) if ($this->isMethodAccessible($reflClass, $setter, 1)) { @@ -808,8 +809,8 @@ private function getWriteAccessInfo($class, $property, $value) } 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, '. + '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, @@ -819,11 +820,11 @@ private function getWriteAccessInfo($class, $property, $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()", '. + '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.'()", '; + return '"add' . $singular . '()"/"remove' . $singular . '()", '; }, $singulars)), $setter, $getsetter, @@ -831,6 +832,7 @@ private function getWriteAccessInfo($class, $property, $value) ); } } + } if (isset($item)) { $this->cacheItemPool->save($item->set($access)); diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php index 7fdb2e82f1fcc..54f7f6642b912 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorCollectionTest.php @@ -207,7 +207,7 @@ public function testSetValueCallsCustomAdderAndRemoverForCollections() $car = new PropertyAccessorCollectionTest_Car($axesBefore); AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->propertyAccessor->setValue($car, 'customAxes', $axesMerged); @@ -229,7 +229,7 @@ public function testSetValueCallsCustomAdderAndRemoverForCollectionsMethodAnnota $car = new PropertyAccessorCollectionTest_Car($axesBefore); AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $this->propertyAccessor->setValue($car, 'customVirtualAxes', $axesMerged); diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index bea99f9e010bd..c96402b8c81ce 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -205,14 +205,14 @@ public function testGetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p public function testGetWithCustomGetter() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $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, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $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')); } @@ -319,7 +319,7 @@ public function testSetValueThrowsExceptionIfNotObjectOrArray($objectOrArray, $p public function testSetValueWithCustomSetter() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $custom = new TestClass('webmozart'); @@ -331,7 +331,7 @@ public function testSetValueWithCustomSetter() public function testSetValueWithCustomSetterMethodAnnotation() { AnnotationRegistry::registerAutoloadNamespace('Symfony\Component\PropertyAccess\Annotation', __DIR__.'/../../../..'); - $this->propertyAccessor = new PropertyAccessor(false, false, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); + $this->propertyAccessor = new PropertyAccessor(false, false, null, new LazyLoadingMetadataFactory(new AnnotationLoader(new AnnotationReader()))); $custom = new TestClass('webmozart', 10, 20); diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index b97870aae6894..d1480d6b4c685 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -25,7 +25,7 @@ "doctrine/annotations": "~1.2", "symfony/cache": "~3.1", "symfony/config": "~2.8|~3.0", - "symfony/yaml": "~2.8|~3.0", + "symfony/yaml": "~2.8|~3.0" }, "suggest": { "psr/cache-implementation": "To cache access methods." From 1e29523a430e54e86fcd49c67d2be02eae98da9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sun, 3 Jul 2016 17:22:10 +0200 Subject: [PATCH 15/18] Addressed @xabbuh and @fabpot comments --- .../FrameworkExtension.php | 19 +++++++------------ .../Fixtures/yml/property_accessor.yml | 2 +- .../Component/PropertyAccess/CHANGELOG.md | 2 +- .../Factory/LazyLoadingMetadataFactory.php | 6 +++--- .../Factory/MetadataFactoryInterface.php | 2 -- .../Mapping/Loader/AnnotationLoader.php | 6 ------ .../Mapping/Loader/LoaderChain.php | 3 --- 7 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 17bd58d3a2d30..9a23fbdf82584 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -25,6 +25,8 @@ use Symfony\Component\Finder\Finder; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\Config\FileLocator; +use Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader; +use Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\Serializer\Mapping\Factory\CacheClassMetadataFactory; use Symfony\Component\Serializer\Normalizer\DataUriNormalizer; @@ -915,13 +917,6 @@ private function registerAnnotationsConfiguration(array $config, ContainerBuilde } } - /** - * Loads the PropertyAccess configuration. - * - * @param array $config A serializer configuration array - * @param ContainerBuilder $container A ContainerBuilder instance - * @param XmlFileLoader $loader An XmlFileLoader instance - */ private function registerPropertyAccessConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) { $loader->load('property_access.xml'); @@ -937,7 +932,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $serializerLoaders = array(); if (isset($config['enable_annotations']) && $config['enable_annotations']) { $annotationLoader = new Definition( - \Symfony\Component\PropertyAccess\Mapping\Loader\AnnotationLoader::class, + AnnotationLoader::class, array(new Reference('annotation_reader')) ); $annotationLoader->setPublic(false); @@ -951,7 +946,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $dirname = dirname($reflection->getFileName()); if (is_file($file = $dirname.'/Resources/config/property_access.xml')) { - $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader::class, array(realpath($file))); + $definition = new Definition(XmlFileLoader::class, array(realpath($file))); $definition->setPublic(false); $serializerLoaders[] = $definition; @@ -959,7 +954,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui } if (is_file($file = $dirname.'/Resources/config/property_access.yml')) { - $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader::class, array(realpath($file))); + $definition = new Definition(YamlFileLoader::class, array(realpath($file))); $definition->setPublic(false); $serializerLoaders[] = $definition; @@ -968,13 +963,13 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui if (is_dir($dir = $dirname.'/Resources/config/property_access')) { foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader::class, array($file->getRealpath())); + $definition = new Definition(XmlFileLoader::class, array($file->getRealpath())); $definition->setPublic(false); $serializerLoaders[] = $definition; } foreach (Finder::create()->files()->in($dir)->name('*.yml') as $file) { - $definition = new Definition(\Symfony\Component\PropertyAccess\Mapping\Loader\YamlFileLoader::class, array($file->getRealpath())); + $definition = new Definition(YamlFileLoader::class, array($file->getRealpath())); $definition->setPublic(false); $serializerLoaders[] = $definition; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml index 155060fe6b924..b4f69d7febeb1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/property_accessor.yml @@ -2,4 +2,4 @@ framework: property_access: magic_call: true throw_exception_on_invalid_index: true - enable_annotations: true + enable_annotations: false diff --git a/src/Symfony/Component/PropertyAccess/CHANGELOG.md b/src/Symfony/Component/PropertyAccess/CHANGELOG.md index c7aead7bfe00a..b109322ea70be 100644 --- a/src/Symfony/Component/PropertyAccess/CHANGELOG.md +++ b/src/Symfony/Component/PropertyAccess/CHANGELOG.md @@ -1,7 +1,7 @@ CHANGELOG ========= -3.1.0 +3.2.0 ------ * added custom method calling for properties. diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php index 551b0f9e6b2ac..57f4f8f93ccf3 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/LazyLoadingMetadataFactory.php @@ -43,21 +43,21 @@ class LazyLoadingMetadataFactory implements MetadataFactoryInterface * * @var LoaderInterface|null */ - protected $loader; + private $loader; /** * The cache for caching class metadata. * * @var CacheItemPoolInterface|null */ - protected $cache; + private $cache; /** * The loaded metadata, indexed by class name. * * @var ClassMetadata[] */ - protected $loadedClasses = array(); + private $loadedClasses = array(); /** * Creates a new metadata factory. diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php b/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php index 3ba28c5a525a4..a62d9c1faa691 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Factory/MetadataFactoryInterface.php @@ -17,8 +17,6 @@ /** * Returns {@link \Symfony\Component\PropertyAccess\Mapping\MetadataInterface} instances for values. * - * @since 3.1 - * * @author Luis Ramón López */ interface MetadataFactoryInterface diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index a1264b4908277..920f59709fb81 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -28,14 +28,8 @@ */ class AnnotationLoader implements LoaderInterface { - /** - * @var Reader - */ private $reader; - /** - * @param Reader $reader - */ public function __construct(Reader $reader) { $this->reader = $reader; diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php index b9707bbb98547..3759661b5fa0b 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/LoaderChain.php @@ -27,9 +27,6 @@ */ class LoaderChain implements LoaderInterface { - /** - * @var LoaderInterface[] - */ private $loaders; /** From 7c6a79cb9c4e22fc770dfae9114b43e4b69da220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sun, 3 Jul 2016 17:23:41 +0200 Subject: [PATCH 16/18] Make fabbot.io happy :) --- .../Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php index 920f59709fb81..7d855508187b6 100644 --- a/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/PropertyAccess/Mapping/Loader/AnnotationLoader.php @@ -68,7 +68,6 @@ public function loadClassMetadata(ClassMetadata $classMetadata) foreach ($reflectionClass->getMethods() as $method) { if ($method->getDeclaringClass()->name === $className) { - foreach ($this->reader->getMethodAnnotations($method) as $annotation) { if ($annotation instanceof PropertyGetter) { if (!isset($propertiesMetadata[$annotation->property])) { From d0bd777c95a2709c25e9e069e2537d13098a7406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Sun, 3 Jul 2016 18:05:54 +0200 Subject: [PATCH 17/18] Fixed class name clash in FrameworkExtension --- .../DependencyInjection/FrameworkExtension.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 9a23fbdf82584..2723374f9f13b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -20,6 +20,7 @@ use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\PropertyAccess\Mapping\Loader\XmlFileLoader as PropertyAccessXmlFileLoader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Finder\Finder; @@ -946,7 +947,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui $dirname = dirname($reflection->getFileName()); if (is_file($file = $dirname.'/Resources/config/property_access.xml')) { - $definition = new Definition(XmlFileLoader::class, array(realpath($file))); + $definition = new Definition(PropertyAccessXmlFileLoader::class, array(realpath($file))); $definition->setPublic(false); $serializerLoaders[] = $definition; @@ -963,7 +964,7 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui if (is_dir($dir = $dirname.'/Resources/config/property_access')) { foreach (Finder::create()->files()->in($dir)->name('*.xml') as $file) { - $definition = new Definition(XmlFileLoader::class, array($file->getRealpath())); + $definition = new Definition(PropertyAccessXmlFileLoader::class, array($file->getRealpath())); $definition->setPublic(false); $serializerLoaders[] = $definition; From 6b7eeff0953eafcf9e48c67fd1553d68bfc97d6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luis=20Ram=C3=B3n=20L=C3=B3pez?= Date: Mon, 4 Jul 2016 09:00:28 +0200 Subject: [PATCH 18/18] Revert unintended change in composer.json --- src/Symfony/Component/PropertyAccess/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/PropertyAccess/composer.json b/src/Symfony/Component/PropertyAccess/composer.json index d1480d6b4c685..6b594d2c65263 100644 --- a/src/Symfony/Component/PropertyAccess/composer.json +++ b/src/Symfony/Component/PropertyAccess/composer.json @@ -39,7 +39,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-master": "3.2-dev" } } }