Skip to content

Commit d557b52

Browse files
committed
[FrameworkBundle] [JsonEncoder] Wire services
1 parent 419da11 commit d557b52

File tree

19 files changed

+459
-1
lines changed

19 files changed

+459
-1
lines changed

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ CHANGELOG
2020
* Add the ability to use an existing service as a lock/semaphore resource
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
23+
* Add JsonEncoder services and configuration
2324

2425
7.1
2526
---

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: 36 additions & 0 deletions
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,38 @@ 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+
->arrayNode('encoder_cache_options')
2569+
->info('The options that will be used for encoder generation during cache warmup.')
2570+
->normalizeKeys(false)
2571+
->variablePrototype()->end()
2572+
->defaultValue([])
2573+
->end()
2574+
->arrayNode('decoder_cache_options')
2575+
->info('The options that will be used for decoder generation during cache warmup.')
2576+
->normalizeKeys(false)
2577+
->variablePrototype()->end()
2578+
->defaultValue([])
2579+
->end()
2580+
->end()
2581+
->end()
2582+
->end()
2583+
;
2584+
}
25492585
}

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

Lines changed: 49 additions & 1 deletion
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;
@@ -402,14 +408,22 @@ public function load(array $configs, ContainerBuilder $container): void
402408
$container->removeDefinition('console.command.serializer_debug');
403409
}
404410

405-
if ($this->readConfigEnabled('type_info', $container, $config['type_info'])) {
411+
if ($typeInfoEnabled = $this->readConfigEnabled('type_info', $container, $config['type_info'])) {
406412
$this->registerTypeInfoConfiguration($container, $loader);
407413
}
408414

409415
if ($propertyInfoEnabled) {
410416
$this->registerPropertyInfoConfiguration($container, $loader);
411417
}
412418

419+
if ($this->readConfigEnabled('json_encoder', $container, $config['json_encoder'])) {
420+
if (!$typeInfoEnabled) {
421+
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".'));
422+
}
423+
424+
$this->registerJsonEncoderConfiguration($config['json_encoder'], $container, $loader);
425+
}
426+
413427
if ($this->readConfigEnabled('lock', $container, $config['lock'])) {
414428
$this->registerLockConfiguration($config['lock'], $container, $loader);
415429
}
@@ -1960,6 +1974,40 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
19601974
$container->setParameter('.serializer.named_serializers', $config['named_serializers'] ?? []);
19611975
}
19621976

