From 469b8e7017730f11303c5786b58c38bcbe6a0c66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 25 Feb 2015 23:42:04 +0100 Subject: [PATCH 01/22] [Serializer] Replace BadMethodCallException by UnexpectedValueException --- .../Serializer/Mapping/Loader/AnnotationLoader.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index 3b25948924..cb59bfb3e1 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Doctrine\Common\Annotations\Reader; use Symfony\Component\Serializer\Annotation\Groups; +use Symfony\Component\Serializer\Exception\MappingException; use Symfony\Component\Serializer\Mapping\ClassMetadata; /** @@ -45,7 +46,7 @@ class AnnotationLoader implements LoaderInterface $loaded = false; foreach ($reflClass->getProperties() as $property) { - if ($property->getDeclaringClass()->name == $className) { + if ($property->getDeclaringClass()->name === $className) { foreach ($this->reader->getPropertyAnnotations($property) as $groups) { if ($groups instanceof Groups) { foreach ($groups->getGroups() as $group) { @@ -59,7 +60,7 @@ class AnnotationLoader implements LoaderInterface } foreach ($reflClass->getMethods() as $method) { - if ($method->getDeclaringClass()->name == $className) { + if ($method->getDeclaringClass()->name === $className) { foreach ($this->reader->getMethodAnnotations($method) as $groups) { if ($groups instanceof Groups) { if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) { @@ -67,7 +68,7 @@ class AnnotationLoader implements LoaderInterface $metadata->addAttributeGroup(lcfirst($matches[2]), $group); } } else { - throw new \BadMethodCallException(sprintf('Groups on "%s::%s" cannot be added. Groups can only be added on methods beginning with "get" or "is".', $className, $method->name)); + throw new MappingException(sprintf('Groups on "%s::%s" cannot be added. Groups can only be added on methods beginning with "get" or "is".', $className, $method->name)); } } From 213cabcf9531e261de239a0666a4506b35a953de Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 26 Feb 2015 10:25:20 +0100 Subject: [PATCH 02/22] [travis] test with php nightly --- .travis.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index a97b93102e..9d1eb930c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,10 @@ matrix: env: deps=low - php: 5.6 env: deps=high + - php: nightly - php: hhvm-nightly allow_failures: + - php: nightly - php: hhvm-nightly fast_finish: true @@ -26,12 +28,12 @@ env: before_install: - travis_retry sudo apt-get install parallel - composer self-update - - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then phpenv config-rm xdebug.ini; fi; - - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; - - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; - - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then pecl install -f memcached-2.1.0; fi; - - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; - - if [ "$TRAVIS_PHP_VERSION" != "hhvm-nightly" ]; then php -i; fi; + - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then phpenv config-rm xdebug.ini; fi; + - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then echo "extension = mongo.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; + - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]] && [ $(php -r "echo PHP_MINOR_VERSION;") -le 4 ]; then echo "extension = apc.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; + - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then pecl install -f memcached-2.1.0; fi; + - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then echo "extension = memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini; fi; + - if [[ "$TRAVIS_PHP_VERSION" != *"nightly" ]]; then php -i; fi; - sudo locale-gen fr_FR.UTF-8 && sudo update-locale # Set the COMPOSER_ROOT_VERSION to the right version according to the branch being built - if [ "$TRAVIS_BRANCH" = "master" ]; then export COMPOSER_ROOT_VERSION=dev-master; else export COMPOSER_ROOT_VERSION="$TRAVIS_BRANCH".x-dev; fi; From 96896390d722f7da9935131145f9cb9e2ffd76df Mon Sep 17 00:00:00 2001 From: ogizanagi Date: Fri, 27 Feb 2015 01:42:10 +0100 Subject: [PATCH 03/22] [TwigBridge] Bootstrap Layout - Fix the label of checkbox cannot be empty --- .../Twig/Resources/views/Form/bootstrap_3_layout.html.twig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index f82979cd62..8aa5ceabc9 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -164,12 +164,12 @@ {% if parent_label_class is defined %} {% set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' ' ~ parent_label_class)|trim}) %} {% endif %} - {% if label is empty %} + {% if label is not sameas(false) and label is empty %} {% set label = name|humanize %} {% endif %} {{ widget|raw }} - {{ label|trans({}, translation_domain) }} + {{ label is not sameas(false) ? label|trans({}, translation_domain) }} {% endif %} {% endblock checkbox_radio_label %} From c41346baef6da05ee198a4db25f40c01662258c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 1 Mar 2015 18:38:38 +0100 Subject: [PATCH 04/22] [Serializer] Optimize GetSetMethodNormalizer and PropertyNormalizer --- .../Component/Serializer/Normalizer/GetSetMethodNormalizer.php | 2 +- .../Component/Serializer/Normalizer/PropertyNormalizer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index 348ab78bb2..b3111eaa18 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -68,7 +68,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer } $attributeValue = $method->invoke($object); - if (array_key_exists($attributeName, $this->callbacks)) { + if (isset($this->callbacks[$attributeName])) { $attributeValue = call_user_func($this->callbacks[$attributeName], $attributeValue); } if (null !== $attributeValue && !is_scalar($attributeValue)) { diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index b54187abb2..5acadde2e7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -64,7 +64,7 @@ class PropertyNormalizer extends AbstractNormalizer $attributeValue = $property->getValue($object); - if (array_key_exists($property->name, $this->callbacks)) { + if (isset($this->callbacks[$property->name])) { $attributeValue = call_user_func($this->callbacks[$property->name], $attributeValue); } if (null !== $attributeValue && !is_scalar($attributeValue)) { From c6bf1dec2e2a18fdf4d0a6a83e7e07ba36989629 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 26 Feb 2015 09:06:05 +0100 Subject: [PATCH 05/22] [Serializer] rename exception interface --- UPGRADE-2.7.md | 3 +++ src/Symfony/Component/Serializer/CHANGELOG.md | 1 + .../Serializer/Exception/Exception.php | 2 +- .../Exception/ExceptionInterface.php | 21 +++++++++++++++++++ .../Exception/InvalidArgumentException.php | 2 +- .../Serializer/Exception/LogicException.php | 2 +- .../Serializer/Exception/RuntimeException.php | 2 +- .../Exception/UnexpectedValueException.php | 2 +- 8 files changed, 30 insertions(+), 5 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Exception/ExceptionInterface.php diff --git a/UPGRADE-2.7.md b/UPGRADE-2.7.md index 137773d647..60ad352fa6 100644 --- a/UPGRADE-2.7.md +++ b/UPGRADE-2.7.md @@ -84,6 +84,9 @@ Serializer $normalizer = new GetSetMethodNormalizer(null, $nameConverter); ``` + * `Symfony\Component\Serializer\Exception\ExceptionInterface` is the new name for the now + deprecated `Symfony\Component\Serializer\Exception\Exception` interface. + PropertyAccess -------------- diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 64be9caced..93645e8f57 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -14,6 +14,7 @@ CHANGELOG * [DEPRECATION] `GetSetMethodNormalizer::setCamelizedAttributes()` and `PropertyNormalizer::setCamelizedAttributes()` are replaced by `CamelCaseToSnakeCaseNameConverter` + * [DEPRECATION] the `Exception` interface has been renamed to `ExceptionInterface` 2.6.0 ----- diff --git a/src/Symfony/Component/Serializer/Exception/Exception.php b/src/Symfony/Component/Serializer/Exception/Exception.php index dc2b8fd1c3..fc0606e2ff 100644 --- a/src/Symfony/Component/Serializer/Exception/Exception.php +++ b/src/Symfony/Component/Serializer/Exception/Exception.php @@ -14,7 +14,7 @@ namespace Symfony\Component\Serializer\Exception; /** * Base exception. * - * @author Johannes M. Schmitt + * @deprecated since version 2.7, to be removed in 3.0. Use ExceptionInterface instead. */ interface Exception { diff --git a/src/Symfony/Component/Serializer/Exception/ExceptionInterface.php b/src/Symfony/Component/Serializer/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..ff67edbb02 --- /dev/null +++ b/src/Symfony/Component/Serializer/Exception/ExceptionInterface.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\Serializer\Exception; + +/** + * Base exception interface. + * + * @author Johannes M. Schmitt + */ +interface ExceptionInterface extends Exception +{ +} diff --git a/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php b/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php index 8cad373a1c..f4e645c448 100644 --- a/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php +++ b/src/Symfony/Component/Serializer/Exception/InvalidArgumentException.php @@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception; * * @author Johannes M. Schmitt */ -class InvalidArgumentException extends \InvalidArgumentException implements Exception +class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } diff --git a/src/Symfony/Component/Serializer/Exception/LogicException.php b/src/Symfony/Component/Serializer/Exception/LogicException.php index a372bf9471..08c53e0ff7 100644 --- a/src/Symfony/Component/Serializer/Exception/LogicException.php +++ b/src/Symfony/Component/Serializer/Exception/LogicException.php @@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception; * * @author Lukas Kahwe Smith */ -class LogicException extends \LogicException implements Exception +class LogicException extends \LogicException implements ExceptionInterface { } diff --git a/src/Symfony/Component/Serializer/Exception/RuntimeException.php b/src/Symfony/Component/Serializer/Exception/RuntimeException.php index 0272865489..a287d97ad4 100644 --- a/src/Symfony/Component/Serializer/Exception/RuntimeException.php +++ b/src/Symfony/Component/Serializer/Exception/RuntimeException.php @@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception; * * @author Johannes M. Schmitt */ -class RuntimeException extends \RuntimeException implements Exception +class RuntimeException extends \RuntimeException implements ExceptionInterface { } diff --git a/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php b/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php index 86cf204038..2a63c5bd3f 100644 --- a/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php +++ b/src/Symfony/Component/Serializer/Exception/UnexpectedValueException.php @@ -16,6 +16,6 @@ namespace Symfony\Component\Serializer\Exception; * * @author Lukas Kahwe Smith */ -class UnexpectedValueException extends \UnexpectedValueException implements Exception +class UnexpectedValueException extends \UnexpectedValueException implements ExceptionInterface { } From 7fd6ba06b18d2f128ee724ba8bc1876b84d0ef7c Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 18 Feb 2015 08:05:44 +0100 Subject: [PATCH 06/22] CS fixes --- .../DependencyInjection/FrameworkExtension.php | 2 +- src/Symfony/Component/DomCrawler/Crawler.php | 5 +++-- src/Symfony/Component/Form/FormEvents.php | 4 ++-- .../PreAuthenticatedAuthenticationProvider.php | 2 +- .../Security/Tests/Acl/Voter/AclVoterTest.php | 12 ++++++------ .../Tests/Http/Firewall/RememberMeListenerTest.php | 8 ++++---- .../Validator/Mapping/Loader/AbstractLoader.php | 14 +++++++------- 7 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 56220995dc..127e7f9c84 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -226,7 +226,7 @@ class FrameworkExtension extends Extension 'memcached' => 'Symfony\Component\HttpKernel\Profiler\MemcachedProfilerStorage', 'redis' => 'Symfony\Component\HttpKernel\Profiler\RedisProfilerStorage', ); - list($class, ) = explode(':', $config['dsn'], 2); + list($class) = explode(':', $config['dsn'], 2); if (!isset($supported[$class])) { throw new \LogicException(sprintf('Driver "%s" is not supported for the profiler.', $class)); } diff --git a/src/Symfony/Component/DomCrawler/Crawler.php b/src/Symfony/Component/DomCrawler/Crawler.php index 1fe5df87cf..7fe313e118 100644 --- a/src/Symfony/Component/DomCrawler/Crawler.php +++ b/src/Symfony/Component/DomCrawler/Crawler.php @@ -35,9 +35,10 @@ class Crawler extends \SplObjectStorage /** * Constructor. * - * @param mixed $node A Node to use as the base for the crawling + * @param mixed $node A Node to use as the base for the crawling * @param string $currentUri The current URI - * @param string $baseHref The base href value + * @param string $baseHref The base href value + * * @api */ public function __construct($node = null, $currentUri = null, $baseHref = null) diff --git a/src/Symfony/Component/Form/FormEvents.php b/src/Symfony/Component/Form/FormEvents.php index c48dff3880..d8c0510c28 100644 --- a/src/Symfony/Component/Form/FormEvents.php +++ b/src/Symfony/Component/Form/FormEvents.php @@ -12,10 +12,10 @@ namespace Symfony\Component\Form; /** * To learn more about how form events work check the documentation - * entry at {@link http://symfony.com/doc/any/components/form/form_events.html} + * entry at {@link http://symfony.com/doc/any/components/form/form_events.html}. * * To learn how to dynamically modify forms using events check the cookbook - * entry at {@link http://symfony.com/doc/any/cookbook/form/dynamic_form_modification.html} + * entry at {@link http://symfony.com/doc/any/cookbook/form/dynamic_form_modification.html}. * * @author Bernhard Schussek */ diff --git a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php index a73c244787..87b7114750 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php +++ b/src/Symfony/Component/Security/Core/Authentication/Provider/PreAuthenticatedAuthenticationProvider.php @@ -60,7 +60,7 @@ class PreAuthenticatedAuthenticationProvider implements AuthenticationProviderIn throw new BadCredentialsException('No pre-authenticated principal found in request.'); } - $user = $this->userProvider->loadUserByUsername($user); + $user = $this->userProvider->loadUserByUsername($user); $this->userChecker->checkPostAuth($user); diff --git a/src/Symfony/Component/Security/Tests/Acl/Voter/AclVoterTest.php b/src/Symfony/Component/Security/Tests/Acl/Voter/AclVoterTest.php index 57f9d1bc73..8544b19078 100644 --- a/src/Symfony/Component/Security/Tests/Acl/Voter/AclVoterTest.php +++ b/src/Symfony/Component/Security/Tests/Acl/Voter/AclVoterTest.php @@ -27,7 +27,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase */ public function testSupportsAttribute($attribute, $supported) { - list($voter, , $permissionMap, ,) = $this->getVoter(true, false); + list($voter, , $permissionMap) = $this->getVoter(true, false); $permissionMap ->expects($this->once()) @@ -44,7 +44,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase */ public function testSupportsAttributeNonString($attribute) { - list($voter, , , , ,) = $this->getVoter(true, false); + list($voter) = $this->getVoter(true, false); $this->assertFalse($voter->supportsAttribute($attribute)); } @@ -72,7 +72,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase */ public function testSupportsClass($class) { - list($voter, , , ,) = $this->getVoter(); + list($voter) = $this->getVoter(); $this->assertTrue($voter->supportsClass($class)); } @@ -88,7 +88,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase public function testVote() { - list($voter, , $permissionMap, ,) = $this->getVoter(); + list($voter, , $permissionMap) = $this->getVoter(); $permissionMap ->expects($this->atLeastOnce()) ->method('getMasks') @@ -103,7 +103,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase */ public function testVoteWhenNoObjectIsPassed($allowIfObjectIdentityUnavailable) { - list($voter, , $permissionMap, ,) = $this->getVoter($allowIfObjectIdentityUnavailable); + list($voter, , $permissionMap) = $this->getVoter($allowIfObjectIdentityUnavailable); $permissionMap ->expects($this->once()) ->method('getMasks') @@ -124,7 +124,7 @@ class AclVoterTest extends \PHPUnit_Framework_TestCase */ public function testVoteWhenOidStrategyReturnsNull($allowIfUnavailable) { - list($voter, , $permissionMap, $oidStrategy,) = $this->getVoter($allowIfUnavailable); + list($voter, , $permissionMap, $oidStrategy) = $this->getVoter($allowIfUnavailable); $permissionMap ->expects($this->once()) ->method('getMasks') diff --git a/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php index 3624f11781..067cacb6cb 100644 --- a/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php +++ b/src/Symfony/Component/Security/Tests/Http/Firewall/RememberMeListenerTest.php @@ -19,7 +19,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase { public function testOnCoreSecurityDoesNotTryToPopulateNonEmptySecurityContext() { - list($listener, $context, $service, ,) = $this->getListener(); + list($listener, $context, $service) = $this->getListener(); $context ->expects($this->once()) @@ -37,7 +37,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase public function testOnCoreSecurityDoesNothingWhenNoCookieIsSet() { - list($listener, $context, $service, ,) = $this->getListener(); + list($listener, $context, $service) = $this->getListener(); $context ->expects($this->once()) @@ -63,7 +63,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase public function testOnCoreSecurityIgnoresAuthenticationExceptionThrownByAuthenticationManagerImplementation() { - list($listener, $context, $service, $manager,) = $this->getListener(); + list($listener, $context, $service, $manager) = $this->getListener(); $context ->expects($this->once()) @@ -101,7 +101,7 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase public function testOnCoreSecurity() { - list($listener, $context, $service, $manager,) = $this->getListener(); + list($listener, $context, $service, $manager) = $this->getListener(); $context ->expects($this->once()) diff --git a/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php b/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php index a69752d6df..6b77e6a3f7 100644 --- a/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php +++ b/src/Symfony/Component/Validator/Mapping/Loader/AbstractLoader.php @@ -58,13 +58,13 @@ abstract class AbstractLoader implements LoaderInterface /** * Creates a new constraint instance for the given constraint name. * - * @param string $name The constraint name. Either a constraint relative - * to the default constraint namespace, or a fully - * qualified class name. Alternatively, the constraint - * may be preceded by a namespace alias and a colon. - * The namespace alias must have been defined using - * {@link addNamespaceAlias()}. - * @param mixed $options The constraint options + * @param string $name The constraint name. Either a constraint relative + * to the default constraint namespace, or a fully + * qualified class name. Alternatively, the constraint + * may be preceded by a namespace alias and a colon. + * The namespace alias must have been defined using + * {@link addNamespaceAlias()}. + * @param mixed $options The constraint options * * @return Constraint * From 0dba547bc8a4d012645ca1466c39ffdae1def205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 2 Mar 2015 23:06:27 +0100 Subject: [PATCH 07/22] [Serializer] Fix ClassMetadataFactory PHPDoc --- .../Serializer/Mapping/Factory/ClassMetadataFactory.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php index c462dd8666..7939015d51 100644 --- a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php +++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php @@ -59,9 +59,10 @@ class ClassMetadataFactory * configuration. At last, the new object is returned. * * @param string|object $value + * * @return ClassMetadata + * * @throws \InvalidArgumentException - */ public function getMetadataFor($value) { @@ -111,6 +112,7 @@ class ClassMetadataFactory * Checks if class has metadata. * * @param mixed $value + * * @return bool */ public function hasMetadataFor($value) @@ -123,7 +125,8 @@ class ClassMetadataFactory /** * Gets a class name for a given class or instance. * - * @param $value + * @param mixed $value + * * @return string|bool */ private function getClass($value) From 8aef213246f203267622d7f4f616809108885526 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Mar 2015 21:53:03 +0100 Subject: [PATCH 08/22] [travis] Tests Security sub-components --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d1eb930c5..2049fa50f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,8 @@ install: - if [ "$deps" = "no" ]; then composer --prefer-source install; fi; script: - - if [ "$deps" = "no" ]; then ls -d src/Symfony/*/* | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - components=`find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -print0 | sed 's#\\(.*\\)/.*#\\1#'` + - if [ "$deps" = "no" ]; then echo $components | parallel -0 --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; - if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; phpunit --group tty || (echo -e "\\e[41mKO\\e[0m tty group" && $(exit 1)); fi; - - if [ "$deps" = "high" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; - - if [ "$deps" = "low" ]; then find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist | sed 's#\(.*\)/.*#\1#' | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - if [ "$deps" = "high" ]; then echo $components | parallel -0 --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - if [ "$deps" = "low" ]; then echo $components | parallel -0 --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; From 4509d36991bad58a5ceddc64c151c09ca622769d Mon Sep 17 00:00:00 2001 From: Dusan Kasan Date: Sat, 28 Feb 2015 16:09:17 +0100 Subject: [PATCH 09/22] [Twig] bootstrap_3_layout.html.twig is usable as a trait --- .../Twig/Resources/views/Form/bootstrap_3_layout.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig index f82979cd62..ed265d297a 100644 --- a/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig +++ b/src/Symfony/Bridge/Twig/Resources/views/Form/bootstrap_3_layout.html.twig @@ -1,4 +1,4 @@ -{% extends "form_div_layout.html.twig" %} +{% use "form_div_layout.html.twig" %} {# Widgets #} From df0fe308966da62d7dd8ee7042a7baed6592e4ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 1 Mar 2015 12:31:43 +0100 Subject: [PATCH 10/22] [Serializer] Test that normalizers ignore non-existing attributes. --- .../Tests/Normalizer/GetSetMethodNormalizerTest.php | 8 ++++++++ .../Tests/Normalizer/PropertyNormalizerTest.php | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index bdfbfa0629..b7be3b6fc8 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -426,6 +426,14 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase $this->assertEquals('foo', $obj->getFoo()); $this->assertEquals('bar', $obj->getBar()); } + + public function testDenormalizeNonExistingAttribute() + { + $this->assertEquals( + new PropertyDummy(), + $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy') + ); + } } class GetSetDummy diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php index 68266a7bcd..95e469f147 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/PropertyNormalizerTest.php @@ -333,6 +333,14 @@ class PropertyNormalizerTest extends \PHPUnit_Framework_TestCase $expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy'); $this->assertEquals($expected, $this->normalizer->normalize($obj)); } + + public function testDenormalizeNonExistingAttribute() + { + $this->assertEquals( + new PropertyDummy(), + $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy') + ); + } } class PropertyDummy From 16cdcf2b942b2564fde3bb7d06ecd4f82658aff8 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 2 Mar 2015 21:53:03 +0100 Subject: [PATCH 11/22] [travis] Tests Security sub-components --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2049fa50f4..6c70c3c307 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,8 +42,8 @@ install: - if [ "$deps" = "no" ]; then composer --prefer-source install; fi; script: - - components=`find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -print0 | sed 's#\\(.*\\)/.*#\\1#'` - - if [ "$deps" = "no" ]; then echo $components | parallel -0 --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - components=$(find src/Symfony -mindepth 3 -type f -name phpunit.xml.dist -printf '%h\n') + - if [ "$deps" = "no" ]; then echo "$components" | parallel --gnu --keep-order 'echo -e "\\nRunning {} tests"; phpunit --exclude-group tty,benchmark,intl-data {} || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; - if [ "$deps" = "no" ]; then echo -e "\\nRunning tests requiring tty"; phpunit --group tty || (echo -e "\\e[41mKO\\e[0m tty group" && $(exit 1)); fi; - - if [ "$deps" = "high" ]; then echo $components | parallel -0 --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; - - if [ "$deps" = "low" ]; then echo $components | parallel -0 --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - if [ "$deps" = "high" ]; then echo "$components" | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source update; phpunit --exclude-group tty,benchmark,intl-data,legacy || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; + - if [ "$deps" = "low" ]; then echo "$components" | parallel --gnu --keep-order -j25% 'echo -e "\\nRunning {} tests"; cd {}; composer --prefer-source --prefer-lowest --prefer-stable update; phpunit --exclude-group tty,benchmark,intl-data || (echo -e "\\e[41mKO\\e[0m {}" && $(exit 1));'; fi; From 77f4dbfa1082d6cdf7c8697c7f300ecaab15b40b Mon Sep 17 00:00:00 2001 From: Vadim Kharitonov Date: Tue, 3 Mar 2015 17:59:03 +0300 Subject: [PATCH 12/22] [OptionsResolver] Remove Unused Variable from Foreach Cycles --- src/Symfony/Component/OptionsResolver/OptionsResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index 4d9653c45b..da6e43b678 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -252,7 +252,7 @@ class OptionsResolver implements Options, OptionsResolverInterface throw new AccessException('Options cannot be made required from a lazy option or normalizer.'); } - foreach ((array) $optionNames as $key => $option) { + foreach ((array) $optionNames as $option) { $this->defined[$option] = true; $this->required[$option] = true; } @@ -333,7 +333,7 @@ class OptionsResolver implements Options, OptionsResolverInterface throw new AccessException('Options cannot be defined from a lazy option or normalizer.'); } - foreach ((array) $optionNames as $key => $option) { + foreach ((array) $optionNames as $option) { $this->defined[$option] = true; } From d733a887df44f12155aff4dc403fbd2f0b0109fc Mon Sep 17 00:00:00 2001 From: Philip Frank Date: Mon, 2 Mar 2015 21:56:10 +0100 Subject: [PATCH 13/22] [PropertyAccess] stop overwriting once a reference is reached (3rd) --- .../PropertyAccess/PropertyAccessor.php | 41 +++++++++---------- .../Tests/Fixtures/TestClass.php | 7 ++++ .../Tests/PropertyAccessorTest.php | 8 ++++ 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php index cef1c6e7f5..17f62bcc45 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyAccessor.php +++ b/src/Symfony/Component/PropertyAccess/PropertyAccessor.php @@ -70,7 +70,6 @@ class PropertyAccessor implements PropertyAccessorInterface } $propertyValues = & $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1); - $overwrite = true; // Add the root object to the list array_unshift($propertyValues, array( @@ -81,18 +80,19 @@ class PropertyAccessor implements PropertyAccessorInterface for ($i = count($propertyValues) - 1; $i >= 0; --$i) { $objectOrArray = & $propertyValues[$i][self::VALUE]; - if ($overwrite) { - $property = $propertyPath->getElement($i); + $property = $propertyPath->getElement($i); - if ($propertyPath->isIndex($i)) { - $this->writeIndex($objectOrArray, $property, $value); - } else { - $this->writeProperty($objectOrArray, $property, $value); - } + if ($propertyPath->isIndex($i)) { + $this->writeIndex($objectOrArray, $property, $value); + } else { + $this->writeProperty($objectOrArray, $property, $value); + } + + if ($propertyValues[$i][self::IS_REF]) { + return; } $value = & $objectOrArray; - $overwrite = !$propertyValues[$i][self::IS_REF]; } } @@ -127,7 +127,6 @@ class PropertyAccessor implements PropertyAccessorInterface try { $propertyValues = $this->readPropertiesUntil($objectOrArray, $propertyPath, $propertyPath->getLength() - 1); - $overwrite = true; // Add the root object to the list array_unshift($propertyValues, array( @@ -138,21 +137,21 @@ class PropertyAccessor implements PropertyAccessorInterface for ($i = count($propertyValues) - 1; $i >= 0; --$i) { $objectOrArray = $propertyValues[$i][self::VALUE]; - if ($overwrite) { - $property = $propertyPath->getElement($i); + $property = $propertyPath->getElement($i); - if ($propertyPath->isIndex($i)) { - if (!$objectOrArray instanceof \ArrayAccess && !is_array($objectOrArray)) { - return false; - } - } else { - if (!$this->isPropertyWritable($objectOrArray, $property)) { - return false; - } + if ($propertyPath->isIndex($i)) { + if (!$objectOrArray instanceof \ArrayAccess && !is_array($objectOrArray)) { + return false; + } + } else { + if (!$this->isPropertyWritable($objectOrArray, $property)) { + return false; } } - $overwrite = !$propertyValues[$i][self::IS_REF]; + if ($propertyValues[$i][self::IS_REF]) { + return true; + } } return true; diff --git a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php index 5cd605b164..7b1b927529 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php +++ b/src/Symfony/Component/PropertyAccess/Tests/Fixtures/TestClass.php @@ -25,6 +25,7 @@ class TestClass private $publicAccessorWithMoreRequiredParameters; private $publicIsAccessor; private $publicHasAccessor; + private $publicGetter; public function __construct($value) { @@ -37,6 +38,7 @@ class TestClass $this->publicAccessorWithMoreRequiredParameters = $value; $this->publicIsAccessor = $value; $this->publicHasAccessor = $value; + $this->publicGetter = $value; } public function setPublicAccessor($value) @@ -166,4 +168,9 @@ class TestClass { return 'foobar'; } + + public function getPublicGetter() + { + return $this->publicGetter; + } } diff --git a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php index 58d4d74402..0c46467567 100644 --- a/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php +++ b/src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php @@ -419,6 +419,14 @@ class PropertyAccessorTest extends \PHPUnit_Framework_TestCase array(array('index' => array('%!@$§.' => 'Bernhard')), '[index][%!@$§.]', 'Bernhard'), array((object) array('%!@$§' => 'Bernhard'), '%!@$§', 'Bernhard'), array((object) array('property' => (object) array('%!@$§' => 'Bernhard')), 'property.%!@$§', 'Bernhard'), + + // nested objects and arrays + array(array('foo' => new TestClass('bar')), '[foo].publicGetSetter', 'bar'), + array(new TestClass(array('foo' => 'bar')), 'publicGetSetter[foo]', 'bar'), + array(new TestClass(new TestClass('bar')), 'publicGetter.publicGetSetter', 'bar'), + array(new TestClass(array('foo' => new TestClass('bar'))), 'publicGetter[foo].publicGetSetter', 'bar'), + array(new TestClass(new TestClass(new TestClass('bar'))), 'publicGetter.publicGetter.publicGetSetter', 'bar'), + array(new TestClass(array('foo' => array('baz' => new TestClass('bar')))), 'publicGetter[foo][baz].publicGetSetter', 'bar'), ); } From 1ea0f86d9ae0103df75f0be219e0e8e5577aa567 Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Wed, 4 Mar 2015 00:01:44 +0100 Subject: [PATCH 14/22] [WebProfilerBundle] Update ajax calls in toolbar to add the css error class --- .../Resources/views/Profiler/base_js.html.twig | 3 +++ .../Resources/views/Profiler/toolbar.css.twig | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig index 2d0a56e668..c3bf01c342 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig @@ -90,6 +90,9 @@ rows.appendChild(row); var methodCell = document.createElement('td'); + if (request.error) { + methodCell.className = 'sf-ajax-request-error'; + } methodCell.textContent = request.method; row.appendChild(methodCell); diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig index c7f84d5773..109b4dd8cd 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/toolbar.css.twig @@ -315,7 +315,7 @@ text-align: right; } .sf-ajax-request-error { - color: #a33; + color: #a33 !important; } .sf-ajax-request-loading { -webkit-animation: sf-blink .5s ease-in-out infinite; From ec11915895c79b9e0fb403b5714aeb3e541b24c0 Mon Sep 17 00:00:00 2001 From: Ruben Gonzalez Date: Wed, 4 Mar 2015 00:44:54 +0100 Subject: [PATCH 15/22] [Validator] Added missing galician (gl) translations --- .../Resources/translations/validators.gl.xlf | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf index 13c7ffe0d2..c9d9f046a5 100644 --- a/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf +++ b/src/Symfony/Component/Validator/Resources/translations/validators.gl.xlf @@ -278,6 +278,38 @@ This value should not be identical to {{ compared_value_type }} {{ compared_value }}. Este valor non debería ser identico a {{ compared_value_type }} {{ compared_value }}. + + The image ratio is too big ({{ ratio }}). Allowed maximum ratio is {{ max_ratio }}. + A proporción da imaxe é demasiado grande ({{ ratio }}). A proporción máxima permitida é {{ max_ratio }}. + + + The image ratio is too small ({{ ratio }}). Minimum ratio expected is {{ min_ratio }}. + A proporción da é demasiado pequena ({{ ratio }}). A proporción mínima permitida é {{ min_ratio }}. + + + The image is square ({{ width }}x{{ height }}px). Square images are not allowed. + A imaxe é cadrada ({{ width }}x{{ height }}px). As imáxenes cadradas non están permitidas. + + + The image is landscape oriented ({{ width }}x{{ height }}px). Landscape oriented images are not allowed. + A imaxe está orientada horizontalmente ({{ width }}x{{ height }}px). As imáxenes orientadas horizontalmente non están permitidas. + + + The image is portrait oriented ({{ width }}x{{ height }}px). Portrait oriented images are not allowed. + A imaxe está orientada verticalmente ({{ width }}x{{ height }}px). As imáxenes orientadas verticalmente non están permitidas. + + + An empty file is not allowed. + Non está permitido un arquivo baleiro. + + + The host could not be resolved. + Non se puido resolver o host. + + + This value does not match the expected {{ charset }} charset. + A codificación de caracteres para este valor debería ser {{ charset }}. + From 8534505db5b80dafd1577abb876d39c3470e016b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Wed, 25 Feb 2015 23:32:01 +0100 Subject: [PATCH 16/22] [Serializer] Refactoring of metadata --- .../Serializer/Mapping/AttributeMetadata.php | 94 +++++++++++++++++++ .../Mapping/AttributeMetadataInterface.php | 50 ++++++++++ .../Serializer/Mapping/ClassMetadata.php | 66 +++++-------- .../Mapping/ClassMetadataInterface.php | 57 +++++++++++ .../Mapping/Factory/ClassMetadataFactory.php | 56 ++++------- .../Factory/ClassMetadataFactoryInterface.php | 53 +++++++++++ .../Mapping/Loader/AnnotationLoader.php | 37 ++++++-- .../Serializer/Mapping/Loader/FileLoader.php | 11 ++- .../Serializer/Mapping/Loader/LoaderChain.php | 16 ++-- .../Mapping/Loader/LoaderInterface.php | 8 +- .../Mapping/Loader/XmlFileLoader.php | 26 +++-- .../Mapping/Loader/YamlFileLoader.php | 21 +++-- .../Normalizer/AbstractNormalizer.php | 44 +++++++-- .../Normalizer/GetSetMethodNormalizer.php | 4 +- .../Normalizer/PropertyNormalizer.php | 4 +- .../Tests/Mapping/AttributeMetadataTest.php | 57 +++++++++++ .../Tests/Mapping/ClassMetadataTest.php | 65 +++++++++++++ .../Factory/ClassMetadataFactoryTest.php | 13 ++- .../Mapping/Loader/AnnotationLoaderTest.php | 43 +++++---- .../Mapping/Loader/XmlFileLoaderTest.php | 11 +++ .../Mapping/Loader/YamlFileLoaderTest.php | 11 +++ .../Mapping/TestClassMetadataFactory.php | 49 +++++++--- .../CamelCaseToSnakeCaseNameConverterTest.php | 6 ++ .../Tests/Normalizer/CustomNormalizerTest.php | 11 +++ .../Normalizer/GetSetMethodNormalizerTest.php | 6 ++ .../Serializer/Tests/SerializerTest.php | 11 +++ 26 files changed, 666 insertions(+), 164 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php create mode 100644 src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php create mode 100644 src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php create mode 100644 src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactoryInterface.php create mode 100644 src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php create mode 100644 src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php new file mode 100644 index 0000000000..7a1d3db94a --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadata.php @@ -0,0 +1,94 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\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 array + * + * @internal This property is public in order to reduce the size of the + * class' serialized representation. Do not access it. Use + * {@link getGroups()} instead. + */ + public $groups = array(); + + /** + * 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 addGroup($group) + { + if (!in_array($group, $this->groups)) { + $this->groups[] = $group; + } + } + + /** + * {@inheritdoc} + */ + public function getGroups() + { + return $this->groups; + } + + /** + * {@inheritdoc} + */ + public function merge(AttributeMetadataInterface $attributeMetadata) + { + foreach ($attributeMetadata->getGroups() as $group) { + $this->addGroup($group); + } + } + + /** + * Returns the names of the properties that should be serialized. + * + * @return string[] + */ + public function __sleep() + { + return array('name', 'groups'); + } +} diff --git a/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.php new file mode 100644 index 0000000000..0701a58b56 --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/AttributeMetadataInterface.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\Serializer\Mapping; + +/** + * Stores metadata needed for serializing and deserializing attributes. + * + * Primarily, the metadata stores serialization groups. + * + * @author Kévin Dunglas + */ +interface AttributeMetadataInterface +{ + /** + * Gets the attribute name. + * + * @return string + */ + public function getName(); + + /** + * Adds this attribute to the given group. + * + * @param string $group + */ + public function addGroup($group); + + /** + * Gets groups of this attribute. + * + * @return string[] + */ + public function getGroups(); + + /** + * Merges an {@see AttributeMetadataInterface} with in the current one. + * + * @param AttributeMetadataInterface $attributeMetadata + */ + public function merge(AttributeMetadataInterface $attributeMetadata); +} diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php index ff24c3cabc..3794a7451b 100644 --- a/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadata.php @@ -12,31 +12,29 @@ namespace Symfony\Component\Serializer\Mapping; /** - * Stores all metadata needed for serializing objects of specific class. - * - * Primarily, the metadata stores serialization groups. + * {@inheritdoc} * * @author Kévin Dunglas */ -class ClassMetadata +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 getClassName()} instead. + * {@link getName()} instead. */ public $name; /** - * @var array + * @var AttributeMetadataInterface[] * * @internal This property is public in order to reduce the size of the * class' serialized representation. Do not access it. Use - * {@link getGroups()} instead. + * {@link getAttributesMetadata()} instead. */ - public $attributesGroups = array(); + public $attributesMetadata = array(); /** * @var \ReflectionClass @@ -54,66 +52,50 @@ class ClassMetadata } /** - * Returns the name of the backing PHP class. - * - * @return string The name of the backing class. + * {@inheritdoc} */ - public function getClassName() + public function getName() { return $this->name; } /** - * Gets serialization groups. - * - * @return array + * {@inheritdoc} */ - public function getAttributesGroups() + public function addAttributeMetadata(AttributeMetadataInterface $attributeMetadata) { - return $this->attributesGroups; + $this->attributesMetadata[$attributeMetadata->getName()] = $attributeMetadata; } /** - * Adds an attribute to a serialization group - * - * @param string $attribute - * @param string $group - * @throws \InvalidArgumentException + * {@inheritdoc} */ - public function addAttributeGroup($attribute, $group) + public function getAttributesMetadata() { - if (!is_string($attribute) || !is_string($group)) { - throw new \InvalidArgumentException('Arguments must be strings.'); - } - - if (!isset($this->groups[$group]) || !in_array($attribute, $this->attributesGroups[$group])) { - $this->attributesGroups[$group][] = $attribute; - } + return $this->attributesMetadata; } /** - * Merges attributes' groups. - * - * @param ClassMetadata $classMetadata + * {@inheritdoc} */ - public function mergeAttributesGroups(ClassMetadata $classMetadata) + public function merge(ClassMetadataInterface $classMetadata) { - foreach ($classMetadata->getAttributesGroups() as $group => $attributes) { - foreach ($attributes as $attribute) { - $this->addAttributeGroup($attribute, $group); + foreach ($classMetadata->getAttributesMetadata() as $attributeMetadata) { + if (isset($this->attributesMetadata[$attributeMetadata->getName()])) { + $this->attributesMetadata[$attributeMetadata->getName()]->merge($attributeMetadata); + } else { + $this->addAttributeMetadata($attributeMetadata); } } } /** - * Returns a ReflectionClass instance for this class. - * - * @return \ReflectionClass + * {@inheritdoc} */ public function getReflectionClass() { if (!$this->reflClass) { - $this->reflClass = new \ReflectionClass($this->getClassName()); + $this->reflClass = new \ReflectionClass($this->getName()); } return $this->reflClass; @@ -128,7 +110,7 @@ class ClassMetadata { return array( 'name', - 'attributesGroups', + 'attributes', ); } } diff --git a/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.php new file mode 100644 index 0000000000..c967666bd7 --- /dev/null +++ b/src/Symfony/Component/Serializer/Mapping/ClassMetadataInterface.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\Serializer\Mapping; + +/** + * Stores metadata needed for serializing and deserializing objects of specific class. + * + * Primarily, the metadata stores the list of attributes to serialize or deserialize. + * + * @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/Serializer/Mapping/Factory/ClassMetadataFactory.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php index 7939015d51..344d633b26 100644 --- a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php +++ b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactory.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Serializer\Mapping\Factory; use Doctrine\Common\Cache\Cache; +use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Mapping\ClassMetadata; use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface; @@ -20,7 +21,7 @@ use Symfony\Component\Serializer\Mapping\Loader\LoaderInterface; * * @author Kévin Dunglas */ -class ClassMetadataFactory +class ClassMetadataFactory implements ClassMetadataFactoryInterface { /** * @var LoaderInterface @@ -46,29 +47,13 @@ class ClassMetadataFactory } /** - * 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. - * - * @param string|object $value - * - * @return ClassMetadata - * - * @throws \InvalidArgumentException + * {@inheritdoc} */ public function getMetadataFor($value) { $class = $this->getClass($value); if (!$class) { - throw new \InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: %s', gettype($value))); + throw new InvalidArgumentException(sprintf('Cannot create metadata for non-objects. Got: "%s"', gettype($value))); } if (isset($this->loadedClasses[$class])) { @@ -80,40 +65,33 @@ class ClassMetadataFactory } if (!class_exists($class) && !interface_exists($class)) { - throw new \InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class)); + throw new InvalidArgumentException(sprintf('The class or interface "%s" does not exist.', $class)); } - $metadata = new ClassMetadata($class); + $classMetadata = new ClassMetadata($class); + $this->loader->loadClassMetadata($classMetadata); - $reflClass = $metadata->getReflectionClass(); + $reflectionClass = $classMetadata->getReflectionClass(); - // Include groups from the parent class - if ($parent = $reflClass->getParentClass()) { - $metadata->mergeAttributesGroups($this->getMetadataFor($parent->name)); + // Include metadata from the parent class + if ($parent = $reflectionClass->getParentClass()) { + $classMetadata->merge($this->getMetadataFor($parent->name)); } - // Include groups from all implemented interfaces - foreach ($reflClass->getInterfaces() as $interface) { - $metadata->mergeAttributesGroups($this->getMetadataFor($interface->name)); - } - - if ($this->loader) { - $this->loader->loadClassMetadata($metadata); + // Include metadata from all implemented interfaces + foreach ($reflectionClass->getInterfaces() as $interface) { + $classMetadata->merge($this->getMetadataFor($interface->name)); } if ($this->cache) { - $this->cache->save($class, $metadata); + $this->cache->save($class, $classMetadata); } - return $this->loadedClasses[$class] = $metadata; + return $this->loadedClasses[$class] = $classMetadata; } /** - * Checks if class has metadata. - * - * @param mixed $value - * - * @return bool + * {@inheritdoc} */ public function hasMetadataFor($value) { diff --git a/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactoryInterface.php b/src/Symfony/Component/Serializer/Mapping/Factory/ClassMetadataFactoryInterface.php new file mode 100644 index 0000000000..a03298a06b --- /dev/null +++ b/src/Symfony/Component/Serializer/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\Serializer\Mapping\Factory; + +use Symfony\Component\Serializer\Exception\InvalidArgumentException; +use Symfony\Component\Serializer\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 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/Serializer/Mapping/Loader/AnnotationLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php index cb59bfb3e1..2db9b9d3d3 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/AnnotationLoader.php @@ -14,7 +14,8 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Doctrine\Common\Annotations\Reader; use Symfony\Component\Serializer\Annotation\Groups; use Symfony\Component\Serializer\Exception\MappingException; -use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** * Annotation loader. @@ -39,18 +40,25 @@ class AnnotationLoader implements LoaderInterface /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadata $metadata) + public function loadClassMetadata(ClassMetadataInterface $classMetadata) { - $reflClass = $metadata->getReflectionClass(); - $className = $reflClass->name; + $reflectionClass = $classMetadata->getReflectionClass(); + $className = $reflectionClass->name; $loaded = false; - foreach ($reflClass->getProperties() as $property) { - if ($property->getDeclaringClass()->name === $className) { + $attributesMetadata = $classMetadata->getAttributesMetadata(); + + foreach ($reflectionClass->getProperties() as $property) { + if (!isset($attributeMetadata[$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 $groups) { if ($groups instanceof Groups) { foreach ($groups->getGroups() as $group) { - $metadata->addAttributeGroup($property->name, $group); + $attributesMetadata[$property->name]->addGroup($group); } } @@ -59,13 +67,22 @@ class AnnotationLoader implements LoaderInterface } } - foreach ($reflClass->getMethods() as $method) { - if ($method->getDeclaringClass()->name === $className) { + foreach ($reflectionClass->getMethods() as $method) { + if ($method->getDeclaringClass()->name == $className) { foreach ($this->reader->getMethodAnnotations($method) as $groups) { if ($groups instanceof Groups) { if (preg_match('/^(get|is)(.+)$/i', $method->name, $matches)) { + $attributeName = lcfirst($matches[2]); + + if (isset($attributesMetadata[$attributeName])) { + $attributeMetadata = $attributesMetadata[$attributeName]; + } else { + $attributesMetadata[$attributeName] = $attributeMetadata = new AttributeMetadata($attributeName); + $classMetadata->addAttributeMetadata($attributeMetadata); + } + foreach ($groups->getGroups() as $group) { - $metadata->addAttributeGroup(lcfirst($matches[2]), $group); + $attributeMetadata->addGroup($group); } } else { throw new MappingException(sprintf('Groups on "%s::%s" cannot be added. Groups can only be added on methods beginning with "get" or "is".', $className, $method->name)); diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php index a583757ba6..38bb59389a 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/FileLoader.php @@ -13,8 +13,16 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Symfony\Component\Serializer\Exception\MappingException; +/** + * Base class for all file based loaders. + * + * @author Kévin Dunglas + */ abstract class FileLoader implements LoaderInterface { + /** + * @var string + */ protected $file; /** @@ -22,8 +30,7 @@ abstract class FileLoader implements LoaderInterface * * @param string $file The mapping file to load * - * @throws MappingException if the mapping file does not exist - * @throws MappingException if the mapping file is not readable + * @throws MappingException if the mapping file does not exist or is not readable */ public function __construct($file) { diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php index 572d78aeca..8bf1c17da9 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderChain.php @@ -12,24 +12,28 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Symfony\Component\Serializer\Exception\MappingException; -use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** - * Calls multiple LoaderInterface instances in a chain + * Calls multiple {@link LoaderInterface} instances in a chain. * * This class accepts multiple instances of LoaderInterface to be passed to the - * constructor. When loadClassMetadata() is called, the same method is called + * 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 { - protected $loaders; + /** + * @var LoaderInterface[] + */ + private $loaders; /** - * Accepts a list of LoaderInterface instances + * Accepts a list of LoaderInterface instances. * * @param LoaderInterface[] $loaders An array of LoaderInterface instances * @@ -49,7 +53,7 @@ class LoaderChain implements LoaderInterface /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadata $metadata) + public function loadClassMetadata(ClassMetadataInterface $metadata) { $success = false; diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php index d81f11e017..ebf84b6a96 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/LoaderInterface.php @@ -11,10 +11,10 @@ namespace Symfony\Component\Serializer\Mapping\Loader; -use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** - * Loads class metadata. + * Loads {@link ClassMetadataInterface}. * * @author Kévin Dunglas */ @@ -23,9 +23,9 @@ interface LoaderInterface /** * Load class metadata. * - * @param ClassMetadata $metadata A metadata + * @param ClassMetadataInterface $classMetadata A metadata * * @return bool */ - public function loadClassMetadata(ClassMetadata $metadata); + public function loadClassMetadata(ClassMetadataInterface $classMetadata); } diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php index 6e47e99a63..0da2f7d690 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/XmlFileLoader.php @@ -13,7 +13,8 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Serializer\Exception\MappingException; -use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; /** * Loads XML mapping files. @@ -23,7 +24,7 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata; class XmlFileLoader extends FileLoader { /** - * An array of SimpleXMLElement instances. + * An array of {@class \SimpleXMLElement} instances. * * @var \SimpleXMLElement[]|null */ @@ -32,7 +33,7 @@ class XmlFileLoader extends FileLoader /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadata $metadata) + public function loadClassMetadata(ClassMetadataInterface $classMetadata) { if (null === $this->classes) { $this->classes = array(); @@ -43,12 +44,23 @@ class XmlFileLoader extends FileLoader } } - if (isset($this->classes[$metadata->getClassName()])) { - $xml = $this->classes[$metadata->getClassName()]; + $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); + } + foreach ($attribute->group as $group) { - $metadata->addAttributeGroup((string) $attribute['name'], (string) $group); + $attributeMetadata->addGroup((string) $group); } } @@ -59,7 +71,7 @@ class XmlFileLoader extends FileLoader } /** - * Parse a XML File. + * Parses a XML File. * * @param string $file Path of file * diff --git a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php index f72aa5a1d1..1c5d5e82e6 100644 --- a/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php +++ b/src/Symfony/Component/Serializer/Mapping/Loader/YamlFileLoader.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Serializer\Mapping\Loader; use Symfony\Component\Serializer\Exception\MappingException; -use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassMetadataInterface; use Symfony\Component\Yaml\Parser; /** - * YAML File Loader + * YAML File Loader. * * @author Kévin Dunglas */ @@ -34,7 +35,7 @@ class YamlFileLoader extends FileLoader /** * {@inheritdoc} */ - public function loadClassMetadata(ClassMetadata $metadata) + public function loadClassMetadata(ClassMetadataInterface $classMetadata) { if (null === $this->classes) { if (!stream_is_local($this->file)) { @@ -59,14 +60,22 @@ class YamlFileLoader extends FileLoader $this->classes = $classes; } - if (isset($this->classes[$metadata->getClassName()])) { - $yaml = $this->classes[$metadata->getClassName()]; + 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['groups'])) { foreach ($data['groups'] as $group) { - $metadata->addAttributeGroup($attribute, $group); + $attributeMetadata->addGroup($group); } } } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index 9ea4d374bf..dfd21b4644 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -15,7 +15,8 @@ use Symfony\Component\Serializer\Exception\CircularReferenceException; use Symfony\Component\Serializer\Exception\InvalidArgumentException; use Symfony\Component\Serializer\Exception\LogicException; use Symfony\Component\Serializer\Exception\RuntimeException; -use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\Mapping\AttributeMetadataInterface; use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter; use Symfony\Component\Serializer\NameConverter\NameConverterInterface; @@ -26,21 +27,42 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface; */ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements NormalizerInterface, DenormalizerInterface { + /** + * @var int + */ protected $circularReferenceLimit = 1; + /** + * @var callable + */ protected $circularReferenceHandler; + /** + * @var ClassMetadataFactoryInterface|null + */ protected $classMetadataFactory; + /** + * @var NameConverterInterface|null + */ protected $nameConverter; + /** + * @var array + */ protected $callbacks = array(); + /** + * @var array + */ protected $ignoredAttributes = array(); + /** + * @var array + */ protected $camelizedAttributes = array(); /** - * Sets the {@link ClassMetadataFactory} to use. + * Sets the {@link ClassMetadataFactoryInterface} to use. * - * @param ClassMetadataFactory|null $classMetadataFactory + * @param ClassMetadataFactoryInterface|null $classMetadataFactory * @param NameConverterInterface|null $nameConverter */ - public function __construct(ClassMetadataFactory $classMetadataFactory = null, NameConverterInterface $nameConverter = null) + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null) { $this->classMetadataFactory = $classMetadataFactory; $this->nameConverter = $nameConverter; @@ -219,19 +241,21 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N * Gets attributes to normalize using groups. * * @param string|object $classOrObject - * @param array $context - * @return array|bool + * @param array $context + * @param bool $attributesAsString If false, return an array of {@link AttributeMetadataInterface} + * + * @return string[]|AttributeMetadataInterface[]|bool */ - protected function getAllowedAttributes($classOrObject, array $context) + protected function getAllowedAttributes($classOrObject, array $context, $attributesAsString = false) { if (!$this->classMetadataFactory || !isset($context['groups']) || !is_array($context['groups'])) { return false; } $allowedAttributes = array(); - foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesGroups() as $group => $attributes) { - if (in_array($group, $context['groups'])) { - $allowedAttributes = array_merge($allowedAttributes, $attributes); + foreach ($this->classMetadataFactory->getMetadataFor($classOrObject)->getAttributesMetadata() as $attributeMetadata) { + if (count(array_intersect($attributeMetadata->getGroups(), $context['groups']))) { + $allowedAttributes[] = $attributesAsString ? $attributeMetadata->getName() : $attributeMetadata; } } diff --git a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php index b3111eaa18..6ccfb984b5 100644 --- a/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/GetSetMethodNormalizer.php @@ -52,7 +52,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer $reflectionObject = new \ReflectionObject($object); $reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC); - $allowedAttributes = $this->getAllowedAttributes($object, $context); + $allowedAttributes = $this->getAllowedAttributes($object, $context, true); $attributes = array(); foreach ($reflectionMethods as $method) { @@ -97,7 +97,7 @@ class GetSetMethodNormalizer extends AbstractNormalizer */ public function denormalize($data, $class, $format = null, array $context = array()) { - $allowedAttributes = $this->getAllowedAttributes($class, $context); + $allowedAttributes = $this->getAllowedAttributes($class, $context, true); $normalizedData = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); diff --git a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php index 5acadde2e7..970d0661cd 100644 --- a/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/PropertyNormalizer.php @@ -46,7 +46,7 @@ class PropertyNormalizer extends AbstractNormalizer $reflectionObject = new \ReflectionObject($object); $attributes = array(); - $allowedAttributes = $this->getAllowedAttributes($object, $context); + $allowedAttributes = $this->getAllowedAttributes($object, $context, true); foreach ($reflectionObject->getProperties() as $property) { if (in_array($property->name, $this->ignoredAttributes)) { @@ -89,7 +89,7 @@ class PropertyNormalizer extends AbstractNormalizer */ public function denormalize($data, $class, $format = null, array $context = array()) { - $allowedAttributes = $this->getAllowedAttributes($class, $context); + $allowedAttributes = $this->getAllowedAttributes($class, $context, true); $data = $this->prepareForDenormalization($data); $reflectionClass = new \ReflectionClass($class); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.php new file mode 100644 index 0000000000..f22746bc5d --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Mapping/AttributeMetadataTest.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\Serializer\Tests\Mapping; + +use Symfony\Component\Serializer\Mapping\AttributeMetadata; + +/** + * @author Kévin Dunglas + */ +class AttributeMetadataTest extends \PHPUnit_Framework_TestCase +{ + public function testInterface() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface', $attributeMetadata); + } + + public function testGetName() + { + $attributeMetadata = new AttributeMetadata('name'); + $this->assertEquals('name', $attributeMetadata->getName()); + } + + public function testGroups() + { + $attributeMetadata = new AttributeMetadata('group'); + $attributeMetadata->addGroup('a'); + $attributeMetadata->addGroup('a'); + $attributeMetadata->addGroup('b'); + + $this->assertEquals(array('a', 'b'), $attributeMetadata->getGroups()); + } + + public function testMerge() + { + $attributeMetadata1 = new AttributeMetadata('a1'); + $attributeMetadata1->addGroup('a'); + $attributeMetadata1->addGroup('b'); + + $attributeMetadata2 = new AttributeMetadata('a2'); + $attributeMetadata2->addGroup('a'); + $attributeMetadata2->addGroup('c'); + + $attributeMetadata1->merge($attributeMetadata2); + + $this->assertEquals(array('a', 'b', 'c'), $attributeMetadata1->getGroups()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.php new file mode 100644 index 0000000000..90017580ac --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Mapping/ClassMetadataTest.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\Serializer\Tests\Mapping; + +use Symfony\Component\Serializer\Mapping\ClassMetadata; + +/** + * @author Kévin Dunglas + */ +class ClassMetadataTest extends \PHPUnit_Framework_TestCase +{ + public function testInterface() + { + $classMetadata = new ClassMetadata('name'); + $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\ClassMetadataInterface', $classMetadata); + } + + public function testAttributeMetadata() + { + $classMetadata = new ClassMetadata('c'); + + $a1 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface'); + $a1->method('getName')->willReturn('a1'); + + $a2 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface'); + $a2->method('getName')->willReturn('a2'); + + $classMetadata->addAttributeMetadata($a1); + $classMetadata->addAttributeMetadata($a2); + + $this->assertEquals(array('a1' => $a1, 'a2' => $a2), $classMetadata->getAttributesMetadata()); + } + + public function testMerge() + { + $classMetadata1 = new ClassMetadata('c1'); + $classMetadata2 = new ClassMetadata('c2'); + + $ac1 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface'); + $ac1->method('getName')->willReturn('a1'); + $ac1->method('getGroups')->willReturn(array('a', 'b')); + + $ac2 = $this->getMock('Symfony\Component\Serializer\Mapping\AttributeMetadataInterface'); + $ac2->method('getName')->willReturn('a1'); + $ac2->method('getGroups')->willReturn(array('b', 'c')); + + $classMetadata1->addAttributeMetadata($ac1); + $classMetadata2->addAttributeMetadata($ac2); + + $classMetadata1->merge($classMetadata2); + + $ac1->method('getGroups')->willReturn('a', 'b', 'c'); + + $this->assertEquals(array('a1' => $ac1), $classMetadata2->getAttributesMetadata()); + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php index 0573f2889a..2e2ba22dce 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Factory/ClassMetadataFactoryTest.php @@ -14,21 +14,26 @@ namespace Symfony\Component\Serializer\Tests\Mapping\Factory; use Doctrine\Common\Annotations\AnnotationReader; use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\Mapping\Loader\LoaderChain; use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; -require_once __DIR__.'/../../../Annotation/Groups.php'; - /** * @author Kévin Dunglas */ class ClassMetadataFactoryTest extends \PHPUnit_Framework_TestCase { + public function testInterface() + { + $classMetadata = new ClassMetadataFactory(new LoaderChain(array())); + $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory', $classMetadata); + } + public function testGetMetadataFor() { $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); - $metadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); + $classMetadata = $factory->getMetadataFor('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); - $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $metadata); + $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true, true), $classMetadata); } public function testHasMetadataFor() diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php index 8a34108cbd..484d062f22 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -16,42 +16,51 @@ use Symfony\Component\Serializer\Mapping\ClassMetadata; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; -require_once __DIR__.'/../../../Annotation/Groups.php'; - /** * @author Kévin Dunglas */ 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\Serializer\Mapping\Loader\LoaderInterface', $this->loader); + } + public function testLoadClassMetadataReturnsTrueIfSuccessful() { - $loader = new AnnotationLoader(new AnnotationReader()); - $metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); + $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); - $this->assertTrue($loader->loadClassMetadata($metadata)); + $this->assertTrue($this->loader->loadClassMetadata($classMetadata)); } public function testLoadClassMetadata() { - $loader = new AnnotationLoader(new AnnotationReader()); - $metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); + $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); + $this->loader->loadClassMetadata($classMetadata); - $loader->loadClassMetadata($metadata); - - $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $metadata); + $this->assertEquals(TestClassMetadataFactory::createClassMetadata(), $classMetadata); } public function testLoadClassMetadataAndMerge() { - $loader = new AnnotationLoader(new AnnotationReader()); - $metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); - $parentMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyParent'); + $classMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); + $parentClassMetadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummyParent'); - $loader->loadClassMetadata($parentMetadata); - $metadata->mergeAttributesGroups($parentMetadata); + $this->loader->loadClassMetadata($parentClassMetadata); + $classMetadata->merge($parentClassMetadata); - $loader->loadClassMetadata($metadata); + $this->loader->loadClassMetadata($classMetadata); - $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $metadata); + $this->assertEquals(TestClassMetadataFactory::createClassMetadata(true), $classMetadata); } } diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php index 35888626e0..418e6b1538 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/XmlFileLoaderTest.php @@ -20,7 +20,13 @@ use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; */ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase { + /** + * @var XmlFileLoader + */ private $loader; + /** + * @var ClassMetadata + */ private $metadata; public function setUp() @@ -29,6 +35,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); } + public function testInterface() + { + $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader); + } + public function testLoadClassMetadataReturnsTrueIfSuccessful() { $this->assertTrue($this->loader->loadClassMetadata($this->metadata)); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php index ec6434cf1d..37c9bcbdf9 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/Loader/YamlFileLoaderTest.php @@ -20,7 +20,13 @@ use Symfony\Component\Serializer\Tests\Mapping\TestClassMetadataFactory; */ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase { + /** + * @var YamlFileLoader + */ private $loader; + /** + * @var ClassMetadata + */ private $metadata; public function setUp() @@ -29,6 +35,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase $this->metadata = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); } + public function testInterface() + { + $this->assertInstanceOf('Symfony\Component\Serializer\Mapping\Loader\LoaderInterface', $this->loader); + } + public function testLoadClassMetadataReturnsTrueIfSuccessful() { $this->assertTrue($this->loader->loadClassMetadata($this->metadata)); diff --git a/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php b/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php index c253e72eaa..07b38c024b 100644 --- a/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php +++ b/src/Symfony/Component/Serializer/Tests/Mapping/TestClassMetadataFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Serializer\Tests\Mapping; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; use Symfony\Component\Serializer\Mapping\ClassMetadata; /** @@ -22,22 +23,38 @@ class TestClassMetadataFactory { $expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); + $foo = new AttributeMetadata('foo'); + $foo->addGroup('a'); + $expected->addAttributeMetadata($foo); + + $bar = new AttributeMetadata('bar'); + $bar->addGroup('b'); + $bar->addGroup('c'); + $expected->addAttributeMetadata($bar); + + $fooBar = new AttributeMetadata('fooBar'); + $fooBar->addGroup('a'); + $fooBar->addGroup('b'); + $expected->addAttributeMetadata($fooBar); + + $symfony = new AttributeMetadata('symfony'); + $expected->addAttributeMetadata($symfony); + if ($withParent) { - $expected->addAttributeGroup('kevin', 'a'); - $expected->addAttributeGroup('coopTilleuls', 'a'); - $expected->addAttributeGroup('coopTilleuls', 'b'); + $kevin = new AttributeMetadata('kevin'); + $kevin->addGroup('a'); + $expected->addAttributeMetadata($kevin); + + $coopTilleuls = new AttributeMetadata('coopTilleuls'); + $coopTilleuls->addGroup('a'); + $coopTilleuls->addGroup('b'); + $expected->addAttributeMetadata($coopTilleuls); } if ($withInterface) { - $expected->addAttributeGroup('symfony', 'a'); + $symfony->addGroup('a'); } - $expected->addAttributeGroup('foo', 'a'); - $expected->addAttributeGroup('bar', 'b'); - $expected->addAttributeGroup('bar', 'c'); - $expected->addAttributeGroup('fooBar', 'a'); - $expected->addAttributeGroup('fooBar', 'b'); - // load reflection class so that the comparison passes $expected->getReflectionClass(); @@ -47,9 +64,15 @@ class TestClassMetadataFactory public static function createXmlCLassMetadata() { $expected = new ClassMetadata('Symfony\Component\Serializer\Tests\Fixtures\GroupDummy'); - $expected->addAttributeGroup('foo', 'group1'); - $expected->addAttributeGroup('foo', 'group2'); - $expected->addAttributeGroup('bar', 'group2'); + + $foo = new AttributeMetadata('foo'); + $foo->addGroup('group1'); + $foo->addGroup('group2'); + $expected->addAttributeMetadata($foo); + + $bar = new AttributeMetadata('bar'); + $bar->addGroup('group2'); + $expected->addAttributeMetadata($bar); return $expected; } diff --git a/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php b/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php index 7d677181b3..2d57017340 100644 --- a/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php +++ b/src/Symfony/Component/Serializer/Tests/NameConverter/CamelCaseToSnakeCaseNameConverterTest.php @@ -18,6 +18,12 @@ use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter */ class CamelCaseToSnakeCaseNameConverterTest extends \PHPUnit_Framework_TestCase { + public function testInterface() + { + $attributeMetadata = new CamelCaseToSnakeCaseNameConverter(); + $this->assertInstanceOf('Symfony\Component\Serializer\NameConverter\NameConverterInterface', $attributeMetadata); + } + /** * @dataProvider attributeProvider */ diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php index 0aeaba472a..86ae003120 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/CustomNormalizerTest.php @@ -17,12 +17,23 @@ use Symfony\Component\Serializer\Serializer; class CustomNormalizerTest extends \PHPUnit_Framework_TestCase { + /** + * @var CustomNormalizer + */ + private $normalizer; + protected function setUp() { $this->normalizer = new CustomNormalizer(); $this->normalizer->setSerializer(new Serializer()); } + public function testInterface() + { + $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $this->normalizer); + $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $this->normalizer); + } + public function testSerialize() { $obj = new ScalarDummy(); diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php index b7be3b6fc8..82854fa0a7 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/GetSetMethodNormalizerTest.php @@ -40,6 +40,12 @@ class GetSetMethodNormalizerTest extends \PHPUnit_Framework_TestCase $this->normalizer->setSerializer($this->serializer); } + public function testInterface() + { + $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $this->normalizer); + $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $this->normalizer); + } + public function testNormalize() { $obj = new GetSetDummy(); diff --git a/src/Symfony/Component/Serializer/Tests/SerializerTest.php b/src/Symfony/Component/Serializer/Tests/SerializerTest.php index 3a6b1ae4c5..68f70fcfe5 100644 --- a/src/Symfony/Component/Serializer/Tests/SerializerTest.php +++ b/src/Symfony/Component/Serializer/Tests/SerializerTest.php @@ -22,6 +22,17 @@ use Symfony\Component\Serializer\Tests\Normalizer\TestDenormalizer; class SerializerTest extends \PHPUnit_Framework_TestCase { + public function testInterface() + { + $serializer = new Serializer(); + + $this->assertInstanceOf('Symfony\Component\Serializer\SerializerInterface', $serializer); + $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\NormalizerInterface', $serializer); + $this->assertInstanceOf('Symfony\Component\Serializer\Normalizer\DenormalizerInterface', $serializer); + $this->assertInstanceOf('Symfony\Component\Serializer\Encoder\EncoderInterface', $serializer); + $this->assertInstanceOf('Symfony\Component\Serializer\Encoder\DecoderInterface', $serializer); + } + /** * @expectedException \Symfony\Component\Serializer\Exception\UnexpectedValueException */ From 0050bbb3450aacf58625a333735f8e5d2637e931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Sun, 4 Jan 2015 14:47:41 +0100 Subject: [PATCH 17/22] [Serializer] Introduce ObjectNormalizer --- src/Symfony/Component/Serializer/CHANGELOG.md | 2 + .../Normalizer/ObjectNormalizer.php | 162 ++++++ .../Tests/Normalizer/ObjectNormalizerTest.php | 497 ++++++++++++++++++ .../Component/Serializer/composer.json | 4 +- 4 files changed, 664 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php create mode 100644 src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php diff --git a/src/Symfony/Component/Serializer/CHANGELOG.md b/src/Symfony/Component/Serializer/CHANGELOG.md index 93645e8f57..ceeeeb1e7a 100644 --- a/src/Symfony/Component/Serializer/CHANGELOG.md +++ b/src/Symfony/Component/Serializer/CHANGELOG.md @@ -15,6 +15,8 @@ CHANGELOG `PropertyNormalizer::setCamelizedAttributes()` are replaced by `CamelCaseToSnakeCaseNameConverter` * [DEPRECATION] the `Exception` interface has been renamed to `ExceptionInterface` + * added `ObjectNormalizer` leveraging the `PropertyAccess` component to normalize + objects containing both properties and getters / setters / issers / hassers methods. 2.6.0 ----- diff --git a/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php new file mode 100644 index 0000000000..92628cb531 --- /dev/null +++ b/src/Symfony/Component/Serializer/Normalizer/ObjectNormalizer.php @@ -0,0 +1,162 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Normalizer; + +use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\Serializer\Exception\CircularReferenceException; +use Symfony\Component\Serializer\Exception\LogicException; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; + +/** + * Converts between objects and arrays using the PropertyAccess component. + * + * @author Kévin Dunglas + */ +class ObjectNormalizer extends AbstractNormalizer +{ + /** + * @var PropertyAccessorInterface + */ + protected $propertyAccessor; + + public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyAccessorInterface $propertyAccessor = null) + { + parent::__construct($classMetadataFactory, $nameConverter); + + $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor(); + } + + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return is_object($data); + } + + /** + * {@inheritdoc} + * + * @throws CircularReferenceException + */ + public function normalize($object, $format = null, array $context = array()) + { + if ($this->isCircularReference($object, $context)) { + return $this->handleCircularReference($object); + } + + $data = array(); + $attributes = $this->getAllowedAttributes($object, $context, true); + + // If not using groups, detect manually + if (false === $attributes) { + $attributes = array(); + + // methods + $reflClass = new \ReflectionClass($object); + foreach ($reflClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflMethod) { + if ( + !$reflMethod->isConstructor() && + !$reflMethod->isDestructor() && + 0 === $reflMethod->getNumberOfRequiredParameters() + ) { + $name = $reflMethod->getName(); + + if (strpos($name, 'get') === 0 || strpos($name, 'has') === 0) { + // getters and hassers + $attributes[lcfirst(substr($name, 3))] = true; + } elseif (strpos($name, 'is') === 0) { + // issers + $attributes[lcfirst(substr($name, 2))] = true; + } + } + } + + // properties + foreach ($reflClass->getProperties(\ReflectionProperty::IS_PUBLIC) as $reflProperty) { + $attributes[$reflProperty->getName()] = true; + } + + $attributes = array_keys($attributes); + } + + foreach ($attributes as $attribute) { + if (in_array($attribute, $this->ignoredAttributes)) { + continue; + } + + $attributeValue = $this->propertyAccessor->getValue($object, $attribute); + + if (isset($this->callbacks[$attribute])) { + $attributeValue = call_user_func($this->callbacks[$attribute], $attributeValue); + } + + if (null !== $attributeValue && !is_scalar($attributeValue)) { + if (!$this->serializer instanceof NormalizerInterface) { + throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attribute)); + } + + $attributeValue = $this->serializer->normalize($attributeValue, $format, $context); + } + + if ($this->nameConverter) { + $attribute = $this->nameConverter->normalize($attribute); + } + + $data[$attribute] = $attributeValue; + } + + return $data; + } + + /** + * {@inheritdoc} + */ + public function supportsDenormalization($data, $type, $format = null) + { + return class_exists($type); + } + + /** + * {@inheritdoc} + */ + public function denormalize($data, $class, $format = null, array $context = array()) + { + $allowedAttributes = $this->getAllowedAttributes($class, $context, true); + $normalizedData = $this->prepareForDenormalization($data); + + $reflectionClass = new \ReflectionClass($class); + $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes); + + foreach ($normalizedData as $attribute => $value) { + $allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes); + $ignored = in_array($attribute, $this->ignoredAttributes); + + if ($allowed && !$ignored) { + if ($this->nameConverter) { + $attribute = $this->nameConverter->normalize($attribute); + } + + try { + $this->propertyAccessor->setValue($object, $attribute, $value); + } catch (NoSuchPropertyException $exception) { + // Properties not found are ignored + } + } + } + + return $object; + } +} diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php new file mode 100644 index 0000000000..e90209f357 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -0,0 +1,497 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\Normalizer; + +use Doctrine\Common\Annotations\AnnotationReader; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Serializer\Normalizer\NormalizerInterface; +use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy; +use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder; +use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; +use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy; + +/** + * @author Kévin Dunglas + */ +class ObjectNormalizerTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var ObjectNormalizerTest + */ + private $normalizer; + /** + * @var SerializerInterface + */ + private $serializer; + + protected function setUp() + { + $this->serializer = $this->getMock(__NAMESPACE__.'\ObjectSerializerNormalizer'); + $this->normalizer = new ObjectNormalizer(); + $this->normalizer->setSerializer($this->serializer); + } + + public function testNormalize() + { + $obj = new ObjectDummy(); + $object = new \stdClass(); + $obj->setFoo('foo'); + $obj->bar = 'bar'; + $obj->setBaz(true); + $obj->setCamelCase('camelcase'); + $obj->setObject($object); + + $this->serializer + ->expects($this->once()) + ->method('normalize') + ->with($object, 'any') + ->will($this->returnValue('string_object')) + ; + + $this->assertEquals( + array( + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => true, + 'fooBar' => 'foobar', + 'camelCase' => 'camelcase', + 'object' => 'string_object', + ), + $this->normalizer->normalize($obj, 'any') + ); + } + + public function testDenormalize() + { + $obj = $this->normalizer->denormalize( + array('foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'), + __NAMESPACE__.'\ObjectDummy', + 'any' + ); + $this->assertEquals('foo', $obj->getFoo()); + $this->assertEquals('bar', $obj->bar); + $this->assertTrue($obj->isBaz()); + } + + public function testDenormalizeWithObject() + { + $data = new \stdClass(); + $data->foo = 'foo'; + $data->bar = 'bar'; + $data->fooBar = 'foobar'; + $obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\ObjectDummy', 'any'); + $this->assertEquals('foo', $obj->getFoo()); + $this->assertEquals('bar', $obj->bar); + } + + public function testLegacyDenormalizeOnCamelCaseFormat() + { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + + $this->normalizer->setCamelizedAttributes(array('camel_case')); + $obj = $this->normalizer->denormalize( + array('camel_case' => 'camelCase'), + __NAMESPACE__.'\ObjectDummy' + ); + $this->assertEquals('camelCase', $obj->getCamelCase()); + } + + public function testDenormalizeNull() + { + $this->assertEquals(new ObjectDummy(), $this->normalizer->denormalize(null, __NAMESPACE__.'\ObjectDummy')); + } + + public function testConstructorDenormalize() + { + $obj = $this->normalizer->denormalize( + array('foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'), + __NAMESPACE__.'\ObjectConstructorDummy', 'any'); + $this->assertEquals('foo', $obj->getFoo()); + $this->assertEquals('bar', $obj->bar); + $this->assertTrue($obj->isBaz()); + } + + public function testConstructorDenormalizeWithMissingOptionalArgument() + { + $obj = $this->normalizer->denormalize( + array('foo' => 'test', 'baz' => array(1, 2, 3)), + __NAMESPACE__.'\ObjectConstructorOptionalArgsDummy', 'any'); + $this->assertEquals('test', $obj->getFoo()); + $this->assertEquals(array(), $obj->bar); + $this->assertEquals(array(1, 2, 3), $obj->getBaz()); + } + + public function testConstructorWithObjectDenormalize() + { + $data = new \stdClass(); + $data->foo = 'foo'; + $data->bar = 'bar'; + $data->baz = true; + $data->fooBar = 'foobar'; + $obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\ObjectConstructorDummy', 'any'); + $this->assertEquals('foo', $obj->getFoo()); + $this->assertEquals('bar', $obj->bar); + } + + public function testGroupsNormalize() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->normalizer = new ObjectNormalizer($classMetadataFactory); + $this->normalizer->setSerializer($this->serializer); + + $obj = new GroupDummy(); + $obj->setFoo('foo'); + $obj->setBar('bar'); + $obj->setFooBar('fooBar'); + $obj->setSymfony('symfony'); + $obj->setKevin('kevin'); + $obj->setCoopTilleuls('coopTilleuls'); + + $this->assertEquals(array( + 'bar' => 'bar', + ), $this->normalizer->normalize($obj, null, array('groups' => array('c')))); + + $this->assertEquals(array( + 'symfony' => 'symfony', + 'foo' => 'foo', + 'fooBar' => 'fooBar', + 'bar' => 'bar', + 'kevin' => 'kevin', + 'coopTilleuls' => 'coopTilleuls', + ), $this->normalizer->normalize($obj, null, array('groups' => array('a', 'c')))); + } + + public function testGroupsDenormalize() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->normalizer = new ObjectNormalizer($classMetadataFactory); + $this->normalizer->setSerializer($this->serializer); + + $obj = new GroupDummy(); + $obj->setFoo('foo'); + + $toNormalize = array('foo' => 'foo', 'bar' => 'bar'); + + $normalized = $this->normalizer->denormalize( + $toNormalize, + 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', + null, + array('groups' => array('a')) + ); + $this->assertEquals($obj, $normalized); + + $obj->setBar('bar'); + + $normalized = $this->normalizer->denormalize( + $toNormalize, + 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', + null, + array('groups' => array('a', 'b')) + ); + $this->assertEquals($obj, $normalized); + } + + /** + * @dataProvider provideCallbacks + */ + public function testCallbacks($callbacks, $value, $result, $message) + { + $this->normalizer->setCallbacks($callbacks); + + $obj = new ObjectConstructorDummy('', $value, true); + + $this->assertEquals( + $result, + $this->normalizer->normalize($obj, 'any'), + $message + ); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testUncallableCallbacks() + { + $this->normalizer->setCallbacks(array('bar' => null)); + + $obj = new ObjectConstructorDummy('baz', 'quux', true); + + $this->normalizer->normalize($obj, 'any'); + } + + public function testIgnoredAttributes() + { + $this->normalizer->setIgnoredAttributes(array('foo', 'bar', 'baz', 'camelCase', 'object')); + + $obj = new ObjectDummy(); + $obj->setFoo('foo'); + $obj->bar = 'bar'; + $obj->setBaz(true); + + $this->assertEquals( + array('fooBar' => 'foobar'), + $this->normalizer->normalize($obj, 'any') + ); + } + + public function provideCallbacks() + { + return array( + array( + array( + 'bar' => function ($bar) { + return 'baz'; + }, + ), + 'baz', + array('foo' => '', 'bar' => 'baz', 'baz' => true), + 'Change a string', + ), + array( + array( + 'bar' => function ($bar) { + return; + }, + ), + 'baz', + array('foo' => '', 'bar' => null, 'baz' => true), + 'Null an item', + ), + array( + array( + 'bar' => function ($bar) { + return $bar->format('d-m-Y H:i:s'); + }, + ), + new \DateTime('2011-09-10 06:30:00'), + array('foo' => '', 'bar' => '10-09-2011 06:30:00', 'baz' => true), + 'Format a date', + ), + array( + array( + 'bar' => function ($bars) { + $foos = ''; + foreach ($bars as $bar) { + $foos .= $bar->getFoo(); + } + + return $foos; + }, + ), + array(new ObjectConstructorDummy('baz', '', false), new ObjectConstructorDummy('quux', '', false)), + array('foo' => '', 'bar' => 'bazquux', 'baz' => true), + 'Collect a property', + ), + array( + array( + 'bar' => function ($bars) { + return count($bars); + }, + ), + array(new ObjectConstructorDummy('baz', '', false), new ObjectConstructorDummy('quux', '', false)), + array('foo' => '', 'bar' => 2, 'baz' => true), + 'Count a property', + ), + ); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\LogicException + * @expectedExceptionMessage Cannot normalize attribute "object" because injected serializer is not a normalizer + */ + public function testUnableToNormalizeObjectAttribute() + { + $serializer = $this->getMock('Symfony\Component\Serializer\SerializerInterface'); + $this->normalizer->setSerializer($serializer); + + $obj = new ObjectDummy(); + $object = new \stdClass(); + $obj->setObject($object); + + $this->normalizer->normalize($obj, 'any'); + } + + /** + * @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException + */ + public function testUnableToNormalizeCircularReference() + { + $serializer = new Serializer(array($this->normalizer)); + $this->normalizer->setSerializer($serializer); + $this->normalizer->setCircularReferenceLimit(2); + + $obj = new CircularReferenceDummy(); + + $this->normalizer->normalize($obj); + } + + public function testSiblingReference() + { + $serializer = new Serializer(array($this->normalizer)); + $this->normalizer->setSerializer($serializer); + + $siblingHolder = new SiblingHolder(); + + $expected = array( + 'sibling0' => array('coopTilleuls' => 'Les-Tilleuls.coop'), + 'sibling1' => array('coopTilleuls' => 'Les-Tilleuls.coop'), + 'sibling2' => array('coopTilleuls' => 'Les-Tilleuls.coop'), + ); + $this->assertEquals($expected, $this->normalizer->normalize($siblingHolder)); + } + + public function testCircularReferenceHandler() + { + $serializer = new Serializer(array($this->normalizer)); + $this->normalizer->setSerializer($serializer); + $this->normalizer->setCircularReferenceHandler(function ($obj) { + return get_class($obj); + }); + + $obj = new CircularReferenceDummy(); + + $expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy'); + $this->assertEquals($expected, $this->normalizer->normalize($obj)); + } + + public function testDenormalizeNonExistingAttribute() + { + $this->assertEquals( + new ObjectDummy(), + $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\ObjectDummy') + ); + } +} + +class ObjectDummy +{ + protected $foo; + public $bar; + private $baz; + protected $camelCase; + protected $object; + + public function getFoo() + { + return $this->foo; + } + + public function setFoo($foo) + { + $this->foo = $foo; + } + + public function isBaz() + { + return $this->baz; + } + + public function setBaz($baz) + { + $this->baz = $baz; + } + + public function getFooBar() + { + return $this->foo.$this->bar; + } + + public function getCamelCase() + { + return $this->camelCase; + } + + public function setCamelCase($camelCase) + { + $this->camelCase = $camelCase; + } + + public function otherMethod() + { + throw new \RuntimeException("Dummy::otherMethod() should not be called"); + } + + public function setObject($object) + { + $this->object = $object; + } + + public function getObject() + { + return $this->object; + } +} + +class ObjectConstructorDummy +{ + protected $foo; + public $bar; + private $baz; + + public function __construct($foo, $bar, $baz) + { + $this->foo = $foo; + $this->bar = $bar; + $this->baz = $baz; + } + + public function getFoo() + { + return $this->foo; + } + + public function isBaz() + { + return $this->baz; + } + + public function otherMethod() + { + throw new \RuntimeException("Dummy::otherMethod() should not be called"); + } +} + +abstract class ObjectSerializerNormalizer implements SerializerInterface, NormalizerInterface +{ +} + +class ObjectConstructorOptionalArgsDummy +{ + protected $foo; + public $bar; + private $baz; + + public function __construct($foo, $bar = array(), $baz = array()) + { + $this->foo = $foo; + $this->bar = $bar; + $this->baz = $baz; + } + + public function getFoo() + { + return $this->foo; + } + + public function getBaz() + { + return $this->baz; + } + + public function otherMethod() + { + throw new \RuntimeException("Dummy::otherMethod() should not be called"); + } +} diff --git a/src/Symfony/Component/Serializer/composer.json b/src/Symfony/Component/Serializer/composer.json index cffa235e03..b88ea796d5 100644 --- a/src/Symfony/Component/Serializer/composer.json +++ b/src/Symfony/Component/Serializer/composer.json @@ -22,6 +22,7 @@ "symfony/phpunit-bridge": "~2.7|~3.0.0", "symfony/yaml": "~2.0|~3.0.0", "symfony/config": "~2.2|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0" }, @@ -29,7 +30,8 @@ "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", "doctrine/cache": "For using the default cached annotation reader and metadata cache.", "symfony/yaml": "For using the default YAML mapping loader.", - "symfony/config": "For using the XML mapping loader." + "symfony/config": "For using the XML mapping loader.", + "symfony/property-access": "For using the ObjectNormalizer." }, "autoload": { "psr-0": { "Symfony\\Component\\Serializer\\": "" } From 32a81f79aa8fd91f2d5fcea5f38bfe60e22401d7 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 6 Mar 2015 17:45:31 +0100 Subject: [PATCH 18/22] [VarDumper] Fix "next element is already occupied" --- .../Component/VarDumper/Cloner/VarCloner.php | 15 ++++--- .../VarDumper/Tests/VarClonerTest.php | 43 +++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 5434d7d18b..dd40512819 100644 --- a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -38,8 +38,9 @@ class VarCloner extends AbstractCloner $maxItems = $this->maxItems; $maxString = $this->maxString; $cookie = (object) array(); // Unique object used to detect hard references + $gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable $a = null; // Array cast for nested structures - $stub = null; // Stub capturing the main properties of an original item value, + $stub = null; // Stub capturing the main properties of an original item value // or null if the original value is used directly $zval = array( // Main properties of the current value 'type' => null, @@ -121,16 +122,20 @@ class VarCloner extends AbstractCloner $stub->value = $zval['array_count'] ?: count($v); $a = $v; - $a[] = null; - $h = count($v); - array_pop($a); + + // Copies of $GLOBALS have very strange behavior, + // let's detect them with some black magic + $a[$gid] = true; // Happens with copies of $GLOBALS - if ($h !== $stub->value) { + if (isset($v[$gid])) { + unset($v[$gid]); $a = array(); foreach ($v as $gk => &$gv) { $a[$gk] =& $gv; } + } else { + $a = $v; } } break; diff --git a/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php b/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php index 0e28ebac06..1bb9410036 100644 --- a/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php +++ b/src/Symfony/Component/VarDumper/Tests/VarClonerTest.php @@ -18,6 +18,49 @@ use Symfony\Component\VarDumper\Cloner\VarCloner; */ class VarClonerTest extends \PHPUnit_Framework_TestCase { + public function testMaxIntBoundary() + { + $data = array(PHP_INT_MAX => 123); + + $cloner = new VarCloner(); + $clone = $cloner->cloneVar($data); + + $expected = << Array + ( + [0] => Array + ( + [0] => Symfony\Component\VarDumper\Cloner\Stub Object + ( + [type] => array + [class] => assoc + [value] => 1 + [cut] => 0 + [handle] => 0 + [refCount] => 0 + [position] => 1 + ) + + ) + + [1] => Array + ( + [%s] => 123 + ) + + ) + + [maxDepth:Symfony\Component\VarDumper\Cloner\Data:private] => 20 + [maxItemsPerDepth:Symfony\Component\VarDumper\Cloner\Data:private] => -1 + [useRefHandles:Symfony\Component\VarDumper\Cloner\Data:private] => -1 +) + +EOTXT; + $this->assertSame(sprintf($expected, PHP_INT_MAX), print_r($clone, true)); + } + public function testClone() { $json = json_decode('{"1":{"var":"val"},"2":{"var":"val"}}'); From 37035b0696cd5990ff6c7d07f986f501828d9237 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 5 Mar 2015 10:42:26 +0100 Subject: [PATCH 19/22] [FrameworkBundle] simplify dep declaration --- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index d5ff3e0d41..d330e54510 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -23,7 +23,7 @@ "symfony/http-foundation": "~2.4.9|~2.5,>=2.5.4", "symfony/http-kernel": "~2.6", "symfony/filesystem": "~2.3", - "symfony/routing": "~2.3.25,>2.3.25|~2.6,>2.6.4", + "symfony/routing": "~2.3.26|~2.6,>=2.6.5", "symfony/security-core": "~2.6", "symfony/security-csrf": "~2.6", "symfony/stopwatch": "~2.3", From f13b5f75830921d2f3a05ee97062929c3517ffdd Mon Sep 17 00:00:00 2001 From: Vladimir Reznichenko Date: Fri, 6 Mar 2015 20:37:36 +0100 Subject: [PATCH 20/22] [2.3] [Config] [Console] [DependencyInjection] [DomCrawler] [Form] [HttpKernel] [PropertyAccess] [Security] [Translation] [Yaml] static code analysis, code cleanup --- .../Definition/Builder/ArrayNodeDefinition.php | 2 +- .../Config/Definition/PrototypedArrayNode.php | 2 +- src/Symfony/Component/Config/Util/XmlUtils.php | 2 +- .../Console/Formatter/OutputFormatterStyle.php | 2 +- .../Console/Tests/Helper/DialogHelperTest.php | 2 +- .../DependencyInjection/Dumper/PhpDumper.php | 6 +++--- .../Tests/ContainerBuilderTest.php | 3 ++- .../Tests/Dumper/PhpDumperTest.php | 4 ++-- .../Component/DomCrawler/Tests/CrawlerTest.php | 2 +- .../Component/Form/AbstractRendererEngine.php | 3 +-- src/Symfony/Component/Form/FormBuilder.php | 3 +-- src/Symfony/Component/Form/FormRenderer.php | 3 +-- .../Component/Form/Test/FormPerformanceTestCase.php | 2 +- .../HttpKernel/EventListener/RouterListener.php | 3 +-- .../Tests/DataCollector/LoggerDataCollectorTest.php | 2 +- .../Component/PropertyAccess/PropertyPathBuilder.php | 3 +-- .../Component/Security/Acl/Dbal/AclProvider.php | 12 +++++++----- .../Tests/Acl/Dbal/AclProviderBenchmarkTest.php | 8 ++++---- .../Security/Tests/Core/Util/SecureRandomTest.php | 8 ++++---- .../Tests/Http/Firewall/ExceptionListenerTest.php | 6 +++--- .../Component/Translation/Loader/XliffFileLoader.php | 2 +- src/Symfony/Component/Yaml/Inline.php | 4 ++-- 22 files changed, 41 insertions(+), 43 deletions(-) diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index 7cf662dbc0..d2226f63f1 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -480,7 +480,7 @@ class ArrayNodeDefinition extends NodeDefinition implements ParentNodeDefinition ); } - if (null !== $this->key && (null === $this->addDefaultChildren || is_integer($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { + if (null !== $this->key && (null === $this->addDefaultChildren || is_int($this->addDefaultChildren) && $this->addDefaultChildren > 0)) { throw new InvalidDefinitionException( sprintf('->addDefaultChildrenIfNoneSet() should set default children names as ->useAttributeAsKey() is used at path "%s"', $path) ); diff --git a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php index febb016e82..2c7a7ab052 100644 --- a/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php +++ b/src/Symfony/Component/Config/Definition/PrototypedArrayNode.php @@ -131,7 +131,7 @@ class PrototypedArrayNode extends ArrayNode if (null === $children) { $this->defaultChildren = array('defaults'); } else { - $this->defaultChildren = is_integer($children) && $children > 0 ? range(1, $children) : (array) $children; + $this->defaultChildren = is_int($children) && $children > 0 ? range(1, $children) : (array) $children; } } diff --git a/src/Symfony/Component/Config/Util/XmlUtils.php b/src/Symfony/Component/Config/Util/XmlUtils.php index 78a16e9ac8..7d66799c08 100644 --- a/src/Symfony/Component/Config/Util/XmlUtils.php +++ b/src/Symfony/Component/Config/Util/XmlUtils.php @@ -215,7 +215,7 @@ class XmlUtils LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, trim($error->message), - $error->file ? $error->file : 'n/a', + $error->file ?: 'n/a', $error->line, $error->column ); diff --git a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php index c414a8232d..48e9850725 100644 --- a/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php +++ b/src/Symfony/Component/Console/Formatter/OutputFormatterStyle.php @@ -149,7 +149,7 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface )); } - if (false === array_search(static::$availableOptions[$option], $this->options)) { + if (!in_array(static::$availableOptions[$option], $this->options)) { $this->options[] = static::$availableOptions[$option]; } } diff --git a/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php index ce44fc773d..8caf6569c6 100644 --- a/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/DialogHelperTest.php @@ -159,7 +159,7 @@ class DialogHelperTest extends \PHPUnit_Framework_TestCase protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); - fputs($stream, $input); + fwrite($stream, $input); rewind($stream); return $stream; diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 3cd728faf0..e152b9af2f 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -1087,7 +1087,7 @@ EOF; $behavior[$id] = $argument->getInvalidBehavior(); } - $calls[$id] += 1; + ++$calls[$id]; } } } @@ -1345,12 +1345,12 @@ EOF; } while ($i > 0) { - $i -= 1; + --$i; $name .= $nonFirstChars[$i%$nonFirstCharsLength]; $i = intval($i/$nonFirstCharsLength); } - $this->variableCount += 1; + ++$this->variableCount; // check that the name is not reserved if (in_array($name, $this->reservedVariables, true)) { diff --git a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php index ac67bab9fe..da37230489 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php @@ -259,7 +259,8 @@ class ContainerBuilderTest extends \PHPUnit_Framework_TestCase $builder->setResourceTracking(false); $builderCompilerPasses = $builder->getCompiler()->getPassConfig()->getPasses(); $builder->addCompilerPass($this->getMock('Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface')); - $this->assertEquals(sizeof($builderCompilerPasses) + 1, sizeof($builder->getCompiler()->getPassConfig()->getPasses())); + + $this->assertCount(count($builder->getCompiler()->getPassConfig()->getPasses()) - 1, $builderCompilerPasses); } /** diff --git a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php index 905e600e78..86c2b72ba2 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php @@ -89,8 +89,8 @@ class PhpDumperTest extends \PHPUnit_Framework_TestCase $container = new ContainerBuilder(); $container->setDefinition('test', $definition); - $container->setParameter('foo', 'wiz'.dirname(dirname(__FILE__))); - $container->setParameter('bar', dirname(__FILE__)); + $container->setParameter('foo', 'wiz'.dirname(__DIR__)); + $container->setParameter('bar', __DIR__); $container->setParameter('baz', '%bar%/PhpDumperTest.php'); $container->setParameter('buz', dirname(dirname(__DIR__))); $container->compile(); diff --git a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php index 5743eebebe..9c57bdef4a 100644 --- a/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php +++ b/src/Symfony/Component/DomCrawler/Tests/CrawlerTest.php @@ -316,7 +316,7 @@ EOF { $crawler = $this->createTestCrawler()->filterXPath('//ul[1]/li'); $nodes = $crawler->reduce(function ($node, $i) { - return $i == 1 ? false : true; + return $i !== 1; }); $this->assertNotSame($nodes, $crawler, '->reduce() returns a new instance of a crawler'); $this->assertInstanceOf('Symfony\\Component\\DomCrawler\\Crawler', $nodes, '->reduce() returns a new instance of a crawler'); diff --git a/src/Symfony/Component/Form/AbstractRendererEngine.php b/src/Symfony/Component/Form/AbstractRendererEngine.php index 00bf85401c..8202cbf97b 100644 --- a/src/Symfony/Component/Form/AbstractRendererEngine.php +++ b/src/Symfony/Component/Form/AbstractRendererEngine.php @@ -67,8 +67,7 @@ abstract class AbstractRendererEngine implements FormRendererEngineInterface // Unset instead of resetting to an empty array, in order to allow // implementations (like TwigRendererEngine) to check whether $cacheKey // is set at all. - unset($this->resources[$cacheKey]); - unset($this->resourceHierarchyLevels[$cacheKey]); + unset($this->resources[$cacheKey], $this->resourceHierarchyLevels[$cacheKey]); } /** diff --git a/src/Symfony/Component/Form/FormBuilder.php b/src/Symfony/Component/Form/FormBuilder.php index 9437dad1c1..8b5b919413 100644 --- a/src/Symfony/Component/Form/FormBuilder.php +++ b/src/Symfony/Component/Form/FormBuilder.php @@ -138,8 +138,7 @@ class FormBuilder extends FormConfigBuilder implements \IteratorAggregate, FormB throw new BadMethodCallException('FormBuilder methods cannot be accessed anymore once the builder is turned into a FormConfigInterface instance.'); } - unset($this->unresolvedChildren[$name]); - unset($this->children[$name]); + unset($this->unresolvedChildren[$name], $this->children[$name]); return $this; } diff --git a/src/Symfony/Component/Form/FormRenderer.php b/src/Symfony/Component/Form/FormRenderer.php index 09b010563f..2c6b1dd0be 100644 --- a/src/Symfony/Component/Form/FormRenderer.php +++ b/src/Symfony/Component/Form/FormRenderer.php @@ -279,8 +279,7 @@ class FormRenderer implements FormRendererInterface // Clear the caches if they were filled for the first time within // this function call if ($hierarchyInit) { - unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey]); - unset($this->hierarchyLevelMap[$viewAndSuffixCacheKey]); + unset($this->blockNameHierarchyMap[$viewAndSuffixCacheKey], $this->hierarchyLevelMap[$viewAndSuffixCacheKey]); } if ($varInit) { diff --git a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php index 3060472975..090eb8bc4f 100644 --- a/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php +++ b/src/Symfony/Component/Form/Test/FormPerformanceTestCase.php @@ -54,7 +54,7 @@ abstract class FormPerformanceTestCase extends FormIntegrationTestCase */ public function setMaxRunningTime($maxRunningTime) { - if (is_integer($maxRunningTime) && $maxRunningTime >= 0) { + if (is_int($maxRunningTime) && $maxRunningTime >= 0) { $this->maxRunningTime = $maxRunningTime; } else { throw new \InvalidArgumentException(); diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index 33d340f27a..2e9cb7b6bb 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -107,8 +107,7 @@ class RouterListener implements EventSubscriberInterface } $request->attributes->add($parameters); - unset($parameters['_route']); - unset($parameters['_controller']); + unset($parameters['_route'], $parameters['_controller']); $request->attributes->set('_route_params', $parameters); } catch (ResourceNotFoundException $e) { $message = sprintf('No route found for "%s %s"', $request->getMethod(), $request->getPathInfo()); diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php index 59401662d3..3420bf2986 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php @@ -32,7 +32,7 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase $this->assertSame('logger', $c->getName()); $this->assertSame($nb, $c->countErrors()); - $this->assertSame($expectedLogs ? $expectedLogs : $logs, $c->getLogs()); + $this->assertSame($expectedLogs ?: $logs, $c->getLogs()); $this->assertSame($expectedDeprecationCount, $c->countDeprecations()); } diff --git a/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php b/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php index 079da283f2..5d7fdac6fd 100644 --- a/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php +++ b/src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php @@ -264,8 +264,7 @@ class PropertyPathBuilder // All remaining elements should be removed for (; $i < $length; ++$i) { - unset($this->elements[$i]); - unset($this->isIndex[$i]); + unset($this->elements[$i], $this->isIndex[$i]); } } else { $diff = $insertionLength - $cutLength; diff --git a/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php b/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php index b53dc751ac..5074614bd3 100644 --- a/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php +++ b/src/Symfony/Component/Security/Acl/Dbal/AclProvider.php @@ -173,7 +173,8 @@ class AclProvider implements AclProviderInterface } // Is it time to load the current batch? - if ((self::MAX_BATCH_SIZE === count($currentBatch) || ($i + 1) === $c) && count($currentBatch) > 0) { + $currentBatchesCount = count($currentBatch); + if ($currentBatchesCount > 0 && (self::MAX_BATCH_SIZE === $currentBatchesCount || ($i + 1) === $c)) { try { $loadedBatch = $this->lookupObjectIdentities($currentBatch, $sids, $oidLookup); } catch (AclNotFoundException $aclNotFoundexception) { @@ -557,10 +558,11 @@ QUERY; // attach ACL to the result set; even though we do not enforce that every // object identity has only one instance, we must make sure to maintain // referential equality with the oids passed to findAcls() - if (!isset($oidCache[$objectIdentifier.$classType])) { - $oidCache[$objectIdentifier.$classType] = $acl->getObjectIdentity(); + $oidCacheKey = $objectIdentifier.$classType; + if (!isset($oidCache[$oidCacheKey])) { + $oidCache[$oidCacheKey] = $acl->getObjectIdentity(); } - $result->attach($oidCache[$objectIdentifier.$classType], $acl); + $result->attach($oidCache[$oidCacheKey], $acl); // so, this hasn't been hydrated yet } else { // create object identity if we haven't done so yet @@ -668,7 +670,7 @@ QUERY; // let's see if we have already hydrated this if (isset($acls[$parentId])) { $aclParentAclProperty->setValue($acl, $acls[$parentId]); - $processed += 1; + ++$processed; continue; } diff --git a/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderBenchmarkTest.php b/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderBenchmarkTest.php index c25a697fb4..1a46d2712c 100644 --- a/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderBenchmarkTest.php +++ b/src/Symfony/Component/Security/Tests/Acl/Dbal/AclProviderBenchmarkTest.php @@ -122,7 +122,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase if ($id === 1000 || ($id < 1500 && rand(0, 1))) { $this->insertClassStmt->execute(array($id, $this->getRandomString(rand(20, 100), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\\_'))); - $id += 1; + ++$id; return $id-1; } else { @@ -148,7 +148,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase } $this->generateAces($classId, $id); - $id += 1; + ++$id; return $id-1; } @@ -163,7 +163,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase $this->getRandomString(rand(5, 30)), rand(0, 1), )); - $id += 1; + ++$id; return $id-1; } else { @@ -215,7 +215,7 @@ class AclProviderBenchmarkTest extends \PHPUnit_Framework_TestCase rand(0, 1), )); - $id += 1; + ++$id; } } diff --git a/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php index bd9fec5b61..a8cc660614 100644 --- a/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php +++ b/src/Symfony/Component/Security/Tests/Core/Util/SecureRandomTest.php @@ -41,7 +41,7 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase for ($j = 1; $j <= 5000; $j++) { $k = 4 * $j - 1; - $c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]] += 1; + ++$c[8 * $b[$k - 3] + 4 * $b[$k - 2] + 2 * $b[$k - 1] + $b[$k]]; } $f = 0; @@ -73,14 +73,14 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase $run = 6; } - $runs[$run] += 1; + ++$runs[$run]; }; $currentRun = 0; $lastBit = null; for ($i = 0; $i < 20000; $i++) { if ($lastBit === $b[$i]) { - $currentRun += 1; + ++$currentRun; } else { if ($currentRun > 0) { $addRun($currentRun); @@ -115,7 +115,7 @@ class SecureRandomTest extends \PHPUnit_Framework_TestCase $lastBit = null; for ($i = 0; $i < 20000; $i++) { if ($lastBit === $b[$i]) { - $currentRun += 1; + ++$currentRun; } else { if ($currentRun > $longestRun) { $longestRun = $currentRun; diff --git a/src/Symfony/Component/Security/Tests/Http/Firewall/ExceptionListenerTest.php b/src/Symfony/Component/Security/Tests/Http/Firewall/ExceptionListenerTest.php index b1c7622414..12f18a6832 100644 --- a/src/Symfony/Component/Security/Tests/Http/Firewall/ExceptionListenerTest.php +++ b/src/Symfony/Component/Security/Tests/Http/Firewall/ExceptionListenerTest.php @@ -177,9 +177,9 @@ class ExceptionListenerTest extends \PHPUnit_Framework_TestCase private function createExceptionListener(SecurityContextInterface $context = null, AuthenticationTrustResolverInterface $trustResolver = null, HttpUtils $httpUtils = null, AuthenticationEntryPointInterface $authenticationEntryPoint = null, $errorPage = null, AccessDeniedHandlerInterface $accessDeniedHandler = null) { return new ExceptionListener( - $context ? $context : $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'), - $trustResolver ? $trustResolver : $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface'), - $httpUtils ? $httpUtils : $this->getMock('Symfony\Component\Security\Http\HttpUtils'), + $context ?: $this->getMock('Symfony\Component\Security\Core\SecurityContextInterface'), + $trustResolver ?: $this->getMock('Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface'), + $httpUtils ?: $this->getMock('Symfony\Component\Security\Http\HttpUtils'), 'key', $authenticationEntryPoint, $errorPage, diff --git a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php index 0950a46d2e..79723f3830 100644 --- a/src/Symfony/Component/Translation/Loader/XliffFileLoader.php +++ b/src/Symfony/Component/Translation/Loader/XliffFileLoader.php @@ -137,7 +137,7 @@ class XliffFileLoader implements LoaderInterface LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', $error->code, trim($error->message), - $error->file ? $error->file : 'n/a', + $error->file ?: 'n/a', $error->line, $error->column ); diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 3aa248b2f4..ab7c72e405 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -273,7 +273,7 @@ class Inline { $output = array(); $len = strlen($sequence); - $i += 1; + ++$i; // [foo, bar, ...] while ($i < $len) { @@ -332,7 +332,7 @@ class Inline { $output = array(); $len = strlen($mapping); - $i += 1; + ++$i; // {foo: bar, bar:foo, ...} while ($i < $len) { From e64b75f7d3b313a07e27315930ff64853fb3effc Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Mar 2015 08:38:33 +0100 Subject: [PATCH 21/22] replaced the last remaining is_integer() call --- .../FrameworkBundle/DependencyInjection/Configuration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index d4ef0f968b..2de60489a8 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -233,7 +233,7 @@ class Configuration implements ConfigurationInterface ); foreach ($urls as $i => $url) { - if (is_integer($i)) { + if (is_int($i)) { if (0 === strpos($url, 'https://') || 0 === strpos($url, '//')) { $urls['http'][] = $urls['ssl'][] = $url; } else { From 1530c355d5c51cc012f471ee1723b3532f54248b Mon Sep 17 00:00:00 2001 From: Vladimir Reznichenko Date: Fri, 6 Mar 2015 21:37:14 +0100 Subject: [PATCH 22/22] Php Inspections (EA Extended): squash all PR-13813 commits --- .../Console/Tests/Helper/QuestionHelperTest.php | 2 +- .../Component/OptionsResolver/OptionsResolver.php | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php index e5499fc086..0ab0849371 100644 --- a/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/QuestionHelperTest.php @@ -208,7 +208,7 @@ class QuestionHelperTest extends \PHPUnit_Framework_TestCase protected function getInputStream($input) { $stream = fopen('php://memory', 'r+', false); - fputs($stream, $input); + fwrite($stream, $input); rewind($stream); return $stream; diff --git a/src/Symfony/Component/OptionsResolver/OptionsResolver.php b/src/Symfony/Component/OptionsResolver/OptionsResolver.php index da6e43b678..4965e3156b 100644 --- a/src/Symfony/Component/OptionsResolver/OptionsResolver.php +++ b/src/Symfony/Component/OptionsResolver/OptionsResolver.php @@ -670,14 +670,8 @@ class OptionsResolver implements Options, OptionsResolverInterface } foreach ((array) $optionNames as $option) { - unset($this->defined[$option]); - unset($this->defaults[$option]); - unset($this->required[$option]); - unset($this->resolved[$option]); - unset($this->lazy[$option]); - unset($this->normalizers[$option]); - unset($this->allowedTypes[$option]); - unset($this->allowedValues[$option]); + unset($this->defined[$option], $this->defaults[$option], $this->required[$option], $this->resolved[$option]); + unset($this->lazy[$option], $this->normalizers[$option], $this->allowedTypes[$option], $this->allowedValues[$option]); } return $this;