Skip to content

Commit a619f6f

Browse files
committed
[FrameworkBundle] [JsonEncoder] Wire services
1 parent aed69bb commit a619f6f

File tree

18 files changed

+418
-1
lines changed

18 files changed

+418
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ CHANGELOG
2121
* Add support for configuring multiple serializer instances via the configuration
2222
* Add support for `SYMFONY_TRUSTED_PROXIES`, `SYMFONY_TRUSTED_HEADERS`, `SYMFONY_TRUST_X_SENDFILE_TYPE_HEADER` and `SYMFONY_TRUSTED_HOSTS` env vars
2323
* Add `--no-fill` option to `translation:extract` command
24+
* Add JsonEncoder services and configuration
2425

2526
7.1
2627
---

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

+3
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

+24
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
use Symfony\Component\HttpClient\HttpClient;
3131
use Symfony\Component\HttpFoundation\Cookie;
3232
use Symfony\Component\HttpFoundation\IpUtils;
33+
use Symfony\Component\JsonEncoder\EncoderInterface;
3334
use Symfony\Component\Lock\Lock;
3435
use Symfony\Component\Lock\Store\SemaphoreStore;
3536
use Symfony\Component\Mailer\Mailer;
@@ -180,6 +181,7 @@ public function getConfigTreeBuilder(): TreeBuilder
180181
$this->addHtmlSanitizerSection($rootNode, $enableIfStandalone);
181182
$this->addWebhookSection($rootNode, $enableIfStandalone);
182183
$this->addRemoteEventSection($rootNode, $enableIfStandalone);
184+
$this->addJsonEncoderSection($rootNode, $enableIfStandalone);
183185

184186
return $treeBuilder;
185187
}
@@ -2546,4 +2548,26 @@ private function addHtmlSanitizerSection(ArrayNodeDefinition $rootNode, callable
25462548
->end()
25472549
;
25482550
}
2551+
2552+
private function addJsonEncoderSection(ArrayNodeDefinition $rootNode, callable $enableIfStandalone): void
2553+
{
2554+
$rootNode
2555+
->children()
2556+
->arrayNode('json_encoder')
2557+
->info('JSON encoder configuration')
2558+
->{$enableIfStandalone('symfony/json-encoder', EncoderInterface::class)}()
2559+
->fixXmlConfig('path')
2560+
->children()
2561+
->arrayNode('paths')
2562+
->info('Namespaces and paths of encodable/decodable classes.')
2563+
->normalizeKeys(false)
2564+
->useAttributeAsKey('namespace')
2565+
->scalarPrototype()->end()
2566+
->defaultValue([])
2567+
->end()
2568+
->end()
2569+
->end()
2570+
->end()
2571+
;
2572+
}
25492573
}

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

+45-1
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@
9898
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
9999
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
100100
use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator;
101+
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface;
102+
use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface;
103+
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface;
104+
use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface;
105+
use Symfony\Component\JsonEncoder\JsonEncoder;
101106
use Symfony\Component\Lock\LockFactory;
102107
use Symfony\Component\Lock\LockInterface;
103108
use Symfony\Component\Lock\PersistingStoreInterface;
@@ -175,6 +180,7 @@
175180
use Symfony\Component\TypeInfo\Type;
176181
use Symfony\Component\TypeInfo\TypeResolver\PhpDocAwareReflectionTypeResolver;
177182
use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver;
183+
use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface;
178184
use Symfony\Component\Uid\Factory\UuidFactory;
179185
use Symfony\Component\Uid\UuidV4;
180186
use Symfony\Component\Validator\Constraints\ExpressionLanguageProvider;
@@ -406,14 +412,22 @@ public function load(array $configs, ContainerBuilder $container): void
406412
$container->removeDefinition('console.command.serializer_debug');
407413
}
408414

