8000 [JsonEncoder] Replace normalizers by value transformers · symfony/symfony@2049277 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2049277

Browse files
committed
[JsonEncoder] Replace normalizers by value transformers
1 parent 4dcb217 commit 2049277

File tree

118 files changed

+832
-880
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

118 files changed

+832
-880
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,8 @@ class UnusedTagsPass implements CompilerPassInterface
5353
'form.type_guesser',
5454
'html_sanitizer',
5555
'http_client.client',
56-
'json_encoder.denormalizer',
5756
'json_encoder.encodable',
58-
'json_encoder.normalizer',
57+
'json_encoder.value_transformer',
5958
'kernel.cache_clearer',
6059
'kernel.cache_warmer',
6160
'kernel.event_listener',

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,10 @@
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;
103102
use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface;
104-
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface;
105103
use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface;
106104
use Symfony\Component\JsonEncoder\JsonEncoder;
105+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
107106
use Symfony\Component\Lock\LockFactory;
108107
use Symfony\Component\Lock\LockInterface;
109108
use Symfony\Component\Lock\PersistingStoreInterface;
@@ -2011,10 +2010,8 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde
20112010
throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".');
20122011
}
20132012

2014-
$container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class)
2015-
->addTag('json_encoder.normalizer');
2016-
$container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class)
2017-
->addTag('json_encoder.denormalizer');
2013+
$container->registerForAutoconfiguration(ValueTransformerInterface::class)
2014+
->addTag('json_encoder.value_transformer');
20182015

20192016
$loader->load('json_encoder.php');
20202017

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

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313

1414
use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer;
1515
use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer;
16-
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer;
17-
use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer;
1816
use Symfony\Component\JsonEncoder\JsonDecoder;
1917
use Symfony\Component\JsonEncoder\JsonEncoder;
2018
use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader;
@@ -23,19 +21,21 @@
2321
use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader;
2422
use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader;
2523
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader;
24+
use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer;
25+
use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer;
2626

2727
return static function (ContainerConfigurator $container) {
2828
$container->services()
2929
// encoder/decoder
3030
->set('json_encoder.encoder', JsonEncoder::class)
3131
->args([
32-
tagged_locator('json_encoder.normalizer'),
32+
tagged_locator('json_encoder.value_transformer'),
3333
service('json_encoder.encode.property_metadata_loader'),
3434
param('.json_encoder.encoders_dir'),
3535
])
3636
->set('json_encoder.decoder', JsonDecoder::class)
3737
->args([
38-
tagged_locator('json_encoder.denormalizer'),
38+
tagged_locator('json_encoder.value_transformer'),
3939
service('json_encoder.decode.property_metadata_loader'),
4040
param('.json_encoder.decoders_dir'),
4141
param('.json_encoder.lazy_ghosts_dir'),
@@ -63,7 +63,7 @@
6363
->decorate('json_encoder.encode.property_metadata_loader')
6464
->args([
6565
service('.inner'),
66-
tagged_locator('json_encoder.normalizer'),
66+
tagged_locator('json_encoder.value_transformer'),
6767
service('type_info.resolver'),
6868
])
6969

@@ -86,23 +86,16 @@
8686
->decorate('json_encoder.decode.property_metadata_loader')
8787
->args([
8888
service('.inner'),
89-
tagged_locator('json_encoder.normalizer'),
89+
tagged_locator('json_encoder.value_transformer'),
9090
service('type_info.resolver'),
9191
])
9292

93-
// normalizers/denormalizers
94-
->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class)
95-
->tag('json_encoder.normalizer')
96-
->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class)
97-
->args([
98-
false,
99-
])
100-
->tag('json_encoder.denormalizer')
101-
->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class)
102-
->args([
103-
true,
104-
])
105-
->tag('json_encoder.denormalizer')
93+
// value transformers
94+
->set('json_encoder.value_transformer.date_time_to_string', DateTimeToStringValueTransformer::class)
95+
->tag('json_encoder.value_transformer')
96+
97+
->set('json_encoder.value_transformer.string_to_date_time', StringToDateTimeValueTransformer::class)
98+
->tag('json_encoder.value_transformer')
10699

107100
// cache
108101
->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class)

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,20 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto;
1313

14-
use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer;
15-
use Symfony\Component\JsonEncoder\Attribute\Denormalizer;
14+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer;
15+
use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer;
1616
use Symfony\Component\JsonEncoder\Attribute\EncodedName;
17-
use Symfony\Component\JsonEncoder\Attribute\Normalizer;
17+
use Symfony\Component\JsonEncoder\Attribute\ValueTransformer;
1818

