Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 02161b7

Browse filesBrowse files
committed
bug #60026 [Serializer] Fix ObjectNormalizer default context with named serializers (HypeMC)
This PR was merged into the 7.2 branch. Discussion ---------- [Serializer] Fix ObjectNormalizer default context with named serializers | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #59639 | License | MIT As mentioned in #59639, the main problem is that arguments have precedence over bindings, and children inherit arguments from their parents. This PR moves the logic for setting the default context of `ObjectNormalizer` from the extension to the `SerializerPass`, where only bindings are used. Commits ------- 80d993f [Serializer] Fix ObjectNormalizer default context with named serializers
2 parents 2f6b2e6 + 80d993f commit 02161b7
Copy full SHA for 02161b7

File tree

6 files changed

+110
-28
lines changed
Filter options

6 files changed

+110
-28
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+3-13
Original file line numberDiff line numberDiff line change
@@ -1946,24 +1946,14 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19461946
$container->setParameter('serializer.default_context', $defaultContext);
19471947
}
19481948

1949-
if (!$container->hasDefinition('serializer.normalizer.object')) {
1950-
return;
1951-
}
1952-
1953-
$arguments = $container->getDefinition('serializer.normalizer.object')->getArguments();
1954-
$context = $arguments[6] ?? $defaultContext;
1955-
1956-
if (isset($config['circular_reference_handler']) && $config['circular_reference_handler']) {
1957-
$context += ['circular_reference_handler' => new Reference($config['circular_reference_handler'])];
1958-
$container->getDefinition('serializer.normalizer.object')->setArgument(5, null);
1949+
if ($config['circular_reference_handler'] ?? false) {
1950+
$container->setParameter('.serializer.circular_reference_handler', $config['circular_reference_handler']);
19591951
}
19601952

19611953
if ($config['max_depth_handler'] ?? false) {
1962-
$context += ['max_depth_handler' => new Reference($config['max_depth_handler'])];
1954+
$container->setParameter('.serializer.max_depth_handler', $config['max_depth_handler']);
19631955
}
19641956

1965-
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
1966-
19671957
$container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext);
19681958

19691959
$container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []);

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php
+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
service('property_info')->ignoreOnInvalid(),
130130
service('serializer.mapping.class_discriminator_resolver')->ignoreOnInvalid(),
131131
null,
132-
null,
132+
abstract_arg('default context, set in the SerializerPass'),
133133
service('property_info')->ignoreOnInvalid(),
134134
])
135135
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -1000])

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTestCase.php
+12-4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
3434
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
3535
use Symfony\Component\DependencyInjection\ChildDefinition;
36+
use Symfony\Component\DependencyInjection\Compiler\ResolveBindingsPass;
3637
use Symfony\Component\DependencyInjection\Compiler\ResolveChildDefinitionsPass;
3738
use Symfony\Component\DependencyInjection\Compiler\ResolveInstanceofConditionalsPass;
3839
use Symfony\Component\DependencyInjection\Compiler\ResolveTaggedIteratorArgumentPass;
@@ -67,6 +68,7 @@
6768
use Symfony\Component\Notifier\TexterInterface;
6869
use Symfony\Component\PropertyAccess\PropertyAccessor;
6970
use Symfony\Component\Security\Core\AuthenticationEvents;
71+
use Symfony\Component\Serializer\DependencyInjection\SerializerPass;
7072
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
7173
use Symfony\Component\Serializer\Mapping\Loader\XmlFileLoader;
7274
use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
@@ -1447,9 +1449,6 @@ public function testSerializerEnabled()
14471449
$this->assertEquals(AttributeLoader::class, $argument[0]->getClass());
14481450
$this->assertEquals(new Reference('serializer.name_converter.camel_case_to_snake_case'), $container->getDefinition('serializer.name_converter.metadata_aware')->getArgument(1));
14491451
$this->assertEquals(new Reference('property_info', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE), $container->getDefinition('serializer.normalizer.object')->getArgument(3));
1450-
$this->assertArrayHasKey('circular_reference_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6));
1451-
$this->assertArrayHasKey('max_depth_handler', $container->getDefinition('serializer.normalizer.object')->getArgument(6));
1452-
$this->assertEquals($container->getDefinition('serializer.normalizer.object')->getArgument(6)['max_depth_handler'], new Reference('my.max.depth.handler'));
14531452
}
14541453