409-
if ($this->readConfigEnabled('type_info', $container, $config['type_info'])) {
415+
if ($typeInfoEnabled = $this->readConfigEnabled('type_info', $container, $config['type_info'])) {
410416
$this->registerTypeInfoConfiguration($container, $loader);
411417
}
412418

413419
if ($propertyInfoEnabled) {
414420
$this->registerPropertyInfoConfiguration($container, $loader);
415421
}
416422

423+
if ($this->readConfigEnabled('json_encoder', $container, $config['json_encoder'])) {
424+
if (!$typeInfoEnabled) {
425+
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".'));
426+
}
427+
428+
$this->registerJsonEncoderConfiguration($config['json_encoder'], $container, $loader);
429+
}
430+
417431
if ($this->readConfigEnabled('lock', $container, $config['lock'])) {
418432
$this->registerLockConfiguration($config['lock'], $container, $loader);
419433
}
@@ -1962,6 +1976,36 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19621976
$container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []);
19631977
}
19641978

1979+
private function registerJsonEncoderConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void
1980+
{
1981+
if (!class_exists(JsonEncoder::class)) {
1982+
throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".');
1983+
}
1984+
1985+
$container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class)
1986+
->addTag('json_encoder.normalizer');
1987+
$container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class)
1988+
->addTag('json_encoder.denormalizer');
1989+
1990+
$loader->load('json_encoder.php');
1991+
1992+
$container->registerAliasForArgument('json_encoder.encoder', JsonEncoderEncoderInterface::class, 'json.encoder');
1993+
$container->registerAliasForArgument('json_encoder.decoder', JsonEncoderDecoderInterface::class, 'json.decoder');
1994+
1995+
$container->setParameter('.json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder');
1996+
$container->setParameter('.json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder');
1997+
$container->setParameter('.json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost');
1998+
1999+
$encodableDefinition = (new Definition())
2000+
->setAbstract(true)
2001+
->addTag('container.excluded')
2002+
->addTag('json_encoder.encodable');
2003+
2004+
foreach ($config['paths'] as $namespace => $path) {
2005+
$loader->registerClasses($encodableDefinition, $namespace, $path);
2006+
}
2007+
}
2008+
19652009
private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void
19662010
{
19672011
if (!interface_exists(PropertyInfoExtractorInterface::class)) {
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

+10
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" />
@@ -992,4 +993,13 @@
992993
<xsd:complexType name="remote-event">
993994
<xsd:attribute name="enabled" type="xsd:boolean" />
994995
</xsd:complexType>
996+
997+
<xsd:complexType name="json-encoder">
998+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
999+
<xsd:sequence>
1000+
<xsd:element name="default-context" type="metadata" minOccurs="0" maxOccurs="1" />
1001+
</xsd:sequence>
1002+
</xsd:choice>
1003+
<xsd:attribute name="enabled" type="xsd:boolean" />
1004+
</xsd:complexType>
9951005
</xsd:schema>

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

+4
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,10 @@ class_exists(SemaphoreStore::class) && SemaphoreStore::isSupported() ? 'semaphor
959959
'remote-event' => [
960960
'enabled' => !class_exists(FullStack::class) && class_exists(RemoteEvent::class),
961961
],
962+
'json_encoder' => [
963+
'enabled' => false,
964+
'paths' => [],
965+
],
962966
];
963967
}
964968

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

+1
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>
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>

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

+1
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,4 @@ framework:
7070
formats:
7171
csv: ['text/csv', 'text/plain']
7272
pdf: 'application/pdf'
73+
json_encoder: ~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
framework:
2+
annotations: false
3+
http_method_override: false
4+
handle_all_throwables: true
5+
php_errors:
6+
log: true
7+
type_info:
8+
enabled: true
9+
json_encoder:
10+
enabled: true

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

+6
Original file line numberDiff line numberDiff line change
@@ -2497,6 +2497,12 @@ public function testSemaphoreWithService()
24972497
self::assertEquals(new Reference('my_service'), $storeDef->getArgument(0));
24982498
}
24992499

2500+
public function testJsonEncoderEnabled()
2501+
{
2502+
$container = $this->createContainerFromFile('json_encoder');
2503+
$this->assertTrue($container->has('json_encoder.encoder'));
2504+
}
2505+
25002506
protected function createContainer(array $data = [])
25012507
{
25022508
return new ContainerBuilder(new EnvPlaceholderParameterBag(array_merge([

0 commit comments

Comments
 (0)