Skip to content

Commit 30d8c31

Browse files
committed
feature #58515 [FrameworkBundle][JsonEncoder] Wire services (mtarld)
This PR was merged into the 7.3 branch. Discussion ---------- [FrameworkBundle][JsonEncoder] Wire services | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | | License | MIT Follow-up of * #51718 The FrameworkBundle part of the JsonEncoder component introduction. The component related config is quite simple: ```yaml framework: json_encoder: paths: App\EncodableDto\: '../src/EncodableDto/*' ``` Plus, the framework integration proposes the following bindings: - `EncoderInterface $jsonEncoder` to the `json_encoder.encoder` service - `DecoderInterface $jsonDecoder` to the `json_encoder.decoder` service --- As this PR is based on top of #51718, only the last commit should be considered. Commits ------- e213884 [FrameworkBundle] [JsonEncoder] Wire services
2 parents cd84496 + e213884 commit 30d8c31

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy