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" } } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy