diff --git a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php index ef1166a49349..c5bd3ffd9720 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php @@ -397,7 +397,7 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara } $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 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara } /** - * @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 17c31b132b29..38908a9323ba 100644 --- a/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php +++ b/src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php @@ -94,7 +94,7 @@ public function normalize($object, $format = null, array $context = []) 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 @@ protected function getAttributes($object, $format = null, array $context) return $allowedAttributes; } - if (isset($context['attributes'])) { - return $this->extractAttributes($object, $format, $context); - } + $attributes = $this->extractAttributes($object, $format, $context); - if (isset($this->attributesCache[$class])) { - return $this->attributesCache[$class]; + if ($context['cache_key']) { + $this->attributesCache[$key] = $attributes; } - return $this->attributesCache[$class] = $this->extractAttributes($object, $format, $context); + return $attributes; } /** @@ -276,7 +274,7 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma 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 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute } /** - * 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 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute */ 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 4b72e2a4b503..b30739714b66 100644 --- a/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php +++ b/src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php @@ -380,6 +380,16 @@ public function testIgnoredAttributes() ['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()