Skip to content

Commit 29bd8ce

Browse files
HypeMCfabpot
authored andcommitted
[Serializer] Introduce named serializers
1 parent ce8be29 commit 29bd8ce

27 files changed

+1095
-195
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ CHANGELOG
1515
* Register `Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter` as a service named `serializer.name_converter.snake_case_to_camel_case` if available
1616
* Deprecate `session.sid_length` and `session.sid_bits_per_character` config options
1717
* Add the ability to use an existing service as a lock/semaphore resource
18+
* Add support for configuring multiple serializer instances via the configuration
1819

1920
7.1
2021
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

+36-7
Original file line numberDiff line numberDiff line change
@@ -1099,10 +1099,22 @@ private function addAnnotationsSection(ArrayNodeDefinition $rootNode): void
10991099

11001100
private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void
11011101
{
1102+
$defaultContextNode = fn () => (new NodeBuilder())
1103+
->arrayNode('default_context')
1104+
->normalizeKeys(false)
1105+
->validate()
1106+
->ifTrue(fn () => $this->debug && class_exists(JsonParser::class))
1107+
->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true])
1108+
->end()
1109+
->defaultValue([])
1110+
->prototype('variable')->end()
1111+
;
1112+
11021113
$rootNode
11031114
->children()
11041115
->arrayNode('serializer')
11051116
->info('Serializer configuration')
1117+
->fixXmlConfig('named_serializer', 'named_serializers')
11061118
->{$enableIfStandalone('symfony/serializer', Serializer::class)}()
11071119
->children()
11081120
->booleanNode('enable_attributes')->{class_exists(FullStack::class) ? 'defaultFalse' : 'defaultTrue'}()->end()
@@ -1118,19 +1130,36 @@ private function addSerializerSection(ArrayNodeDefinition $rootNode, callable $e
11181130
->end()
11191131
->end()
11201132
->end()
1121-
->arrayNode('default_context')
1122-
->normalizeKeys(false)
1133+
->append($defaultContextNode())
1134+
->arrayNode('named_serializers')
1135+
->useAttributeAsKey('name')
1136+
->arrayPrototype()
1137+
->children()
1138+
->scalarNode('name_converter')->end()
1139+
->append($defaultContextNode())
1140+
->booleanNode('include_built_in_normalizers')
1141+
->info('Whether to include the built-in normalizers')
1142+
->defaultTrue()
1143+
->end()
1144+
->booleanNode('include_built_in_encoders')
1145+
->info('Whether to include the built-in encoders')
1146+
->defaultTrue()
1147+
->end()
1148+
->end()
1149+
->end()
11231150
->validate()
1124-
->ifTrue(fn () => $this->debug && class_exists(JsonParser::class))
1125-
->then(fn (array $v) => $v + [JsonDecode::DETAILED_ERROR_MESSAGES => true])
1151+
->ifTrue(fn ($v) => isset($v['default']))
1152+
->thenInvalid('"default" is a reserved name.')
11261153
->end()
1127-
->defaultValue([])
1128-
->prototype('variable')->end()
11291154
->end()
11301155
->end()
11311156
->validate()
11321157
->ifTrue(fn ($v) => $this->debug && class_exists(JsonParser::class) && !isset($v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES]))
1133-
->then(function ($v) { $v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES] = true; return $v; })
1158+
->then(function ($v) {
1159+
$v['default_context'][JsonDecode::DETAILED_ERROR_MESSAGES] = true;
1160+
1161+
return $v;
1162+
})
11341163
->end()
11351164
->end()
11361165
->end()

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

+3
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19061906
$container->getDefinition('serializer.mapping.cache_warmer')->replaceArgument(0, $serializerLoaders);
19071907

19081908
if (isset($config['name_converter']) && $config['name_converter']) {
1909+
$container->setParameter('.serializer.name_converter', $config['name_converter']);
19091910
$container->getDefinition('serializer.name_converter.metadata_aware')->setArgument(1, new Reference($config['name_converter']));
19101911
}
19111912