1919
/**
2020
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
2121
*/
2222
class Dummy
2323
{
2424
#[EncodedName('@name')]
25-
#[Normalizer('strtoupper')]
26-
#[Denormalizer('strtolower')]
25+
#[ValueTransformer(toJsonValue: 'strtoupper', toNativeValue: 'strtolower')]
2726
public string $name = 'dummy';
2827

29-
#[Normalizer(RangeNormalizer::class)]
30-
#[Denormalizer(RangeNormalizer::class)]
28+
#[ValueTransformer(toJsonValue: RangeToStringValueTransformer::class, toNativeValue: StringToRangeValueTransformer::class)]
3129
public array $range = [10, 20];
3230
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder;
13+
14+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
15+
use Symfony\Component\TypeInfo\Type;
16+
use Symfony\Component\TypeInfo\Type\BuiltinType;
17+
18+
/**
19+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
20+
*/
21+
class RangeToStringValueTransformer implements ValueTransformerInterface
22+
{
23+
public function transform(mixed $value, array $options = []): string
24+
{
25+
return $value[0].'..'.$value[1];
26+
}
27+
28+
public static function getJsonValueType(): BuiltinType
29+
{
30+
return Type::string();
31+
}
32+
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeNormalizer.php renamed to src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,21 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder;
1313

14-
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface;
15-
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface;
14+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
1615
use Symfony\Component\TypeInfo\Type;
1716
use Symfony\Component\TypeInfo\Type\BuiltinType;
1817

1918
/**
2019
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
2120
*/
22-
class RangeNormalizer implements NormalizerInterface, DenormalizerInterface
21+
class StringToRangeValueTransformer implements ValueTransformerInterface
2322
{
24-
public function normalize(mixed $denormalized, array $options = []): string
23+
public function transform(mixed $value, array $options = []): array
2524
{
26-
return $denormalized[0].'..'.$denormalized[1];
25+
return array_map(static fn (string $v): int => (int) $v, explode('..', $value));
2726
}
2827

29-
public function denormalize(mixed $normalized, array $options = []): array
30-
{
31-
return array_map(static fn (string $v): int => (int) $v, explode('..', $normalized));
32-
}
33-
34-
public static function getNormalizedType(): BuiltinType
28+
public static function getJsonValueType(): BuiltinType
3529
{
3630
return Type::string();
3731
}

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ services:
1818
alias: json_encoder.decoder
1919
public: true
2020

21-
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~
21+
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer: ~
22+
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer: ~

src/Symfony/Component/JsonEncoder/Attribute/Denormalizer.php

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/Symfony/Component/JsonEncoder/Attribute/Normalizer.php

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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\JsonEncoder\Attribute;
13+
14+
use Symfony\Component\JsonEncoder\Exception\LogicException;
15+
16+
/**
17+
* Defines a callable or a {@see \Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface} service id
18+
* that will be used to transform the property data during encoding and decoding.
19+
*
20+
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
21+
*
22+
* @experimental
23+
*/
24+
#[\Attribute(\Attribute::TARGET_PROPERTY)]
25+
class ValueTransformer
26+
{
27+
private \Closure|string|null $toNativeValue;
28+
private \Closure|string|null $toJsonValue;
29+
30+
/**
31+
* @param (callable(mixed, array<string, mixed>?): mixed)|(callable(mixed): mixed)|string|null $toNativeValue
32+
* @param (callable(mixed, array<string, mixed>?): mixed)|(callable(mixed): mixed)|string|null $toJsonValue
33+
*/
34+
public function __construct(
35+
callable|string|null $toNativeValue = null,
36+
callable|string|null $toJsonValue = null,
37+
) {
38+
if (!$toNativeValue && !$toJsonValue) {
39+
throw new LogicException('#[ValueTransformer] attribute must declare either $toNativeValue or $toJsonValue.');
40+
}
41+
42+
if (\is_callable($toNativeValue)) {
43+
$toNativeValue = $toNativeValue(...);
44+
}
45+
46+
if (\is_callable($toJsonValue)) {
47+
$toJsonValue = $toJsonValue(...);
48+
}
49+
50+
$this->toNativeValue = $toNativeValue;
51+
$this->toJsonValue = $toJsonValue;
52+
}
53+
54+
public function getToNativeValueTransformer(): string|\Closure|null
55+
{
56+
return $this->toNativeValue;
57+
}
58+
59+
public function getToJsonValueTransformer(): string|\Closure|null
60+
{
61+
return $this->toJsonValue;
62+
}
63+
}

src/Symfony/Component/JsonEncoder/Decode/DecoderGenerator.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,23 +136,23 @@ public function createDataModel(Type $type, array $options = [], array $context
136136
'name' => $propertyMetadata->getName(),
137137
'value' => $this->createDataModel($propertyMetadata->getType(), $options, $context),
138138
'accessor' => function (DataAccessorInterface $accessor) use ($propertyMetadata): DataAccessorInterface {
139-
foreach ($propertyMetadata->getDenormalizers() as $denormalizer) {
140-
if (\is_string($denormalizer)) {
141-
$denormalizerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($denormalizer)], new VariableDataAccessor('denormalizers'));
142-
$accessor = new FunctionDataAccessor('denormalize', [$accessor, new VariableDataAccessor('options')], $denormalizerServiceAccessor);
139+
foreach ($propertyMetadata->getToNativeValueTransformers() as $valueTransformer) {
140+
if (\is_string($valueTransformer)) {
141+
$valueTransformerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($valueTransformer)], new VariableDataAccessor('valueTransformers'));
142+
$accessor = new FunctionDataAccessor('transform', [$accessor, new VariableDataAccessor('options')], $valueTransformerServiceAccessor);
143143

144144
continue;
145145
}
146146

147147
try {
148-
$functionReflection = new \ReflectionFunction($denormalizer);
148+
$functionReflection = new \ReflectionFunction($valueTransformer);
149149
} catch (\ReflectionException $e) {
150150
throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
151151
}
152152

153-
$functionName = !$functionReflection->getClosureScopeClass()
153+
$functionName = !$functionReflection->getClosureCalledClass()
154154
? $functionReflection->getName()
155-
: \sprintf('%s::%s', $functionReflection->getClosureScopeClass()->getName(), $functionReflection->getName());
155+
: \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName());
156156
$arguments = $functionReflection->isUserDefined() ? [$accessor, new VariableDataAccessor('options')] : [$accessor];
157157

158158
$accessor = new FunctionDataAccessor($functionName, $arguments);

0 commit comments

Comments
 (0)
0