From 39718b14f61b9495f98c93fad57a500f21ef2c93 Mon Sep 17 00:00:00 2001 From: David Badura Date: Fri, 4 Aug 2017 14:11:36 +0200 Subject: [PATCH] add naming strategy for property accessor --- .../DependencyInjection/Configuration.php | 1 + .../FrameworkExtension.php | 4 + .../Resources/config/schema/symfony-1.0.xsd | 1 + .../Fixtures/php/property_accessor.php | 1 + .../Fixtures/xml/property_accessor.xml | 2 +- .../Fixtures/yml/property_accessor.yml | 1 + .../FrameworkExtensionTest.php | 1 + .../NamingStrategy/CamelCaseStrategy.php | 65 ++++++ .../NamingStrategyInterface.php | 48 ++++ .../PropertyAccess/PropertyAccessor.php | 212 +++++++++--------- .../PropertyAccessorBuilder.php | 25 ++- .../NamingStrategy/CamelCaseStrategyTest.php | 128 +++++++++++ .../Tests/PropertyAccessorBuilderTest.php | 9 + .../Tests/PropertyAccessorTest.php | 17 ++ 14 files changed, 403 insertions(+), 112 deletions(-) create mode 100644 src/Symfony/Component/PropertyAccess/NamingStrategy/CamelCaseStrategy.php create mode 100644 src/Symfony/Component/PropertyAccess/NamingStrategy/NamingStrategyInterface.php create mode 100644 src/Symfony/Component/PropertyAccess/Tests/NamingStrategy/CamelCaseStrategyTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 998bd98789273..7cb7db37c8da3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -722,6 +722,7 @@ private function addPropertyAccessSection(ArrayNodeDefinition $rootNode) ->children() ->booleanNode('magic_call')->defaultFalse()->end() ->booleanNode('throw_exception_on_invalid_index')->defaultFalse()->end() + ->scalarNode('naming_strategy')->cannotBeEmpty()->end() ->end() ->end() ->end() diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 337e1f7f491cd..18e20d9d6561b 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -1113,6 +1113,10 @@ private function registerPropertyAccessConfiguration(array $config, ContainerBui ->replaceArgument(0, $config['magic_call']) ->replaceArgument(1, $config['throw_exception_on_invalid_index']) ; + + if (isset($config['naming_strategy'])) { + $container->getDefinition('property_accessor')->addArgument(new Reference($config['naming_strategy'])); + } } private function registerSecurityCsrfConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader) 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 abdf9955791a5..ae59ec17b81af 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 @@ -217,6 +217,7 @@ + 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..ddffd729c6f73 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, + 'naming_strategy' => 'my_naming_strategy', ), )); diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml index d7db78a6e2e38..177765a15416e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/property_accessor.xml @@ -7,6 +7,6 @@ http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd"> - + 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..cd46af14ab814 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 + naming_strategy: my_naming_strategy diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php index 3335c5cad5afe..17be9595d4daa 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php @@ -78,6 +78,7 @@ public function testPropertyAccessWithOverriddenValues() $def = $container->getDefinition('property_accessor'); $this->assertTrue($def->getArgument(0)); $this->assertTrue($def->getArgument(1)); + $this->assertEquals(new Reference('my_naming_strategy'), $def->getArgument(3)); } public function testPropertyAccessCache() diff --git a/src/Symfony/Component/PropertyAccess/NamingStrategy/CamelCaseStrategy.php b/src/Symfony/Component/PropertyAccess/NamingStrategy/CamelCaseStrategy.php new file mode 100644 index 0000000000000..d64d46ed80259 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/NamingStrategy/CamelCaseStrategy.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\NamingStrategy; + +use Symfony\Component\Inflector\Inflector; + +/** + * @author David Badura + */ +class CamelCaseStrategy implements NamingStrategyInterface +{ + /** + * {@inheritdoc} + */ + public function getGetters(string $class, string $property): array + { + $camelProp = self::camelize($property); + + return array( + 'get'.$camelProp, + lcfirst($camelProp), // jQuery style, e.g. read: last(), write: last($item) + 'is'.$camelProp, + 'has'.$camelProp, + ); + } + + /** + * {@inheritdoc} + */ + public function getSetters(string $class, string $property): array + { + $camelized = self::camelize($property); + + return array( + 'set'.$camelized, + lcfirst($camelized), // jQuery style, e.g. read: last(), write: last($item) + ); + } + + /** + * {@inheritdoc} + */ + public function getAddersAndRemovers(string $class, string $property): array + { + $singulars = (array) Inflector::singularize(self::camelize($property)); + + return array_map(function ($singular) { + return array('add'.$singular, 'remove'.$singular); + }, $singulars); + } + + private static function camelize(string $string): string + { + return str_replace(' ', '', ucwords(str_replace('_', ' ', $string))); + } +} diff --git a/src/Symfony/Component/PropertyAccess/NamingStrategy/NamingStrategyInterface.php b/src/Symfony/Component/PropertyAccess/NamingStrategy/NamingStrategyInterface.php new file mode 100644 index 0000000000000..90fd962271164 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/NamingStrategy/NamingStrategyInterface.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\NamingStrategy; + +/** + * @author David Badura + */ +interface NamingStrategyInterface +{ + /** + * Returns all possible getters. + * + * @param string $class + * @param string $property + * + * @return array + */ + public function getGetters(string $class, string $property): array; + + /** + * Returns all possible setters. + * + * @param string $class + * @param string $property + * + * @return array + */ + public function getSetters(string $class, string $property): array; + + /** + * Returns all possible adders and removers. + * + * @param string $class + * @param string $property + * + * @return array + */ + public function getAddersAndRemovers(string $class, string $property): array; +} diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index a3a4ab053d53c..c0120286284cd 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -1,5 +1,4 @@ null); @@ -71,11 +71,12 @@ class PropertyAccessor implements PropertyAccessorInterface * Should not be used by application code. Use * {@link PropertyAccess::createPropertyAccessor()} instead. */ - public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null) + public function __construct(bool $magicCall = false, bool $throwExceptionOnInvalidIndex = false, CacheItemPoolInterface $cacheItemPool = null, NamingStrategyInterface $namingStrategy = null) { $this->magicCall = $magicCall; $this->ignoreInvalidIndices = !$throwExceptionOnInvalidIndex; $this->cacheItemPool = $cacheItemPool instanceof NullAdapter ? null : $cacheItemPool; // Replace the NullAdapter by the null value + $this->namingStrategy = $namingStrategy ?: new CamelCaseStrategy(); } /** @@ -435,50 +436,47 @@ private function getReadAccessInfo($class, $property) $reflClass = new \ReflectionClass($class); $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property); - $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()) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; - $access[self::ACCESS_NAME] = $getter; - } elseif ($reflClass->hasMethod($getsetter) && $reflClass->getMethod($getsetter)->isPublic()) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; - $access[self::ACCESS_NAME] = $getsetter; - } elseif ($reflClass->hasMethod($isser) && $reflClass->getMethod($isser)->isPublic()) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; - $access[self::ACCESS_NAME] = $isser; - } elseif ($reflClass->hasMethod($hasser) && $reflClass->getMethod($hasser)->isPublic()) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; - $access[self::ACCESS_NAME] = $hasser; - } elseif ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; - $access[self::ACCESS_NAME] = $property; - $access[self::ACCESS_REF] = false; - } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; - $access[self::ACCESS_NAME] = $property; - $access[self::ACCESS_REF] = true; - } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) { - // we call the getter and hope the __call do the job - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; - $access[self::ACCESS_NAME] = $getter; - } else { - $methods = array($getter, $getsetter, $isser, $hasser, '__get'); - if ($this->magicCall) { - $methods[] = '__call'; + + $getters = $this->namingStrategy->getGetters($class, $property); + + foreach ($getters as $getter) { + if ($reflClass->hasMethod($getter) && $reflClass->getMethod($getter)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $getter; + + break; } + } - $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; - $access[self::ACCESS_NAME] = sprintf( - 'Neither the property "%s" nor one of the methods "%s()" '. - 'exist and have public access in class "%s".', - $property, - implode('()", "', $methods), - $reflClass->name - ); + if (!isset($access[self::ACCESS_TYPE])) { + if ($reflClass->hasMethod('__get') && $reflClass->getMethod('__get')->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; + $access[self::ACCESS_NAME] = $property; + $access[self::ACCESS_REF] = false; + } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; + $access[self::ACCESS_NAME] = $property; + $access[self::ACCESS_REF] = true; + } elseif ($this->magicCall && $reflClass->hasMethod('__call') && $reflClass->getMethod('__call')->isPublic()) { + // we call the getter and hope the __call do the job + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; + $access[self::ACCESS_NAME] = $getters[0]; + } else { + $methods = $getters; + $methods[] = '__get'; + if ($this->magicCall) { + $methods[] = '__call'; + } + + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; + $access[self::ACCESS_NAME] = sprintf( + 'Neither the property "%s" nor one of the methods "%s()" '. + 'exist and have public access in class "%s".', + $property, + implode('()", "', $methods), + $reflClass->name + ); + } } if (isset($item)) { @@ -589,7 +587,7 @@ private function writeCollection($zval, $property, $collection, $addMethod, $rem /** * Guesses how to write the property value. * - * @param mixed $value + * @param mixed $value */ private function getWriteAccessInfo(string $class, string $property, $value): array { @@ -610,11 +608,11 @@ private function getWriteAccessInfo(string $class, string $property, $value): ar $reflClass = new \ReflectionClass($class); $access[self::ACCESS_HAS_PROPERTY] = $reflClass->hasProperty($property); - $camelized = $this->camelize($property); - $singulars = (array) Inflector::singularize($camelized); + + $addersAndRemovers = $this->namingStrategy->getAddersAndRemovers($class, $property); if (is_array($value) || $value instanceof \Traversable) { - $methods = $this->findAdderAndRemover($reflClass, $singulars); + $methods = $this->findAdderAndRemover($reflClass, $addersAndRemovers); if (null !== $methods) { $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; @@ -624,49 +622,52 @@ private function getWriteAccessInfo(string $class, string $property, $value): ar } if (!isset($access[self::ACCESS_TYPE])) { - $setter = 'set'.$camelized; - $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) + $setters = $this->namingStrategy->getSetters($class, $property); - 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 - ); + foreach ($setters as $setter) { + if ($this->isMethodAccessible($reflClass, $setter, 1)) { + $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; + $access[self::ACCESS_NAME] = $setter; + + break; + } + } + + if (!isset($access[self::ACCESS_TYPE])) { + if ($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] = $setters[0]; + } elseif (null !== $methods = $this->findAdderAndRemover($reflClass, $addersAndRemovers)) { + $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()", '. + '"__set()" or "__call()" exist and have public access in class "%s".', + $property, + implode('', array_map(function ($adderAndRemover) { + return '"'.$adderAndRemover[0].'()"/"'.$adderAndRemover[1].'()", '; + }, $addersAndRemovers)), + implode('()", "', $setters), + $reflClass->name + ); + } } } @@ -697,35 +698,26 @@ private function isPropertyWritable($object, string $property): bool || self::ACCESS_TYPE_MAGIC === $access[self::ACCESS_TYPE]; } - /** - * Camelizes a given string. - */ - private function camelize(string $string): string - { - return str_replace(' ', '', ucwords(str_replace('_', ' ', $string))); - } - /** * Searches for add and remove methods. * - * @param \ReflectionClass $reflClass The reflection class for the given object - * @param array $singulars The singular form of the property name or null + * @param \ReflectionClass $reflClass The reflection class for the given object + * @param array $addersAndRemovers list of possible adders and removers * * @return array|null An array containing the adder and remover when found, null otherwise */ - private function findAdderAndRemover(\ReflectionClass $reflClass, array $singulars) + private function findAdderAndRemover(\ReflectionClass $reflClass, array $addersAndRemovers): ?array { - foreach ($singulars as $singular) { - $addMethod = 'add'.$singular; - $removeMethod = 'remove'.$singular; - - $addMethodFound = $this->isMethodAccessible($reflClass, $addMethod, 1); - $removeMethodFound = $this->isMethodAccessible($reflClass, $removeMethod, 1); + foreach ($addersAndRemovers as $adderAndRemover) { + $addMethodFound = $this->isMethodAccessible($reflClass, $adderAndRemover[0], 1); + $removeMethodFound = $this->isMethodAccessible($reflClass, $adderAndRemover[1], 1); if ($addMethodFound && $removeMethodFound) { - return array($addMethod, $removeMethod); + return $adderAndRemover; } } + + return null; } /** diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php b/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php index 1db6a1dba23ed..52a65caa3a32a 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessorBuilder.php @@ -12,6 +12,7 @@ namespace Symfony\Component\PropertyAccess; use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\PropertyAccess\NamingStrategy\NamingStrategyInterface; /** * A configurable builder to create a PropertyAccessor. @@ -28,6 +29,11 @@ class PropertyAccessorBuilder */ private $cacheItemPool; + /** + * @var NamingStrategyInterface|null + */ + private $namingStrategy; + /** * Enables the use of "__call" by the PropertyAccessor. * @@ -121,6 +127,18 @@ public function getCacheItemPool() return $this->cacheItemPool; } + public function setNamingStrategy(NamingStrategyInterface $namingStrategy = null): self + { + $this->namingStrategy = $namingStrategy; + + return $this; + } + + public function getNamingStrategy(): ?NamingStrategyInterface + { + return $this->namingStrategy; + } + /** * Builds and returns a new PropertyAccessor object. * @@ -128,6 +146,11 @@ 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->namingStrategy + ); } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/NamingStrategy/CamelCaseStrategyTest.php b/src/Symfony/Component/PropertyAccess/Tests/NamingStrategy/CamelCaseStrategyTest.php new file mode 100644 index 0000000000000..e10a976fca249 --- /dev/null +++ b/src/Symfony/Component/PropertyAccess/Tests/NamingStrategy/CamelCaseStrategyTest.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\PropertyAccess\Tests\NamingStrategy; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\PropertyAccess\NamingStrategy\CamelCaseStrategy; + +class CamelCaseStrategyTest extends TestCase +{ + /** + * @var CamelCaseStrategy + */ + private $namingStrategy; + + protected function setUp() + { + $this->namingStrategy = new CamelCaseStrategy(); + } + + public function getCamelCaseProperty() + { + return array( + array( + 'test', + array('getTest', 'test', 'isTest', 'hasTest'), + array('setTest', 'test'), + array( + array('addTest', 'removeTest'), + ), + ), + array( + 'fooBar', + array('getFooBar', 'fooBar', 'isFooBar', 'hasFooBar'), + array('setFooBar', 'fooBar'), + array( + array('addFooBar', 'removeFooBar'), + ), + ), + array( + 'users', + array('getUsers', 'users', 'isUsers', 'hasUsers'), + array('setUsers', 'users'), + array( + array('addUser', 'removeUser'), + ), + ), + array( + 'userGroups', + array('getUserGroups', 'userGroups', 'isUserGroups', 'hasUserGroups'), + array('setUserGroups', 'userGroups'), + array( + array('addUserGroup', 'removeUserGroup'), + ), + ), + array( + 'circuses', + array('getCircuses', 'circuses', 'isCircuses', 'hasCircuses'), + array('setCircuses', 'circuses'), + array( + array('addCircus', 'removeCircus'), + array('addCircuse', 'removeCircuse'), + array('addCircusis', 'removeCircusis'), + ), + ), + ); + } + + public function getUnderscoreProperty() + { + return array( + array( + 'foo_bar', + array('getFooBar', 'fooBar', 'isFooBar', 'hasFooBar'), + array('setFooBar', 'fooBar'), + array( + array('addFooBar', 'removeFooBar'), + ), + ), + array( + 'user_groups', + array('getUserGroups', 'userGroups', 'isUserGroups', 'hasUserGroups'), + array('setUserGroups', 'userGroups'), + array( + array('addUserGroup', 'removeUserGroup'), + ), + ), + array( + 'super_circuses', + array('getSuperCircuses', 'superCircuses', 'isSuperCircuses', 'hasSuperCircuses'), + array('setSuperCircuses', 'superCircuses'), + array( + array('addSuperCircus', 'removeSuperCircus'), + array('addSuperCircuse', 'removeSuperCircuse'), + array('addSuperCircusis', 'removeSuperCircusis'), + ), + ), + ); + } + + /** + * @dataProvider getCamelCaseProperty + */ + public function testCamelCaseProperty($property, $getters, $setters, $addersAndRemovers) + { + $this->assertSame($getters, $this->namingStrategy->getGetters('SomeClass', $property)); + $this->assertSame($setters, $this->namingStrategy->getSetters('SomeClass', $property)); + $this->assertSame($addersAndRemovers, $this->namingStrategy->getAddersAndRemovers('SomeClass', $property)); + } + + /** + * @dataProvider getUnderscoreProperty + */ + public function testUnderscoreProperty($property, $getters, $setters, $addersAndRemovers) + { + $this->assertSame($getters, $this->namingStrategy->getGetters('SomeClass', $property)); + $this->assertSame($setters, $this->namingStrategy->getSetters('SomeClass', $property)); + $this->assertSame($addersAndRemovers, $this->namingStrategy->getAddersAndRemovers('SomeClass', $property)); + } +} diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php index 63bd64225039a..3da708dd04b3f 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorBuilderTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\PropertyAccess\NamingStrategy\CamelCaseStrategy; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyAccess\PropertyAccessorBuilder; @@ -63,4 +64,12 @@ public function testUseCache() $this->assertEquals($cacheItemPool, $this->builder->getCacheItemPool()); $this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor()); } + + public function testNamingStrategy() + { + $namingStrategy = new CamelCaseStrategy(); + $this->builder->setNamingStrategy($namingStrategy); + $this->assertEquals($namingStrategy, $this->builder->getNamingStrategy()); + $this->assertInstanceOf(PropertyAccessor::class, $this->builder->getPropertyAccessor()); + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index f160be61f325f..a43deeabe817a 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException; +use Symfony\Component\PropertyAccess\NamingStrategy\NamingStrategyInterface; use Symfony\Component\PropertyAccess\PropertyAccessor; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClass; use Symfony\Component\PropertyAccess\Tests\Fixtures\TestClassMagicCall; @@ -568,6 +569,22 @@ public function testCacheReadAccess() $this->assertEquals('baz', $propertyAccessor->getValue($obj, 'publicGetSetter')); } + public function testNamingStrategy() + { + $strategy = $this->getMockBuilder(NamingStrategyInterface::class)->getMock(); + $strategy->method('getGetters')->with(TestClass::class, 'my_path1')->willReturn(array('getPublicAccessor')); + $strategy->method('getSetters')->with(TestClass::class, 'my_path2')->willReturn(array('setPublicAccessor')); + $strategy->method('getAddersAndRemovers')->with(TestClass::class, 'my_path2')->willReturn(array()); + + $obj = new TestClass('foo'); + + $propertyAccessor = new PropertyAccessor(false, false, null, $strategy); + + $this->assertEquals('foo', $propertyAccessor->getValue($obj, 'my_path1')); + $propertyAccessor->setValue($obj, 'my_path2', 'baz'); + $this->assertEquals('baz', $propertyAccessor->getValue($obj, 'my_path1')); + } + /** * @expectedException \Symfony\Component\PropertyAccess\Exception\InvalidArgumentException * @expectedExceptionMessage Expected argument of type "Countable", "string" given