@@ -1934,6 +1935,8 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19341935
$container->getDefinition('serializer.normalizer.object')->setArgument(6, $context);
19351936

19361937
$container->getDefinition('serializer.normalizer.property')->setArgument(5, $defaultContext);
1938+
1939+
$container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []);
19371940
}
19381941

19391942
private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void

src/Symfony/Bundle/FrameworkBundle/Resources/config/messenger.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
])
7373

7474
->set('serializer.normalizer.flatten_exception', FlattenExceptionNormalizer::class)
75-
->tag('serializer.normalizer', ['priority' => -880])
75+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -880])
7676

7777
->set('messenger.transport.native_php_serializer', PhpSerializer::class)
7878
->alias('messenger.default_serializer', 'messenger.transport.native_php_serializer')

src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

+11
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@
320320
<xsd:choice minOccurs="0" maxOccurs="unbounded">
321321
<xsd:element name="mapping" type="file_mapping" />
322322
<xsd:element name="default-context" type="metadata" minOccurs="0" maxOccurs="1" />
323+
<xsd:element name="named-serializer" type="named_serializer_options" />
323324
</xsd:choice>
324325
<xsd:attribute name="enabled" type="xsd:boolean" />
325326
<xsd:attribute name="enable-attributes" type="xsd:boolean" />
@@ -332,6 +333,16 @@
332333
<xsd:attribute name="enabled" type="xsd:boolean" />
333334
</xsd:complexType>
334335

336+
<xsd:complexType name="named_serializer_options">
337+
<xsd:sequence>
338+
<xsd:element name="default-context" type="metadata" minOccurs="0" maxOccurs="1" />
339+
</xsd:sequence>
340+
<xsd:attribute name="name" type="xsd:string" />
341+
<xsd:attribute name="name-converter" type="xsd:string" />
342+
<xsd:attribute name="include-built-in-normalizers" type="xsd:boolean" />
343+
<xsd:attribute name="include-built-in-encoders" type="xsd:boolean" />
344+
</xsd:complexType>
345+
335346
<xsd:complexType name="property_info">
336347
<xsd:attribute name="enabled" type="xsd:boolean" />
337348
</xsd:complexType>

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

+24-20
Original file line numberDiff line numberDiff line change
@@ -80,46 +80,46 @@
8080
->set('serializer.normalizer.constraint_violation_list', ConstraintViolationListNormalizer::class)
8181
->args([1 => service('serializer.name_converter.metadata_aware')])
8282
->autowire(true)
83-
->tag('serializer.normalizer', ['priority' => -915])
83+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
8484

8585
->set('serializer.normalizer.mime_message', MimeMessageNormalizer::class)
8686
->args([service('serializer.normalizer.property')])
87-
->tag('serializer.normalizer', ['priority' => -915])
87+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
8888

8989
->set('serializer.normalizer.datetimezone', DateTimeZoneNormalizer::class)
90-
->tag('serializer.normalizer', ['priority' => -915])
90+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
9191

9292
->set('serializer.normalizer.dateinterval', DateIntervalNormalizer::class)
93-
->tag('serializer.normalizer', ['priority' => -915])
93+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
9494

9595
->set('serializer.normalizer.data_uri', DataUriNormalizer::class)
9696
->args([service('mime_types')->nullOnInvalid()])
97-
->tag('serializer.normalizer', ['priority' => -920])
97+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -920])
9898

9999
->set('serializer.normalizer.datetime', DateTimeNormalizer::class)
100-
->tag('serializer.normalizer', ['priority' => -910])
100+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -910])
101101

102102
->set('serializer.normalizer.json_serializable', JsonSerializableNormalizer::class)
103103
->args([null, null])
104-
->tag('serializer.normalizer', ['priority' => -950])
104+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -950])
105105

106106
->set('serializer.normalizer.problem', ProblemNormalizer::class)
107107
->args([param('kernel.debug'), '$translator' => service('translator')->nullOnInvalid()])
108-
->tag('serializer.normalizer', ['priority' => -890])
108+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -890])
109109

