Skip to content

Commit 7a3060a

Browse files
committed
bug symfony#30907 [Serializer] Respect ignored attributes in cache key of normalizer (dbu)
This PR was squashed before being merged into the 3.4 branch (closes symfony#30907). Discussion ---------- [Serializer] Respect ignored attributes in cache key of normalizer EUFOSSA | Q | A | ------------- | --- | Branch | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - Do not share the attributes cache in object normalizer when using a different setting for the ignoredAttributes setting. In Symfony 4.2, the setter is deprecated in favor of the ignored_attibutes option in the $context. When merging this up, we will however still need to respect the field as well for BC, the cache key does not look at the default context (apart from the deprecated modifiers, the default context is immutable) There might be performance regression for some use cases, but also could be a performance improvement when using 'attributes' in the context with lists of objects of the same class. Commits ------- 926d228 [Serializer] Respect ignored attributes in cache key of normalizer
2 parents d3eae71 + 926d228 commit 7a3060a

File tree

3 files changed

+53
-14
lines changed

3 files changed

+53
-14
lines changed

src/Symfony/Component/Serializer/Normalizer/AbstractNormalizer.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara
397397
}
398398
$parameterClass = $parameter->getClass()->getName();
399399

400-
return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName));
400+
return $this->serializer->denormalize($parameterData, $parameterClass, $format, $this->createChildContext($context, $parameterName, $format));
401401
}
402402

403403
return $parameterData;
@@ -407,14 +407,15 @@ protected function denormalizeParameter(\ReflectionClass $class, \ReflectionPara
407407
}
408408

409409
/**
410-
* @param array $parentContext
411-
* @param string $attribute
410+
* @param array $parentContext
411+
* @param string $attribute Attribute name
412+
* @param string|null $format
412413
*
413414
* @return array
414415
*
415416
* @internal
416417
*/
417-
protected function createChildContext(array $parentContext, $attribute)
418+
protected function createChildContext(array $parentContext, $attribute/*, string $format = null */)
418419
{
419420
if (isset($parentContext[self::ATTRIBUTES][$attribute])) {
420421
$parentContext[self::ATTRIBUTES] = $parentContext[self::ATTRIBUTES][$attribute];

src/Symfony/Component/Serializer/Normalizer/AbstractObjectNormalizer.php

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function normalize($object, $format = null, array $context = [])
9494
throw new LogicException(sprintf('Cannot normalize attribute "%s" because the injected serializer is not a normalizer', $attribute));
9595
}
9696

97-
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute)));
97+
$data = $this->updateData($data, $attribute, $this->serializer->normalize($attributeValue, $format, $this->createChildContext($context, $attribute, $format)));
9898
}
9999

100100
return $data;
@@ -128,15 +128,13 @@ protected function getAttributes($object, $format = null, array $context)
128128
return $allowedAttributes;
129129
}
130130

131-
if (isset($context['attributes'])) {
132-
return $this->extractAttributes($object, $format, $context);
133-
}
131+
$attributes = $this->extractAttributes($object, $format, $context);
134132

135-
if (isset($this->attributesCache[$class])) {
136-
return $this->attributesCache[$class];
133+
if ($context['cache_key']) {
134+
$this->attributesCache[$key] = $attributes;
137135
}
138136

139-
return $this->attributesCache[$class] = $this->extractAttributes($object, $format, $context);
137+
return $attributes;
140138
}
141139

142140
/**
@@ -276,7 +274,7 @@ private function validateAndDenormalize($currentClass, $attribute, $data, $forma
276274
throw new LogicException(sprintf('Cannot denormalize attribute "%s" for class "%s" because injected serializer is not a denormalizer', $attribute, $class));
277275
}
278276

279-
$childContext = $this->createChildContext($context, $attribute);
277+
$childContext = $this->createChildContext($context, $attribute, $format);
280278
if ($this->serializer->supportsDenormalization($data, $class, $format, $childContext)) {
281279
return $this->serializer->denormalize($data, $class, $format, $childContext);
282280
}
@@ -373,7 +371,32 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute
373371
}
374372

375373
/**
376-
* Gets the cache key to use.
374+
* Overwritten to update the cache key for the child.
375+
*
376+
* We must not mix up the attribute cache between parent and children.
377+
*
378+
* {@inheritdoc}
379+
*/
380+
protected function createChildContext(array $parentContext, $attribute/*, string $format = null */)
381+
{
382+
if (\func_num_args() >= 3) {
383+
$format = \func_get_arg(2);
384+
} else {
385+
// will be deprecated in version 4
386+
$format = null;
387+
}
388+
389+
$context = parent::createChildContext($parentContext, $attribute, $format);
390+
// format is already included in the cache_key of the parent.
391+
$context['cache_key'] = $this->getCacheKey($format, $context);
392+
393+
return $context;
394+
}
395+
396+
/**
397+
* Builds the cache key for the attributes cache.
398+
*
399+
* The key must be different for every option in the context that could change which attributes should be handled.
377400
*
378401
* @param string|null $format
379402
* @param array $context
@@ -382,8 +405,13 @@ private function isMaxDepthReached(array $attributesMetadata, $class, $attribute
382405
*/
383406
private function getCacheKey($format, array $context)
384407
{
408+
unset($context['cache_key']); // avoid artificially different keys
385409
try {
386-
return md5($format.serialize($context));
410+
return md5($format.serialize([
411+
'context' => $context,
412+
'ignored' => $this->ignoredAttributes,
413+
'camelized' => $this->camelizedAttributes,
414+
]));
387415
} catch (\Exception $exception) {
388416
// The context cannot be serialized, skip the cache
389417
return false;

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,16 @@ public function testIgnoredAttributes()
380380
['fooBar' => 'foobar'],
381381
$this->normalizer->normalize($obj, 'any')
382382
);
383+
384+
$this->normalizer->setIgnoredAttributes(['foo', 'baz', 'camelCase', 'object']);
385+
386+
$this->assertEquals(
387+
[
388+
'fooBar' => 'foobar',
389+
'bar' => 'bar',
390+
],
391+
$this->normalizer->normalize($obj, 'any')
392+
);
383393
}
384394

385395
public function testIgnoredAttributesDenormalize()

0 commit comments

Comments
 (0)