From fdb668e051ce45177cbb6eb7ee6f0e03c248e321 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 7 Apr 2019 14:41:46 +0200 Subject: [PATCH 1/6] prevent mixup of the object to populate --- .../Normalizer/AbstractNormalizer.php | 2 ++ .../Tests/Normalizer/ObjectNormalizerTest.php | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index f99c61a36b..ef1166a493 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -330,6 +330,8 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N return $object; } + // clean up even if no match + unset($context[static::OBJECT_TO_POPULATE]); $constructor = $this->getConstructor($data, $class, $context, $reflectionClass, $allowedAttributes); if ($constructor) { diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 4068479c87..4b72e2a4b5 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -315,6 +315,30 @@ class ObjectNormalizerTest extends TestCase ); } + public function testObjectToPopulateNoMatch() + { + $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); + $this->normalizer = new ObjectNormalizer($classMetadataFactory, null, null, new PhpDocExtractor()); + new Serializer([$this->normalizer]); + + $objectToPopulate = new ObjectInner(); + $objectToPopulate->foo = 'foo'; + + $outer = $this->normalizer->denormalize([ + 'foo' => 'foo', + 'inner' => [ + 'bar' => 'bar', + ], + ], ObjectOuter::class, null, [ObjectNormalizer::OBJECT_TO_POPULATE => $objectToPopulate]); + + $this->assertInstanceOf(ObjectOuter::class, $outer); + $inner = $outer->getInner(); + $this->assertInstanceOf(ObjectInner::class, $inner); + $this->assertNotSame($objectToPopulate, $inner); + $this->assertSame('bar', $inner->bar); + $this->assertNull($inner->foo); + } + /** * @dataProvider provideCallbacks */ @@ -936,6 +960,9 @@ class ObjectOuter { public $foo; public $bar; + /** + * @var ObjectInner + */ private $inner; private $date; From d2f2e56472fa82ac8227cc04788fe1d2bd27aa1e Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Mon, 8 Apr 2019 09:06:46 +0200 Subject: [PATCH 2/6] Fix dark themed componnents --- .../Resources/views/Collector/messenger.html.twig | 1 + .../Resources/views/Profiler/profiler.css.twig | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig index faef05d963..779f1259ed 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/messenger.html.twig @@ -58,6 +58,7 @@ .message-item tbody.sf-toggle-content.sf-toggle-visible { display: table-row-group; } td.message-bus-dispatch-caller { background: #f1f2f3; } + .theme-dark td.message-bus-dispatch-caller { background: var(--base-1); } {% endblock %} diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig index 3cd0933622..3a5674ebbc 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/profiler.css.twig @@ -1020,6 +1020,8 @@ table.logs .metadata { display: block; font-size: 12px; } +.theme-dark tr.status-error td, +.theme-dark tr.status-warning td { border-bottom: unset; border-top: unset; } {# Doctrine panel ========================================================================= #} From a0c66a399ddc04ede9c2f053044e2bb898df2c9e Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Wed, 6 Feb 2019 00:57:29 +0100 Subject: [PATCH 3/6] Fix TestRunner compatibility to PhpUnit 8 --- .../Bridge/PhpUnit/Legacy/CommandForV5.php | 20 +++++++- .../Bridge/PhpUnit/Legacy/CommandForV6.php | 22 ++++++++- .../Bridge/PhpUnit/Legacy/TestRunnerForV5.php | 48 ------------------ .../Bridge/PhpUnit/Legacy/TestRunnerForV6.php | 49 ------------------- .../Bridge/PhpUnit/Legacy/TestRunnerForV7.php | 49 ------------------- .../Bridge/PhpUnit/TextUI/TestRunner.php | 26 ---------- 6 files changed, 39 insertions(+), 175 deletions(-) delete mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php delete mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php delete mode 100644 src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php delete mode 100644 src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php index d4b5ea26d8..95dcb1e554 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV5.php @@ -23,6 +23,24 @@ class CommandForV5 extends \PHPUnit_TextUI_Command */ protected function createRunner() { - return new TestRunnerForV5($this->arguments['loader']); + $listener = new SymfonyTestsListenerForV5(); + + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : array(); + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListenerForV5) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = $listener; + } + + return parent::createRunner(); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php index fc717ef415..f8f75bb09a 100644 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php +++ b/src/Symfony/Bridge/PhpUnit/Legacy/CommandForV6.php @@ -13,7 +13,7 @@ namespace Symfony\Bridge\PhpUnit\Legacy; use PHPUnit\TextUI\Command as BaseCommand; use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\TextUI\TestRunner; +use Symfony\Bridge\PhpUnit\SymfonyTestsListener; /** * {@inheritdoc} @@ -27,6 +27,24 @@ class CommandForV6 extends BaseCommand */ protected function createRunner(): BaseRunner { - return new TestRunner($this->arguments['loader']); + $listener = new SymfonyTestsListener(); + + $this->arguments['listeners'] = isset($this->arguments['listeners']) ? $this->arguments['listeners'] : []; + + $registeredLocally = false; + + foreach ($this->arguments['listeners'] as $registeredListener) { + if ($registeredListener instanceof SymfonyTestsListener) { + $registeredListener->globalListenerDisabled(); + $registeredLocally = true; + break; + } + } + + if (!$registeredLocally) { + $this->arguments['listeners'][] = $listener; + } + + return parent::createRunner(); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php deleted file mode 100644 index 7897861cf5..0000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV5.php +++ /dev/null @@ -1,48 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV5 extends \PHPUnit_TextUI_TestRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListenerForV5(); - - $result = parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListenerForV5) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - - return $result; - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php deleted file mode 100644 index 6da7c65448..0000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV6.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV6 extends BaseRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments) - { - $listener = new SymfonyTestsListener(); - - parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php deleted file mode 100644 index a175fb65d7..0000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/TestRunnerForV7.php +++ /dev/null @@ -1,49 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\TextUI\TestRunner as BaseRunner; -use Symfony\Bridge\PhpUnit\SymfonyTestsListener; - -/** - * {@inheritdoc} - * - * @internal - */ -class TestRunnerForV7 extends BaseRunner -{ - /** - * {@inheritdoc} - */ - protected function handleConfiguration(array &$arguments): void - { - $listener = new SymfonyTestsListener(); - - parent::handleConfiguration($arguments); - - $arguments['listeners'] = isset($arguments['listeners']) ? $arguments['listeners'] : array(); - - $registeredLocally = false; - - foreach ($arguments['listeners'] as $registeredListener) { - if ($registeredListener instanceof SymfonyTestsListener) { - $registeredListener->globalListenerDisabled(); - $registeredLocally = true; - break; - } - } - - if (!$registeredLocally) { - $arguments['listeners'][] = $listener; - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php b/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php deleted file mode 100644 index cda5920979..0000000000 --- a/src/Symfony/Bridge/PhpUnit/TextUI/TestRunner.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\TextUI; - -if (class_exists('PHPUnit_Runner_Version') && version_compare(\PHPUnit_Runner_Version::id(), '6.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV5', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} elseif (version_compare(\PHPUnit\Runner\Version::id(), '7.0.0', '<')) { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV6', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} else { - class_alias('Symfony\Bridge\PhpUnit\Legacy\TestRunnerForV7', 'Symfony\Bridge\PhpUnit\TextUI\TestRunner'); -} - -if (false) { - class TestRunner - { - } -} From 8fb2074972b720c5b5f64845870bfb475fe9fef3 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Mon, 8 Apr 2019 11:27:09 +0200 Subject: [PATCH 4/6] fix resetting the COLUMN environment variable --- .../Component/Console/Tests/ApplicationTest.php | 16 ++++++++-------- .../Console/Tests/Helper/ProgressBarTest.php | 2 +- .../Console/Tests/Style/SymfonyStyleTest.php | 2 +- .../Component/Console/Tests/TerminalTest.php | 12 ++++++------ 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/Console/Tests/ApplicationTest.php b/src/Symfony/Component/Console/Tests/ApplicationTest.php index 85484b5c5a..200568f070 100644 --- a/src/Symfony/Component/Console/Tests/ApplicationTest.php +++ b/src/Symfony/Component/Console/Tests/ApplicationTest.php @@ -48,6 +48,14 @@ class ApplicationTest extends TestCase $this->colSize = getenv('COLUMNS'); } + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); + putenv('SHELL_VERBOSITY'); + unset($_ENV['SHELL_VERBOSITY']); + unset($_SERVER['SHELL_VERBOSITY']); + } + public static function setUpBeforeClass() { self::$fixturesPath = realpath(__DIR__.'/Fixtures/'); @@ -1698,14 +1706,6 @@ class ApplicationTest extends TestCase $this->assertSame($e->getMessage(), 'Class \'UnknownClass\' not found'); } } - - protected function tearDown() - { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); - putenv('SHELL_VERBOSITY'); - unset($_ENV['SHELL_VERBOSITY']); - unset($_SERVER['SHELL_VERBOSITY']); - } } class CustomApplication extends Application diff --git a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php index 214e943d73..a0be9b8a6d 100644 --- a/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php +++ b/src/Symfony/Component/Console/Tests/Helper/ProgressBarTest.php @@ -31,7 +31,7 @@ class ProgressBarTest extends TestCase protected function tearDown() { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); } public function testMultipleStart() diff --git a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php index a6feb122a1..88d00c8a99 100644 --- a/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php +++ b/src/Symfony/Component/Console/Tests/Style/SymfonyStyleTest.php @@ -38,7 +38,7 @@ class SymfonyStyleTest extends TestCase protected function tearDown() { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); $this->command = null; $this->tester = null; } diff --git a/src/Symfony/Component/Console/Tests/TerminalTest.php b/src/Symfony/Component/Console/Tests/TerminalTest.php index ca3b874374..93b8c44a78 100644 --- a/src/Symfony/Component/Console/Tests/TerminalTest.php +++ b/src/Symfony/Component/Console/Tests/TerminalTest.php @@ -25,6 +25,12 @@ class TerminalTest extends TestCase $this->lineSize = getenv('LINES'); } + protected function tearDown() + { + putenv($this->colSize ? 'COLUMNS='.$this->colSize : 'COLUMNS'); + putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize); + } + public function test() { putenv('COLUMNS=100'); @@ -40,12 +46,6 @@ class TerminalTest extends TestCase $this->assertSame(60, $terminal->getHeight()); } - protected function tearDown() - { - putenv($this->colSize ? 'COLUMNS' : 'COLUMNS='.$this->colSize); - putenv($this->lineSize ? 'LINES' : 'LINES='.$this->lineSize); - } - public function test_zero_values() { putenv('COLUMNS=0'); From 926d228877deb81c6edf86d3802894c4877b9e92 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sat, 6 Apr 2019 16:28:08 +0200 Subject: [PATCH 5/6] [Serializer] Respect ignored attributes in cache key of normalizer --- .../Normalizer/AbstractNormalizer.php | 9 ++-- .../Normalizer/AbstractObjectNormalizer.php | 50 +++++++++++++++---- .../Tests/Normalizer/ObjectNormalizerTest.php | 10 ++++ 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index ef1166a493..c5bd3ffd97 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -397,7 +397,7 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N } $parameterClass = $parameter->getClass()->getName(); - return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName)); + return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format)); } return $parameterData; @@ -407,14 +407,15 @@ abstract class AbstractNormalizer extends SerializerAwareNormalizer implements N } /** - * @param array $parentContext - * @param string $attribute + * @param array $parentContext + * @param string $attribute Attribute name + * @param string|null $format * * @return array * * @internal */ - protected function createChildContext(array $parentContext, $attribute) + protected function createChildContext(array $parentContext, $attribute/*, string $format = null */) { if (isset($parentContext[self::ATTRIBUTES][$attribute])) { $parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute]; diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 17c31b132b..38908a9323 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -94,7 +94,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute)); } - $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute))); + $data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute, $format))); } return $data; @@ -128,15 +128,13 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer return $allowedAttributes; } - if (isset($context['attributes'])) { - return $this->extractAttributes($object, $format, $context); + $attributes = $this->extractAttributes($object, $format, $context); + + if ($context['cache_key']) { + $this->attributesCache[$key] = $attributes; } - if (isset($this->attributesCache[$class])) { - return $this->attributesCache[$class]; - } - - return $this->attributesCache[$class] = $this->extractAttributes($object, $format, $context); + return $attributes; } /** @@ -276,7 +274,7 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer', $attribute, $class)); } - $childContext = $this->createChildContext($context, $attribute); + $childContext = $this->createChildContext($context, $attribute, $format); if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) { return $this->serializer->denormalize($data, $class, $format, $childContext); } @@ -373,7 +371,32 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer } /** - * Gets the cache key to use. + * Overwritten to update the cache key for the child. + * + * We must not mix up the attribute cache between parent and children. + * + * {@inheritdoc} + */ + protected function createChildContext(array $parentContext, $attribute/*, string $format = null */) + { + if (\func_num_args() >= 3) { + $format = \func_get_arg(2); + } else { + // will be deprecated in version 4 + $format = null; + } + + $context = parent::createChildContext($parentContext, $attribute, $format); + // format is already included in the cache_key of the parent. + $context['cache_key'] = $this->getCacheKey($format, $context); + + return $context; + } + + /** + * Builds the cache key for the attributes cache. + * + * The key must be different for every option in the context that could change which attributes should be handled. * * @param string|null $format * @param array $context @@ -382,8 +405,13 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer */ private function getCacheKey($format, array $context) { + unset($context['cache_key']); // avoid artificially different keys try { - return md5($format.serialize($context)); + return md5($format.serialize([ + 'context' => $context, + 'ignored' => $this->ignoredAttributes, + 'camelized' => $this->camelizedAttributes, + ])); } catch (\Exception $exception) { // The context cannot be serialized, skip the cache return false; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 4b72e2a4b5..b30739714b 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -380,6 +380,16 @@ class ObjectNormalizerTest extends TestCase ['fooBar' => 'foobar'], $this->normalizer->normalize($obj, 'any') ); + + $this->normalizer->setIgnoredAttributes(['foo', 'baz', 'camelCase', 'object']); + + $this->assertEquals( + [ + 'fooBar' => 'foobar', + 'bar' => 'bar', + ], + $this->normalizer->normalize($obj, 'any') + ); } public function testIgnoredAttributesDenormalize() From 37891525f7e7b5ac49243790838361e47d50f9d1 Mon Sep 17 00:00:00 2001 From: David Buchmann Date: Sun, 7 Apr 2019 09:41:38 +0200 Subject: [PATCH 6/6] [serializer] validate that the specified callbacks and max_depth_handler are actually callable --- .../Normalizer/AbstractNormalizer.php | 8 ++++-- .../Normalizer/AbstractObjectNormalizer.php | 27 ++++++++++++++++++- .../Tests/Normalizer/ObjectNormalizerTest.php | 6 ++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index fe4174e088..79e18b7fc3 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -99,10 +99,14 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn $this->nameConverter = $nameConverter; $this->defaultContext = array_merge($this->defaultContext, $defaultContext); - if (\is_array($this->defaultContext[self::CALLBACKS] ?? null)) { + if (isset($this->defaultContext[self::CALLBACKS])) { + if (!\is_array($this->defaultContext[self::CALLBACKS])) { + throw new InvalidArgumentException(sprintf('The "%s" default context option must be an array of callables.', self::CALLBACKS)); + } + foreach ($this->defaultContext[self::CALLBACKS] as $attribute => $callback) { if (!\is_callable($callback)) { - throw new InvalidArgumentException(sprintf('The given callback for attribute "%s" is not callable.', $attribute)); + throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" default context option.', $attribute, self::CALLBACKS)); } } } diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php index 5204990674..ad1c4b1667 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -59,6 +59,11 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer public function __construct(ClassMetadataFactoryInterface $classMetadataFactory = null, NameConverterInterface $nameConverter = null, PropertyTypeExtractorInterface $propertyTypeExtractor = null, ClassDiscriminatorResolverInterface $classDiscriminatorResolver = null, callable $objectClassResolver = null, array $defaultContext = []) { parent::__construct($classMetadataFactory, $nameConverter, $defaultContext); + + if (isset($this->defaultContext[self::MAX_DEPTH_HANDLER]) && !\is_callable($this->defaultContext[self::MAX_DEPTH_HANDLER])) { + throw new InvalidArgumentException(sprintf('The "%s" given in the default context is not callable.', self::MAX_DEPTH_HANDLER)); + } + $this->defaultContext[self::EXCLUDE_FROM_CACHE_KEY] = [self::CIRCULAR_REFERENCE_LIMIT_COUNTERS]; $this->propertyTypeExtractor = $propertyTypeExtractor; @@ -87,6 +92,18 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer $context['cache_key'] = $this->getCacheKey($format, $context); } + if (isset($context[self::CALLBACKS])) { + if (!\is_array($context[self::CALLBACKS])) { + throw new InvalidArgumentException(sprintf('The "%s" context option must be an array of callables.', self::CALLBACKS)); + } + + foreach ($context[self::CALLBACKS] as $attribute => $callback) { + if (!\is_callable($callback)) { + throw new InvalidArgumentException(sprintf('Invalid callback found for attribute "%s" in the "%s" context option.', $attribute, self::CALLBACKS)); + } + } + } + if ($this->isCircularReference($object, $context)) { return $this->handleCircularReference($object, $format, $context); } @@ -96,7 +113,15 @@ abstract class AbstractObjectNormalizer extends AbstractNormalizer $attributes = $this->getAttributes($object, $format, $context); $class = $this->objectClassResolver ? ($this->objectClassResolver)($object) : \get_class($object); $attributesMetadata = $this->classMetadataFactory ? $this->classMetadataFactory->getMetadataFor($class)->getAttributesMetadata() : null; - $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER] ?? $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler; + if (isset($context[self::MAX_DEPTH_HANDLER])) { + $maxDepthHandler = $context[self::MAX_DEPTH_HANDLER]; + if (!\is_callable($maxDepthHandler)) { + throw new InvalidArgumentException(sprintf('The "%s" given in the context is not callable.', self::MAX_DEPTH_HANDLER)); + } + } else { + // already validated in constructor resp by type declaration of setMaxDepthHandler + $maxDepthHandler = $this->defaultContext[self::MAX_DEPTH_HANDLER] ?? $this->maxDepthHandler; + } foreach ($attributes as $attribute) { $maxDepthReached = false; diff --git a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php index 8ad705e2f7..0edf6f38aa 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -815,7 +815,11 @@ class ObjectNormalizerTest extends TestCase $this->normalizer->setMaxDepthHandler($handler); } } else { - $this->createNormalizer([ObjectNormalizer::MAX_DEPTH_HANDLER => $handler], $classMetadataFactory); + $context = []; + if (null !== $handler) { + $context[ObjectNormalizer::MAX_DEPTH_HANDLER] = $handler; + } + $this->createNormalizer($context, $classMetadataFactory); } $this->serializer = new Serializer([$this->normalizer]); $this->normalizer->setSerializer($this->serializer);