110110
->set('serializer.denormalizer.unwrapping', UnwrappingDenormalizer::class)
111111
->args([service('serializer.property_accessor')])
112-
->tag('serializer.normalizer', ['priority' => 1000])
112+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => 1000])
113113

114114
->set('serializer.normalizer.uid', UidNormalizer::class)
115-
->tag('serializer.normalizer', ['priority' => -890])
115+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -890])
116116

117117
->set('serializer.normalizer.translatable', TranslatableNormalizer::class)
118118
->args(['$translator' => service('translator')])
119-
->tag('serializer.normalizer', ['priority' => -920])
119+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -920])
120120

121121
->set('serializer.normalizer.form_error', FormErrorNormalizer::class)
122-
->tag('serializer.normalizer', ['priority' => -915])
122+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
123123

124124
->set('serializer.normalizer.object', ObjectNormalizer::class)
125125
->args([
@@ -132,7 +132,7 @@
132132
null,
133133
service('property_info')->ignoreOnInvalid(),
134134
])
135-
->tag('serializer.normalizer', ['priority' => -1000])
135+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -1000])
136136

137137
->set('serializer.normalizer.property', PropertyNormalizer::class)
138138
->args([
@@ -144,7 +144,7 @@
144144
])
145145

146146
->set('serializer.denormalizer.array', ArrayDenormalizer::class)
147-
->tag('serializer.normalizer', ['priority' => -990])
147+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -990])
148148

149149
// Loader
150150
->set('serializer.mapping.chain_loader', LoaderChain::class)
@@ -174,26 +174,30 @@
174174

175175
// Encoders
176176
->set('serializer.encoder.xml', XmlEncoder::class)
177-
->tag('serializer.encoder')
177+
->tag('serializer.encoder', ['built_in' => true])
178178

179179
->set('serializer.encoder.json', JsonEncoder::class)
180180
->args([null, null])
181-
->tag('serializer.encoder')
181+
->tag('serializer.encoder', ['built_in' => true])
182182

183183
->set('serializer.encoder.yaml', YamlEncoder::class)
184184
->args([null, null])
185-
->tag('serializer.encoder')
185+
->tag('serializer.encoder', ['built_in' => true])
186186

187187
->set('serializer.encoder.csv', CsvEncoder::class)
188-
->tag('serializer.encoder')
188+
->tag('serializer.encoder', ['built_in' => true])
189189

190190
// Name converters
191191
->set('serializer.name_converter.camel_case_to_snake_case', CamelCaseToSnakeCaseNameConverter::class)
192192
->set('serializer.name_converter.snake_case_to_camel_case', SnakeCaseToCamelCaseNameConverter::class)
193193

194-
->set('serializer.name_converter.metadata_aware', MetadataAwareNameConverter::class)
194+
->set('serializer.name_converter.metadata_aware.abstract', MetadataAwareNameConverter::class)
195+
->abstract()
195196
->args([service('serializer.mapping.class_metadata_factory')])
196197

198+
->set('serializer.name_converter.metadata_aware')
199+
->parent('serializer.name_converter.metadata_aware.abstract')
200+
197201
// PropertyInfo extractor
198202
->set('property_info.serializer_extractor', SerializerExtractor::class)
199203
->args([service('serializer.mapping.class_metadata_factory')])
@@ -216,6 +220,6 @@
216220
])
217221

218222
->set('serializer.normalizer.backed_enum', BackedEnumNormalizer::class)
219-
->tag('serializer.normalizer', ['priority' => -915])
223+
->tag('serializer.normalizer', ['built_in' => true, 'priority' => -915])
220224
;
221225
};

src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer_debug.php

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
->args([
2222
service('debug.serializer.inner'),
2323
service('serializer.data_collector'),
24+
'default',
2425
])
2526

2627
->set('serializer.data_collector', SerializerDataCollector::class)

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php

