Skip to content

Commit e213884

Browse files
committed
[FrameworkBundle] [JsonEncoder] Wire services
1 parent 2d54786 commit e213884

File tree

18 files changed

+418
-1
lines changed

18 files changed

+418
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add support for assets pre-compression
88
* Rename `TranslationUpdateCommand` to `TranslationExtractCommand`
9+
* Add JsonEncoder services and configuration
910

1011
7.2
1112
---

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ class UnusedTagsPass implements CompilerPassInterface
5353
'form.type_guesser',
5454
'html_sanitizer',
5555
'http_client.client',
56+
'json_encoder.denormalizer',
57+
'json_encoder.encodable',
58+
'json_encoder.normalizer',
5659
'kernel.cache_clearer',
5760
'kernel.cache_warmer',
5861
'kernel.event_listener',

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use Symfony\Component\HttpClient\HttpClient;
3232
use Symfony\Component\HttpFoundation\Cookie;
3333
use Symfony\Component\HttpFoundation\IpUtils;
34+
use Symfony\Component\JsonEncoder\EncoderInterface;
3435
use Symfony\Component\Lock\Lock;
3536
use Symfony\Component\Lock\Store\SemaphoreStore;
3637
use Symfony\Component\Mailer\Mailer;
@@ -181,6 +182,7 @@ public function getConfigTreeBuilder(): TreeBuilder
181182
$this->addHtmlSanitizerSection($rootNode, $enableIfStandalone);
182183
$this->addWebhookSection($rootNode, $enableIfStandalone);
183184
$this->addRemoteEventSection($rootNode, $enableIfStandalone);
185+
$this->addJsonEncoderSection($rootNode, $enableIfStandalone);
184186

185187
return $treeBuilder;
186188
}
@@ -2570,4 +2572,26 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable
25702572
->end()
25712573
;
25722574
}
2575+
2576+
private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void
2577+
{
2578+
$rootNode
2579+
->children()
2580+
->arrayNode('json_encoder')
2581+
->info('JSON encoder configuration')
2582+
->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}()
2583+
->fixXmlConfig('path')
2584+
->children()
2585+
->arrayNode('paths')
2586+
->info('Namespaces and paths of encodable/decodable classes.')
2587+
->normalizeKeys(false)
2588+
->useAttributeAsKey('namespace')
2589+
->scalarPrototype()->end()
2590+
->defaultValue([])
2591+
->end()
2592+
->end()
2593+
->end()
2594+
->end()
2595+
;
2596+
}
25732597
}

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@
9999
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
100100
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
101101
use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator;
102+
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface;
103+
use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface;
104+
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface;
105+
use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface;
106+
use Symfony\Component\JsonEncoder\JsonEncoder;
102107
use Symfony\Component\Lock\LockFactory;
103108
use Symfony\Component\Lock\LockInterface;
104109
use Symfony\Component\Lock\PersistingStoreInterface;
@@ -176,6 +181,7 @@
176181
use Symfony\Component\TypeInfo\Type;
177182
use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver;
178183
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
184+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;
179185
use Symfony\Component\Uid\Factory\UuidFactory;
180186
use Symfony\Component\Uid\UuidV4;
181187
use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider;
@@ -414,14 +420,22 @@ public function load(array $configs, ContainerBuilder $container): void
414420
$container->removeDefinition('console.command.serializer_debug');
415421
}
416422

417-
if ($this->readConfigEnabled('type_info', $container, $config['type_info'])) {
423+
if ($typeInfoEnabled = $this->readConfigEnabled('type_info', $container, $config['type_info'])) {
418424
$this->registerTypeInfoConfiguration($container, $loader);
419425
}
420426

421427
if ($propertyInfoEnabled) {
422428
$this->registerPropertyInfoConfiguration($container, $loader);
423429
}
424430

431+
if ($this->readConfigEnabled('json_encoder', $container, $config['json_encoder'])) {
432+
if (!$typeInfoEnabled) {
433+
throw new LogicException('JsonEncoder support cannot be enabled as the TypeInfo component is not '.(interface_exists(TypeResolverInterface::class) ? 'enabled.' : 'installed. Try running "composer require symfony/type-info".'));
434+
}
435+
436+
$this->registerJsonEncoderConfiguration($config['json_encoder'], $container, $loader);
437+
}
438+
425439
if ($this->readConfigEnabled('lock', $container, $config['lock'])) {
426440
$this->registerLockConfiguration($config['lock'], $container, $loader);
427441
}
@@ -1990,6 +2004,36 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19902004
$container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []);
19912005
}
19922006