14551454
public function testSerializerWithoutTranslator()
@@ -1547,13 +1546,22 @@ public function testJsonSerializableNormalizerRegistered()
15471546

15481547
public function testObjectNormalizerRegistered()
15491548
{
1550-
$container = $this->createContainerFromFile('full');
1549+
$container = $this->createContainerFromFile('full', compile: false);
1550+
$container->addCompilerPass(new SerializerPass());
1551+
$container->addCompilerPass(new ResolveBindingsPass());
1552+
$container->compile();
15511553

15521554
$definition = $container->getDefinition('serializer.normalizer.object');
15531555
$tag = $definition->getTag('serializer.normalizer');
15541556

15551557
$this->assertEquals(ObjectNormalizer::class, $definition->getClass());
15561558
$this->assertEquals(-1000, $tag[0]['priority']);
1559+
1560+
$this->assertEquals([
1561+
'enable_max_depth' => true,
1562+
'circular_reference_handler' => new Reference('my.circular.reference.handler'),
1563+
'max_depth_handler' => new Reference('my.max.depth.handler'),
1564+
], $definition->getArgument(6));
15571565
}
15581566

15591567
public function testConstraintViolationListNormalizerRegistered()

‎src/Symfony/Bundle/FrameworkBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/composer.json
+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"symfony/scheduler": "^6.4.4|^7.0.4",
6060
"symfony/security-bundle": "^6.4|^7.0",
6161
"symfony/semaphore": "^6.4|^7.0",
62-
"symfony/serializer": "^7.1",
62+
"symfony/serializer": "^7.2.5",
6363
"symfony/stopwatch": "^6.4|^7.0",
6464
"symfony/string": "^6.4|^7.0",
6565
"symfony/translation": "^6.4|^7.0",
@@ -97,7 +97,7 @@
9797
"symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4",
9898
"symfony/security-csrf": "<7.2",
9999
"symfony/security-core": "<6.4",
100-
"symfony/serializer": "<7.1",
100+
"symfony/serializer": "<7.2.5",
101101
"symfony/stopwatch": "<6.4",
102102
"symfony/translation": "<6.4",
103103
"symfony/twig-bridge": "<6.4",

‎src/Symfony/Component/Serializer/DependencyInjection/SerializerPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/DependencyInjection/SerializerPass.php
+31-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\DependencyInjection\Reference;
2020
use Symfony\Component\Serializer\Debug\TraceableEncoder;
2121
use Symfony\Component\Serializer\Debug\TraceableNormalizer;
22+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2223
use Symfony\Component\Serializer\SerializerInterface;
2324

2425
/**
@@ -54,17 +55,27 @@ public function process(ContainerBuilder $container): void
5455
throw new RuntimeException('You must tag at least one service as "serializer.encoder" to use the "serializer" service.');
5556
}
5657

58+
$defaultContext = [];
5759
if ($container->hasParameter('serializer.default_context')) {
5860
$defaultContext = $container->getParameter('serializer.default_context');
59-
$this->bindDefaultContext($container, array_merge($normalizers, $encoders), $defaultContext);
6061
$container->getParameterBag()->remove('serializer.default_context');
6162
$container->getDefinition('serializer')->setArgument('$defaultContext', $defaultContext);
6263
}
6364

65+
/** @var ?string $circularReferenceHandler */
66+
$circularReferenceHandler = $container->hasParameter('.serializer.circular_reference_handler')
67+
? $container->getParameter('.serializer.circular_reference_handler') : null;
68+
69+
/** @var ?string $maxDepthHandler */
70+
$maxDepthHandler = $container->hasParameter('.serializer.max_depth_handler')
71+
? $container->getParameter('.serializer.max_depth_handler') : null;
72+
73+
$this->bindDefaultContext($container, array_merge($normalizers, $encoders), $defaultContext, $circularReferenceHandler, $maxDepthHandler);
74+
6475
$this->configureSerializer($container, 'serializer', $normalizers, $encoders, 'default');
6576