+18
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,7 @@ protected static function getBundleDefaultConfig()
784784
'enabled' => true,
785785
'enable_attributes' => !class_exists(FullStack::class),
786786
'mapping' => ['paths' => []],
787+
'named_serializers' => [],
787788
],
788789
'property_access' => [
789790
'enabled' => true,
@@ -958,4 +959,21 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
958959
],
959960
];
960961
}
962+
963+
public function testNamedSerializersReservedName()
964+
{
965+
$processor = new Processor();
966+
$configuration = new Configuration(true);
967+
968+
$this->expectException(InvalidConfigurationException::class);
969+
$this->expectExceptionMessage('Invalid configuration for path "framework.serializer.named_serializers": "default" is a reserved name.');
970+
971+
$processor->processConfiguration($configuration, [[
972+
'serializer' => [
973+
'named_serializers' => [
974+
'default' => ['include_built_in_normalizers' => false],
975+
],
976+
],
977+
]]);
978+
}
961979
}

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/php/full.php

+7
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,13 @@
6666
'circular_reference_handler' => 'my.circular.reference.handler',
6767
'max_depth_handler' => 'my.max.depth.handler',
6868
'default_context' => ['enable_max_depth' => true],
69+
'named_serializers' => [
70+
'api' => [
71+
'include_built_in_normalizers' => true,
72+
'include_built_in_encoders' => true,
73+
'default_context' => ['enable_max_depth' => false],
74+
],
75+
],
6976
],
7077
'property_info' => true,
7178
'type_info' => true,

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/full.xml

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
<framework:default-context>
3939
<framework:enable_max_depth>true</framework:enable_max_depth>
4040
</framework:default-context>
41+
<framework:named-serializer name="api" include-built-in-normalizers="true" include-built-in-encoders="true">
42+
<framework:default-context>
43+
<framework:enable_max_depth>false</framework:enable_max_depth>
44+
</framework:default-context>
45+
</framework:named-serializer>
4146
</framework:serializer>
4247
<framework:property-info />
4348
<framework:type-info />

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/yml/full.yml

+6
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ framework:
5757
max_depth_handler: my.max.depth.handler
5858
default_context:
5959
enable_max_depth: true
60+
named_serializers:
61+
api:
62+
include_built_in_normalizers: true
63+
include_built_in_encoders: true
64+
default_context:
65+
enable_max_depth: false
6066
type_info: ~
6167
property_info: ~
6268
ide: file%%link%%format

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

+20
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,26 @@ public function testSerializerWithoutTranslator()
14631463
$this->assertFalse($container->hasDefinition('serializer.normalizer.translatable'));
14641464
}
14651465

1466+
public function testSerializerDefaultParameters()
1467+
{
1468+
$container = $this->createContainerFromFile('serializer_enabled');
1469+
$this->assertFalse($container->hasParameter('.serializer.name_converter'));
1470+
$this->assertFalse($container->hasParameter('serializer.default_context'));
1471+
$this->assertTrue($container->hasParameter('.serializer.named_serializers'));
1472+
$this->assertSame([], $container->getParameter('.serializer.named_serializers'));
1473+
}
1474+
1475+
public function testSerializerParametersAreSet()
1476+
{
1477+
$container = $this->createContainerFromFile('full');
1478+
$this->assertTrue($container->hasParameter('.serializer.name_converter'));
1479+
$this->assertSame('serializer.name_converter.camel_case_to_snake_case', $container->getParameter('.serializer.name_converter'));
1480+
$this->assertTrue($container->hasParameter('serializer.default_context'));
1481+
$this->assertSame(['enable_max_depth' => true], $container->getParameter('serializer.default_context'));
1482+
$this->assertTrue($container->hasParameter('.serializer.named_serializers'));
1483+
$this->assertSame(['api' => ['include_built_in_normalizers' => true, 'include_built_in_encoders' => true, 'default_context' => ['enable_max_depth' => false]]], $container->getParameter('.serializer.named_serializers'));
1484+
}
1485+
14661486
public function testRegisterSerializerExtractor()
14671487
{
14681488
$container = $this->createContainerFromFile('full');

src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.2
5+
---
6+
7+
* Add support for displaying profiles of multiple serializer instances
8+
49
7.1
510
---
611

0 commit comments

Comments
 (0)