Skip to content
Permalink
Browse files

bug #30950 [Serializer] Also validate callbacks when given in the nor…

…malizer context (dbu)

This PR was merged into the 4.2 branch.

Discussion
----------

[Serializer] Also validate callbacks when given in the normalizer context

| Q             | A
| ------------- | ---
| Branch?       | 4.2 (callbacks are handled differently in 3.4)
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no (unless somebody relied on this bug ignoring `null` as callback
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | Related to #30888
| License       | MIT
| Doc PR        | -

callbacks configuration for the normalizer is validated to be valid callbacks when using setCallbacks or using the callbacks field in the default options. however, it was not validated when using the callbacks field in a context passed to `normalize()`

Commits
-------

3789152 [serializer] validate that the specified callbacks and max_depth_handler are actually callable
  • Loading branch information...
fabpot committed Apr 8, 2019
2 parents ec41d76 + 3789152 commit 08f24b0cae5d6e5f72d57e046c261ebfbaf3e47e
@@ -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));
}
}
}
@@ -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;
@@ -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);

0 comments on commit 08f24b0

Please sign in to comment.
You can’t perform that action at this time.