6677
if ($namedSerializers) {
67-
$this->configureNamedSerializers($container);
78+
$this->configureNamedSerializers($container, $circularReferenceHandler, $maxDepthHandler);
6879
}
6980
}
7081

@@ -98,11 +109,22 @@ private function createNamedSerializerTags(ContainerBuilder $container, string $
98109
}
99110
}
100111

101-
private function bindDefaultContext(ContainerBuilder $container, array $services, array $defaultContext): void
112+
private function bindDefaultContext(ContainerBuilder $container, array $services, array $defaultContext, ?string $circularReferenceHandler, ?string $maxDepthHandler): void
102113
{
103114
foreach ($services as $id) {
104115
$definition = $container->getDefinition((string) $id);
105-
$definition->setBindings(['array $defaultContext' => new BoundArgument($defaultContext, false)] + $definition->getBindings());
116+
117+
$context = $defaultContext;
118+
if (is_a($definition->getClass(), ObjectNormalizer::class, true)) {
119+
if (null !== $circularReferenceHandler) {
120+
$context += ['circular_reference_handler' => new Reference($circularReferenceHandler)];
121+
}
122+
if (null !== $maxDepthHandler) {
123+
$context += ['max_depth_handler' => new Reference($maxDepthHandler)];
124+
}
125+
}
126+
127+
$definition->setBindings(['array $defaultContext' => new BoundArgument($context, false)] + $definition->getBindings());
106128
}
107129
}
108130

@@ -125,7 +147,7 @@ private function configureSerializer(ContainerBuilder $container, string $id, ar
125147
$serializerDefinition->replaceArgument(1, $encoders);
126148
}
127149

