diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index fe4174e08852..79e18b7fc3e7 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -99,10 +99,14 @@ public function __construct(ClassMetadataFactoryInterface $classMetadataFactory $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 520499067414..ad1c4b166715 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 @@ public function normalize($object, $format = null, array $context = []) $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 @@ public function normalize($object, $format = null, array $context = []) $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 8ad705e2f765..0edf6f38aa2e 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -815,7 +815,11 @@ private function createNormalizerWithMaxDepthHandler(callable $handler = null, b $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);