1977+
private function registerJsonEncoderConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void
1978+
{
1979+
if (!class_exists(JsonEncoder::class)) {
1980+
throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".');
1981+
}
1982+
1983+
$container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class)
1984+
->addTag('json_encoder.normalizer');
1985+
$container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class)
1986+
->addTag('json_encoder.denormalizer');
1987+
1988+
$loader->load('json_encoder.php');
1989+
1990+
$container->registerAliasForArgument('json_encoder.encoder', JsonEncoderEncoderInterface::class, 'json.encoder');
1991+
$container->registerAliasForArgument('json_encoder.decoder', JsonEncoderDecoderInterface::class, 'json.decoder');
1992+
1993+
$container->setParameter('json_encoder.encoders_dir', '%kernel.cache_dir%/json_encoder/encoder');
1994+
$container->setParameter('json_encoder.decoders_dir', '%kernel.cache_dir%/json_encoder/decoder');
1995+
$container->setParameter('json_encoder.lazy_ghosts_dir', '%kernel.cache_dir%/json_encoder/lazy_ghost');
1996+
1997+
$container->getDefinition('.json_encoder.cache_warmer.encoder_decoder')
1998+
->replaceArgument(5, $config['encoder_cache_options'])
1999+
->replaceArgument(6, $config['decoder_cache_options']);
2000+
2001+
$encodableDefinition = (new Definition())
2002+
->setAbstract(true)
2003+
->addTag('container.excluded')
2004+
->addTag('json_encoder.encodable');
2005+
2006+
foreach ($config['paths'] as $namespace => $path) {
2007+
$loader->registerClasses($encodableDefinition, $namespace, $path);
2008+
}
2009+
}
2010+
19632011
private function registerPropertyInfoConfiguration(ContainerBuilder $container, PhpFileLoader $loader): void
19642012
{
19652013
if (!interface_exists(PropertyInfoExtractorInterface::class)) {
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
])
36+
->set('json_encoder.decoder', JsonDecoder::class)
37+
->args([
38+
tagged_locator('json_encoder.denormalizer'),
39+
service('json_encoder.decode.property_metadata_loader'),
40+
param('json_encoder.decoders_dir'),
41+
param('json_encoder.lazy_ghosts_dir'),
42+
])
43+
->alias(JsonEncoder::class, 'json_encoder.encoder')
44+
->alias(JsonDecoder::class, 'json_encoder.decoder')
45+
46+
// metadata
47+
->stack('json_encoder.encode.property_metadata_loader', [
48+
inline_service(EncodeAttributePropertyMetadataLoader::class)
49+
->args([
50+
service('.inner'),
51+
tagged_locator('json_encoder.normalizer'),
52+
]),
53+
inline_service(EncodeDateTimeTypePropertyMetadataLoader::class)
54+
->args([
55+
service('.inner'),
56+
]),
57+
inline_service(GenericTypePropertyMetadataLoader::class)
58+
->args([
59+
service('.inner'),
60+
service('type_info.type_context_factory'),
61+
]),
62+
inline_service(PropertyMetadataLoader::class)
63+
->args([
64+
service('type_info.resolver'),
65+
]),
66+
])
67+
68+
->stack('json_encoder.decode.property_metadata_loader', [
69+
inline_service(DecodeAttributePropertyMetadataLoader::class)
70+
->args([
71+
service('.inner'),
72+
tagged_locator('json_encoder.denormalizer'),
73+
]),
74+
inline_service(DecodeDateTimeTypePropertyMetadataLoader::class)
75+
->args([
76+
service('.inner'),
77+
]),
78+
inline_service(GenericTypePropertyMetadataLoader::class)
79+
->args([
80+
service('.inner'),
81+
service('type_info.type_context_factory'),
82+
]),
83+
inline_service(PropertyMetadataLoader::class)
84+
->args([
85+
service('type_info.resolver'),
86+
]),
87+
])
88+
89+
// normalizers/denormalizers
90+
->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class)
91+
->tag('json_encoder.normalizer')
92+
->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class)
93+
->args([
94+
false,
95+
])
96+
->tag('json_encoder.denormalizer')
97+
->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class)
98+
->args([
99+
true,
100+
])
101+
->tag('json_encoder.denormalizer')
102+
103+
// cache
104+
->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class)
105+
->args([
106+
tagged_iterator('json_encoder.encodable'),
107+
service('json_encoder.encode.property_metadata_loader'),
108+
service('json_encoder.decode.property_metadata_loader'),
109+
param('json_encoder.encoders_dir'),
110+
param('json_encoder.decoders_dir'),
111+
abstract_arg('encoder cache generation config'),
112+
abstract_arg('decoder cache generation config'),
113+
service('logger')->ignoreOnInvalid(),
114+
])
115+
->tag('kernel.cache_warmer')
116+
117+
->set('.json_encoder.cache_warmer.lazy_ghost', LazyGhostCacheWarmer::class)
118+
->args([
119+
tagged_iterator('json_encoder.encodable'),
120+
param('json_encoder.lazy_ghosts_dir'),
121+
])
122+
->tag('kernel.cache_warmer')
123+
;
124+
};

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

Lines changed: 12 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" />
@@ -992,4 +993,15 @@
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:element name="cache-encoder-config" type="metadata" minOccurs="0" maxOccurs="1" />
1003+
<xsd:element name="cache-decoder-config" type="metadata" minOccurs="0" maxOccurs="1" />
1004+
</xsd:choice>
1005+
<xsd:attribute name="enabled" type="xsd:boolean" />
1006+
</xsd:complexType>
9951007
</xsd:schema>

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,6 +959,12 @@ 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+
'encoder_cache_options' => [],
966+
'decoder_cache_options' => [],
967+
],
962968
];
963969
}
964970

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