Skip to content

Commit

Permalink
bug #30950 [Serializer] Also validate callbacks when given in the nor…
Browse files Browse the repository at this point in the history
…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 08f24b0
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 4 deletions.
Expand Up @@ -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));
}
}
}
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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;
Expand Down
Expand Up @@ -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);
Expand Down

0 comments on commit 08f24b0

Please sign in to comment.