Skip to content

Commit 68944f5

Browse files
committed
feature #59290 [JsonEncoder] Replace normalizers by value transformers (mtarld)
This PR was merged into the 7.3 branch. Discussion ---------- [JsonEncoder] Replace normalizers by value transformers | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #59288 | License | MIT For a better DX and to avoid confusion with Serializer's normalizers and denormalizers: 1. Merge the `NormalizerInterface` and `DenormalizerInterface` to a single `ValueTransformerInterface`: ```php interface ValueTransformerInterface { public function transform(mixed $value, array $options = []): mixed; public static function getJsonValueType(): Type; } ``` 2. Replace the `Normalizer` and `Denormalizer` attributes by a single `ValueTransformer` attribute: ```php #[\Attribute(\Attribute::TARGET_PROPERTY)] class ValueTransformer { // ... public function __construct( callable|string|null $toNativeValue = null, // can be a callable or a ValueTransformerInterface service id callable|string|null $toJsonValue = null, // can be a callable or a ValueTransformerInterface service id ) { // ... } // ... ``` /cc `@wouterj` Commits ------- ced7191 [JsonEncoder] Replace normalizers by value transformers
2 parents dfd8e53 + ced7191 commit 68944f5

File tree

118 files changed

+837
-885
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

+837
-885
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,8 +53,7 @@ class UnusedTagsPass implements CompilerPassInterface
5353
'form.type_guesser',
5454
'html_sanitizer',
5555
'http_client.client',
56-
'json_encoder.denormalizer',
57-
'json_encoder.normalizer',
56+
'json_encoder.value_transformer',
5857
'kernel.cache_clearer',
5958
'kernel.cache_warmer',
6059
'kernel.event_listener',

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,10 @@
101101
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
102102
use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator;
103103
use Symfony\Component\JsonEncoder\Attribute\JsonEncodable;
104-
use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface;
105104
use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface;
106-
use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface;
107105
use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface;
108106
use Symfony\Component\JsonEncoder\JsonEncoder;
107+
use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface;
109108
use Symfony\Component\Lock\LockFactory;
110109
use Symfony\Component\Lock\LockInterface;
111110
use Symfony\Component\Lock\PersistingStoreInterface;
@@ -2040,10 +2039,8 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde
20402039
throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".');
20412040
}
20422041

2043-
$container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class)
2044-
->addTag('json_encoder.normalizer');
2045-
$container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class)
2046-
->addTag('json_encoder.denormalizer');
2042+
$container->registerForAutoconfiguration(ValueTransformerInterface::class)
2043+
->addTag('json_encoder.value_transformer');
20472044

20482045
$loader->load('json_encoder.php');
20492046

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,11 +11,11 @@
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;
1717
use Symfony\Component\JsonEncoder\Attribute\JsonEncodable;
18-
use Symfony\Component\JsonEncoder\Attribute\Normalizer;
18+
use Symfony\Component\JsonEncoder\Attribute\ValueTransformer;
1919

2020
/**
2121
* @author Mathias Arlaud <mathias.arlaud@gmail.com>
@@ -24,11 +24,9 @@
2424
class Dummy
2525
{
2626
#[EncodedName('@name')]
27-
#[Normalizer('strtoupper')]
28-
#[Denormalizer('strtolower')]
27+
#[ValueTransformer(toJsonValue: 'strtoupper', toNativeValue: 'strtolower')]
2928
public string $name = 'dummy';
3029

31-
#[Normalizer(RangeNormalizer::class)]
32-
#[Denormalizer(RangeNormalizer::class)]
30+
#[ValueTransformer(toJsonValue: RangeToStringValueTransformer::class, toNativeValue: StringToRangeValueTransformer::class)]
3331
public array $range = [10, 20];
3432
}
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
@@ -23,4 +23,5 @@ services:
2323
public: true
2424

2525
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~
26-
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~
26+
Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer: ~
27+
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)|string|null $toNativeValue
32+
* @param (callable(mixed, array<string, 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+
}

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