2007+
private function registerJsonEncoderConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void
2008+
{
2009+
if (!class_exists(JsonEncoder::class)) {
2010+
throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".');
2011+
}
2012+
2013+
$container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class)
2014+
->addTag('json_encoder.normalizer');
2015+
$container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class)
2016+
->addTag('json_encoder.denormalizer');
2017+
2018+
$loader->load('json_encoder.php');
2019+
2020+
$container->registerAliasForArgument('json_encoder.encoder', JsonEncoderEncoderInterface::class, 'json.encoder');
2021+
$container->registerAliasForArgument('json_encoder.decoder', JsonEncoderDecoderInterface::class, 'json.decoder');
2022+
2023+
$container->setParameter('.json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder');
2024+
$container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder');
2025+
$container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost');
2026+
2027+
$encodableDefinition = (new Definition())
2028+
->setAbstract(true)
2029+
->addTag('container.excluded')
2030+
->addTag('json_encoder.encodable');
2031+
2032+
foreach ($config['paths'] as $namespace => $path) {
2033+
$loader->registerClasses($encodableDefinition, $namespace, $path);
2034+
}
2035+
}
2036+
19932037
private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void
19942038
{
19952039
if (!interface_exists(PropertyInfoExtractorInterface::class)) {
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
13+
14+
use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer;
15+
use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer;
16+
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer;
17+
use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer;
18+
use Symfony\Component\JsonEncoder\JsonDecoder;
19+
use Symfony\Component\JsonEncoder\JsonEncoder;
20+
use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader;
21+
use Symfony\Component\JsonEncoder\Mapping\Decode\DateTimeTypePropertyMetadataLoader as DecodeDateTimeTypePropertyMetadataLoader;
22+
use Symfony\Component\JsonEncoder\Mapping\Encode\AttributePropertyMetadataLoader as EncodeAttributePropertyMetadataLoader;
23+
use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader;
24+
use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader;
25+
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader;
26+
27+
return static function (ContainerConfigurator $container) {
28+
$container->services()
29+
// encoder/decoder
30+
->set('json_encoder.encoder', JsonEncoder::class)
31+
->args([
32+
tagged_locator('json_encoder.normalizer'),
33+
service('json_encoder.encode.property_metadata_loader'),
34+
param('.json_encoder.encoders_dir'),
35+
false,
36+
])
37+
->set('json_encoder.decoder', JsonDecoder::class)
38+
->args([
39+
tagged_locator('json_encoder.denormalizer'),
40+
service('json_encoder.decode.property_metadata_loader'),
41+
param('.json_encoder.decoders_dir'),
42+
param('.json_encoder.lazy_ghosts_dir'),
43+
])
44+
->alias(JsonEncoder::class, 'json_encoder.encoder')
45+
->alias(JsonDecoder::class, 'json_encoder.decoder')
46+
47+
// metadata
48+
->stack('json_encoder.encode.property_metadata_loader', [
49+
inline_service(EncodeAttributePropertyMetadataLoader::class)
50+
->args([
51+
service('.inner'),
52+
tagged_locator('json_encoder.normalizer'),
53+
service('type_info.resolver'),
54+
]),
55+
inline_service(EncodeDateTimeTypePropertyMetadataLoader::class)
56+
->args([
57+
service('.inner'),
58+
]),
59+
inline_service(GenericTypePropertyMetadataLoader::class)
60+
->args([
61+
service('.inner'),
62+
service('type_info.type_context_factory'),
63+
]),
64+
inline_service(PropertyMetadataLoader::class)
65+
->args([
66+
service('type_info.resolver'),
67+
]),
68+
])
69+
70+
->stack('json_encoder.decode.property_metadata_loader', [
71+
inline_service(DecodeAttributePropertyMetadataLoader::class)
72+
->args([
73+
service('.inner'),
74+
tagged_locator('json_encoder.denormalizer'),
75+
service('type_info.resolver'),
76+
]),
77+
inline_service(DecodeDateTimeTypePropertyMetadataLoader::class)
78+
->args([
79+
service('.inner'),
80+
]),
81+
inline_service(GenericTypePropertyMetadataLoader::class)
82+
->args([
83+
service('.inner'),
84+
service('type_info.type_context_factory'),
85+
]),
86+
inline_service(PropertyMetadataLoader::class)
87+
->args([
88+
service('type_info.resolver'),
89+
]),
90+
])
91+
92+
// normalizers/denormalizers
93+
->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class)
94+
->tag('json_encoder.normalizer')
95+
->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class)
96+
->args([
97+
false,
98+
])
99+
->tag('json_encoder.denormalizer')
100+
->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class)
101+
->args([
102+
true,
103+
])
104+
->tag('json_encoder.denormalizer')
105+
106+
// cache
107+
->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class)
108+
->args([
109+
tagged_iterator('json_encoder.encodable'),
110+
service('json_encoder.encode.property_metadata_loader'),
111+
service('json_encoder.decode.property_metadata_loader'),
112+
param('.json_encoder.encoders_dir'),
113+
param('.json_encoder.decoders_dir'),
114+
false,
115+
service('logger')->ignoreOnInvalid(),
116+
])
117+
->tag('kernel.cache_warmer')
118+
119+
->set('.json_encoder.cache_warmer.lazy_ghost', LazyGhostCacheWarmer::class)
120+
->args([
121+
tagged_iterator('json_encoder.encodable'),
122+
param('.json_encoder.lazy_ghosts_dir'),
123+
])
124+
->tag('kernel.cache_warmer')
125+
;
126+
};

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
<xsd:element name="enabled-locale" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
4747
<xsd:element name="webhook" type="webhook" minOccurs="0" maxOccurs="1" />
4848
<xsd:element name="remote-event" type="remote-event" minOccurs="0" maxOccurs="1" />
49+
<xsd:element name="json-encoder" type="json-encoder" minOccurs="0" maxOccurs="1" />
4950
</xsd:choice>
5051