128-
private function configureNamedSerializers(ContainerBuilder $container): void
150+
private function configureNamedSerializers(ContainerBuilder $container, ?string $circularReferenceHandler, ?string $maxDepthHandler): void
129151
{
130152
$defaultSerializerNameConverter = $container->hasParameter('.serializer.name_converter')
131153
? $container->getParameter('.serializer.name_converter') : null;
@@ -149,7 +171,7 @@ private function configureNamedSerializers(ContainerBuilder $container): void
149171
$normalizers = $this->buildChildDefinitions($container, $serializerName, $normalizers, $config);
150172
$encoders = $this->buildChildDefinitions($container, $serializerName, $encoders, $config);
151173

152-
$this->bindDefaultContext($container, array_merge($normalizers, $encoders), $config['default_context']);
174+
$this->bindDefaultContext($container, array_merge($normalizers, $encoders), $config['default_context'], $circularReferenceHandler, $maxDepthHandler);
153175

154176
$container->registerChild($serializerId, 'serializer')->setArgument('$defaultContext', $config['default_context']);
155177
$container->registerAliasForArgument($serializerId, SerializerInterface::class, $serializerName.'.serializer');
@@ -184,7 +206,9 @@ private function buildChildDefinitions(ContainerBuilder $container, string $seri
184206
foreach ($services as &$id) {
185207
$childId = $id.'.'.$serializerName;
186208

187-
$definition = $container->registerChild($childId, (string) $id);
209+
$definition = $container->registerChild($childId, (string) $id)
210+
->setClass($container->getDefinition((string) $id)->getClass())
211+
;
188212

189213
if (null !== $nameConverterIndex = $this->findNameConverterIndex($container, (string) $id)) {
190214
$definition->replaceArgument($nameConverterIndex, new Reference($config['name_converter']));

‎src/Symfony/Component/Serializer/Tests/DependencyInjection/SerializerPassTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Serializer/Tests/DependencyInjection/SerializerPassTest.php
+61-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\Serializer\Debug\TraceableNormalizer;
2020
use Symfony\Component\Serializer\Debug\TraceableSerializer;
2121
use Symfony\Component\Serializer\DependencyInjection\SerializerPass;
22+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
2223
use Symfony\Component\Serializer\SerializerInterface;
2324

2425
/**
@@ -99,6 +100,32 @@ public function testBindSerializerDefaultContext()
99100
$this->assertEquals($context, $container->getDefinition('serializer')->getArgument('$defaultContext'));
100101
}
101102

103+
/**
104+
* @testWith [{}, {}]
105+
* [{"serializer.default_context": {"enable_max_depth": true}}, {"enable_max_depth": true}]
106+
* [{".serializer.circular_reference_handler": "foo"}, {"circular_reference_handler": "foo"}]
107+
* [{".serializer.max_depth_handler": "bar"}, {"max_depth_handler": "bar"}]
108+
* [{"serializer.default_context": {"enable_max_depth": true}, ".serializer.circular_reference_handler": "foo", ".serializer.max_depth_handler": "bar"}, {"enable_max_depth": true, "circular_reference_handler": "foo", "max_depth_handler": "bar"}]
109+
*/
110+
public function testBindObjectNormalizerDefaultContext(array $parameters, array $context)
111+
{
112+
$container = new ContainerBuilder();
113+
$container->setParameter('kernel.debug', false);
114+
$container->register('serializer')->setArguments([null, null, []]);
115+
$container->getParameterBag()->add($parameters);
116+
$definition = $container->register('serializer.normalizer.object')
117+
->setClass(ObjectNormalizer::class)
118+
->addTag('serializer.normalizer')
119+
->addTag('serializer.encoder')
120+
;
121+
122+
$serializerPass = new SerializerPass();
123+
$serializerPass->process($container);
124+
125+
$bindings = $definition->getBindings();
126+
$this->assertEquals($bindings['array $defaultContext'], new BoundArgument($context, false));
127+
}
128+
102129
public function testNormalizersAndEncodersAreDecoratedAndOrderedWhenCollectingData()
103130
{
104131
$container = new ContainerBuilder();
@@ -565,7 +592,9 @@ public function testBindSerializerDefaultContextToNamedSerializers()
565592
$serializerPass = new SerializerPass();
566593
$serializerPass->process($container);
567594

568-
$this->assertEmpty($definition->getBindings());
595+
$bindings = $definition->getBindings();
596+
$this->assertArrayHasKey('array $defaultContext', $bindings);
597+
$this->assertEquals($bindings['array $defaultContext'], new BoundArgument([], false));
569598

570599
$bindings = $container->getDefinition('n1.api')->getBindings();
571600
$this->assertArrayHasKey('array $defaultContext', $bindings);
@@ -574,6 +603,37 @@ public function testBindSerializerDefaultContextToNamedSerializers()
574603
$this->assertEquals($defaultContext, $container->getDefinition('serializer.api')->getArgument('$defaultContext'));
575604
}
576605

606+
/**
607+
* @testWith [{}, {}, {}]
608+
* [{"enable_max_depth": true}, {}, {"enable_max_depth": true}]
609+
* [{}, {".serializer.circular_reference_handler": "foo"}, {"circular_reference_handler": "foo"}]
610+
* [{}, {".serializer.max_depth_handler": "bar"}, {"max_depth_handler": "bar"}]
611+
* [{"enable_max_depth": true}, {".serializer.circular_reference_handler": "foo", ".serializer.max_depth_handler": "bar"}, {"enable_max_depth": true, "circular_reference_handler": "foo", "max_depth_handler": "bar"}]
612+
*/
613+
public function testBindNamedSerializerObjectNormalizerDefaultContext(array $defaultContext, array $parameters, array $context)
614+
{
615+
$container = new ContainerBuilder();
616+
$container->setParameter('kernel.debug', false);
617+
$container->setParameter('.serializer.named_serializers', [
618+
'api' => ['default_context' => $defaultContext],
619+
]);
620+
621+
$container->register('serializer')->setArguments([null, null, []]);
622+
$container->getParameterBag()->add($parameters);
623+
$container->register('serializer.normalizer.object')
624+
->setClass(ObjectNormalizer::class)
625+
->addTag('serializer.normalizer', ['serializer' => '*'])
626+
->addTag('serializer.encoder', ['serializer' => '*'])
627+
;
628+
629+
$serializerPass = new SerializerPass();
630+
$serializerPass->process($container);
631+
632+
$bindings = $container->getDefinition('serializer.normalizer.object.api')->getBindings();
633+
$this->assertArrayHasKey('array $defaultContext', $bindings);
634+
$this->assertEquals($bindings['array $defaultContext'], new BoundArgument($context, false));
635+
}
636+
577637
public function testNamedSerializersAreRegistered()
578638
{
579639
$container = new ContainerBuilder();

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.