Skip to content

Commit d8b75b2

Browse files
committed
[Serializer] Fix ObjectNormalizer default context with named serializers
1 parent d3e6cd1 commit d8b75b2

File tree

2 files changed

+92
-8
lines changed

2 files changed

+92
-8
lines changed

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']));

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)