5152
<xsd:attribute name="http-method-override" type="xsd:boolean" />
@@ -1003,4 +1004,13 @@
10031004
<xsd:complexType name="remote-event">
10041005
<xsd:attribute name="enabled" type="xsd:boolean" />
10051006
</xsd:complexType>
1007+
1008+
<xsd:complexType name="json-encoder">
1009+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
1010+
<xsd:sequence>
1011+
<xsd:element name="default-context" type="metadata" minOccurs="0" maxOccurs="1" />
1012+
</xsd:sequence>
1013+
</xsd:choice>
1014+
<xsd:attribute name="enabled" type="xsd:boolean" />
1015+
</xsd:complexType>
10061016
</xsd:schema>

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,10 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
970970
'remote-event' => [
971971
'enabled' => !class_exists(FullStack::class) && class_exists(RemoteEvent::class),
972972
],
973+
'json_encoder' => [
974+
'enabled' => false,
975+
'paths' => [],
976+
],
973977
];
974978
}
975979

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
$container->loadFromExtension('framework', [
4+
'annotations' => false,
5+
'http_method_override' => false,
6+
'handle_all_throwables' => true,
7+
'php_errors' => ['log' => true],
8+
'type_info' => [
9+
'enabled' => true,
10+
],
11+
'json_encoder' => [
12+
'enabled' => true,
13+
],
14+
]);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@
4646
</framework:serializer>
4747
<framework:property-info />
4848
<framework:type-info />
49+
<framework:json-encoder />
4950
</framework:config>
5051
</container>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xmlns:framework="http://symfony.com/schema/dic/symfony"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services https://symfony.com/schema/dic/services/services-1.0.xsd
6+
http://symfony.com/schema/dic/symfony https://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
7+
8+
<framework:config http-method-override="false" handle-all-throwables="true">
9+
<framework:annotations enabled="false" />
10+
<framework:php-errors log="true" />
11+
<framework:type-info enabled="true" />
12+
<framework:json-encoder enabled="true" />
13+
</framework:config>
14+
</container>

0 commit comments

Comments
 (0)