From ced7191fc085501bc881d999e7f44902115d6408 Mon Sep 17 00:00:00 2001 From: Mathias Arlaud Date: Mon, 23 Dec 2024 15:26:31 +0100 Subject: [PATCH] [JsonEncoder] Replace normalizers by value transformers --- .../Compiler/UnusedTagsPass.php | 3 +- .../FrameworkExtension.php | 9 +-- .../Resources/config/json_encoder.php | 31 +++----- .../Functional/app/JsonEncoder/Dto/Dummy.php | 12 ++- .../RangeToStringValueTransformer.php | 32 ++++++++ ....php => StringToRangeValueTransformer.php} | 16 ++-- .../Functional/app/JsonEncoder/config.yml | 3 +- .../JsonEncoder/Attribute/Denormalizer.php | 43 ----------- .../JsonEncoder/Attribute/Normalizer.php | 43 ----------- .../Attribute/ValueTransformer.php | 63 +++++++++++++++ .../JsonEncoder/Decode/DecoderGenerator.php | 14 ++-- .../Denormalizer/DenormalizerInterface.php | 31 -------- .../JsonEncoder/Decode/PhpAstBuilder.php | 14 ++-- .../JsonEncoder/Encode/EncoderGenerator.php | 14 ++-- .../JsonEncoder/Encode/PhpAstBuilder.php | 2 +- .../Component/JsonEncoder/JsonDecoder.php | 31 ++++---- .../Component/JsonEncoder/JsonEncoder.php | 30 ++++---- .../AttributePropertyMetadataLoader.php | 49 ++++++------ .../DateTimeTypePropertyMetadataLoader.php | 17 +++-- .../AttributePropertyMetadataLoader.php | 45 +++++------ .../DateTimeTypePropertyMetadataLoader.php | 8 +- .../JsonEncoder/Mapping/PropertyMetadata.php | 52 ++++++------- .../Tests/Attribute/ValueTransformerTest.php | 27 +++++++ .../Tests/Decode/DecoderGeneratorTest.php | 12 +-- .../Denormalizer/DateTimeDenormalizerTest.php | 76 ------------------- .../Tests/Decode/LazyInstantiatorTest.php | 4 +- .../Tests/Encode/EncoderGeneratorTest.php | 12 +-- .../Normalizer/DateTimeNormalizerTest.php | 42 ---------- .../Attribute/BooleanStringDenormalizer.php | 15 ---- .../Attribute/BooleanStringNormalizer.php | 15 ---- .../BooleanStringValueTransformer.php | 16 ++++ .../BooleanStringDenormalizer.php | 19 ----- .../DivideStringAndCastToIntDenormalizer.php | 19 ----- .../Fixtures/Model/DummyWithDateTimes.php | 1 - .../Model/DummyWithNormalizerAttributes.php | 45 ----------- .../DummyWithValueTransformerAttributes.php | 45 +++++++++++ .../Normalizer/BooleanStringNormalizer.php | 19 ----- .../DoubleIntAndCastToStringNormalizer.php | 19 ----- .../BooleanToStringValueTransformer.php | 19 +++++ ...videStringAndCastToIntValueTransformer.php | 19 +++++ ...ubleIntAndCastToStringValueTransformer.php | 19 +++++ .../StringToBooleanValueTransformer.php | 19 +++++ .../Tests/Fixtures/decoder/backed_enum.php | 2 +- .../Fixtures/decoder/backed_enum.stream.php | 2 +- .../Tests/Fixtures/decoder/dict.php | 2 +- .../Tests/Fixtures/decoder/dict.stream.php | 6 +- .../Tests/Fixtures/decoder/iterable.php | 2 +- .../Fixtures/decoder/iterable.stream.php | 6 +- .../Tests/Fixtures/decoder/list.php | 2 +- .../Tests/Fixtures/decoder/list.stream.php | 6 +- .../Tests/Fixtures/decoder/mixed.php | 2 +- .../Tests/Fixtures/decoder/mixed.stream.php | 2 +- .../Tests/Fixtures/decoder/null.php | 2 +- .../Tests/Fixtures/decoder/null.stream.php | 2 +- .../Fixtures/decoder/nullable_backed_enum.php | 4 +- .../decoder/nullable_backed_enum.stream.php | 4 +- .../Fixtures/decoder/nullable_object.php | 6 +- .../decoder/nullable_object.stream.php | 8 +- .../Fixtures/decoder/nullable_object_dict.php | 10 +-- .../decoder/nullable_object_dict.stream.php | 12 +-- .../Fixtures/decoder/nullable_object_list.php | 10 +-- .../decoder/nullable_object_list.stream.php | 12 +-- .../Tests/Fixtures/decoder/object.php | 4 +- .../Tests/Fixtures/decoder/object.stream.php | 6 +- .../Tests/Fixtures/decoder/object_dict.php | 8 +- .../Fixtures/decoder/object_dict.stream.php | 10 +-- .../Fixtures/decoder/object_in_object.php | 8 +- .../decoder/object_in_object.stream.php | 14 ++-- .../Fixtures/decoder/object_iterable.php | 8 +- .../decoder/object_iterable.stream.php | 10 +-- .../Tests/Fixtures/decoder/object_list.php | 8 +- .../Fixtures/decoder/object_list.stream.php | 10 +-- .../decoder/object_with_denormalizer.php | 10 --- .../object_with_denormalizer.stream.php | 19 ----- .../object_with_nullable_properties.php | 6 +- ...object_with_nullable_properties.stream.php | 8 +- .../Fixtures/decoder/object_with_union.php | 6 +- .../decoder/object_with_union.stream.php | 8 +- .../decoder/object_with_value_transformer.php | 10 +++ .../object_with_value_transformer.stream.php | 19 +++++ .../Tests/Fixtures/decoder/scalar.php | 2 +- .../Tests/Fixtures/decoder/scalar.stream.php | 2 +- .../Tests/Fixtures/decoder/union.php | 10 +-- .../Tests/Fixtures/decoder/union.stream.php | 12 +-- .../Tests/Fixtures/encoder/backed_enum.php | 2 +- .../Tests/Fixtures/encoder/bool.php | 2 +- .../Tests/Fixtures/encoder/bool_list.php | 2 +- .../Tests/Fixtures/encoder/dict.php | 2 +- .../Tests/Fixtures/encoder/iterable.php | 2 +- .../Tests/Fixtures/encoder/list.php | 2 +- .../Tests/Fixtures/encoder/mixed.php | 2 +- .../Tests/Fixtures/encoder/null.php | 2 +- .../Tests/Fixtures/encoder/null_list.php | 2 +- .../Fixtures/encoder/nullable_backed_enum.php | 2 +- .../Fixtures/encoder/nullable_object.php | 2 +- .../Fixtures/encoder/nullable_object_dict.php | 2 +- .../Fixtures/encoder/nullable_object_list.php | 2 +- .../Tests/Fixtures/encoder/object.php | 2 +- .../Tests/Fixtures/encoder/object_dict.php | 2 +- .../Fixtures/encoder/object_in_object.php | 2 +- .../Fixtures/encoder/object_iterable.php | 2 +- .../Tests/Fixtures/encoder/object_list.php | 2 +- .../encoder/object_with_normalizer.php | 13 ---- .../Fixtures/encoder/object_with_union.php | 2 +- .../encoder/object_with_value_transformer.php | 13 ++++ .../Tests/Fixtures/encoder/scalar.php | 2 +- .../Tests/Fixtures/encoder/union.php | 2 +- .../JsonEncoder/Tests/JsonDecoderTest.php | 21 +++-- .../JsonEncoder/Tests/JsonEncoderTest.php | 42 +++++----- .../AttributePropertyMetadataLoaderTest.php | 38 +++++----- ...DateTimeTypePropertyMetadataLoaderTest.php | 21 +++-- .../AttributePropertyMetadataLoaderTest.php | 38 +++++----- ...DateTimeTypePropertyMetadataLoaderTest.php | 4 +- .../DateTimeToStringValueTransformerTest.php | 42 ++++++++++ .../StringToDateTimeValueTransformerTest.php | 61 +++++++++++++++ .../DateTimeToStringValueTransformer.php} | 18 ++--- .../StringToDateTimeValueTransformer.php} | 34 ++++----- .../ValueTransformerInterface.php} | 11 +-- 118 files changed, 837 insertions(+), 885 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php rename src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/{RangeNormalizer.php => StringToRangeValueTransformer.php} (51%) delete mode 100644 src/Symfony/Component/JsonEncoder/Attribute/Denormalizer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Attribute/Normalizer.php create mode 100644 src/Symfony/Component/JsonEncoder/Attribute/ValueTransformer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DenormalizerInterface.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Attribute/ValueTransformerTest.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Decode/Denormalizer/DateTimeDenormalizerTest.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Encode/Normalizer/DateTimeNormalizerTest.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Attribute/BooleanStringDenormalizer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Attribute/BooleanStringNormalizer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Attribute/BooleanStringValueTransformer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Denormalizer/BooleanStringDenormalizer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Denormalizer/DivideStringAndCastToIntDenormalizer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Model/DummyWithNormalizerAttributes.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Model/DummyWithValueTransformerAttributes.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Normalizer/BooleanStringNormalizer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/Normalizer/DoubleIntAndCastToStringNormalizer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/ValueTransformer/BooleanToStringValueTransformer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/ValueTransformer/DivideStringAndCastToIntValueTransformer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/ValueTransformer/DoubleIntAndCastToStringValueTransformer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/ValueTransformer/StringToBooleanValueTransformer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.stream.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.stream.php delete mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_normalizer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_value_transformer.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/DateTimeToStringValueTransformerTest.php create mode 100644 src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/StringToDateTimeValueTransformerTest.php rename src/Symfony/Component/JsonEncoder/{Encode/Normalizer/DateTimeNormalizer.php => ValueTransformer/DateTimeToStringValueTransformer.php} (53%) rename src/Symfony/Component/JsonEncoder/{Decode/Denormalizer/DateTimeDenormalizer.php => ValueTransformer/StringToDateTimeValueTransformer.php} (51%) rename src/Symfony/Component/JsonEncoder/{Encode/Normalizer/NormalizerInterface.php => ValueTransformer/ValueTransformerInterface.php} (55%) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index 5714f8fc2e6a5..c5191a6599dc4 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -53,8 +53,7 @@ class UnusedTagsPass implements CompilerPassInterface 'form.type_guesser', 'html_sanitizer', 'http_client.client', - 'json_encoder.denormalizer', - 'json_encoder.normalizer', + 'json_encoder.value_transformer', 'kernel.cache_clearer', 'kernel.cache_warmer', 'kernel.event_listener', diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 88fe69ec32756..ee302a20ce329 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -101,11 +101,10 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\Log\DebugLoggerConfigurator; use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface as JsonEncoderDenormalizerInterface; use Symfony\Component\JsonEncoder\DecoderInterface as JsonEncoderDecoderInterface; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface as JsonEncoderNormalizerInterface; use Symfony\Component\JsonEncoder\EncoderInterface as JsonEncoderEncoderInterface; use Symfony\Component\JsonEncoder\JsonEncoder; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\Lock\LockFactory; use Symfony\Component\Lock\LockInterface; use Symfony\Component\Lock\PersistingStoreInterface; @@ -2027,10 +2026,8 @@ private function registerJsonEncoderConfiguration(array $config, ContainerBuilde throw new LogicException('JsonEncoder support cannot be enabled as the JsonEncoder component is not installed. Try running "composer require symfony/json-encoder".'); } - $container->registerForAutoconfiguration(JsonEncoderNormalizerInterface::class) - ->addTag('json_encoder.normalizer'); - $container->registerForAutoconfiguration(JsonEncoderDenormalizerInterface::class) - ->addTag('json_encoder.denormalizer'); + $container->registerForAutoconfiguration(ValueTransformerInterface::class) + ->addTag('json_encoder.value_transformer'); $loader->load('json_encoder.php'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php index 67cb25d0aa13a..f6fbd05e6f038 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/json_encoder.php @@ -13,8 +13,6 @@ use Symfony\Component\JsonEncoder\CacheWarmer\EncoderDecoderCacheWarmer; use Symfony\Component\JsonEncoder\CacheWarmer\LazyGhostCacheWarmer; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer; -use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; use Symfony\Component\JsonEncoder\JsonDecoder; use Symfony\Component\JsonEncoder\JsonEncoder; use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader as DecodeAttributePropertyMetadataLoader; @@ -23,19 +21,21 @@ use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader as EncodeDateTimeTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; +use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; +use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer; return static function (ContainerConfigurator $container) { $container->services() // encoder/decoder ->set('json_encoder.encoder', JsonEncoder::class) ->args([ - tagged_locator('json_encoder.normalizer'), + tagged_locator('json_encoder.value_transformer'), service('json_encoder.encode.property_metadata_loader'), param('.json_encoder.encoders_dir'), ]) ->set('json_encoder.decoder', JsonDecoder::class) ->args([ - tagged_locator('json_encoder.denormalizer'), + tagged_locator('json_encoder.value_transformer'), service('json_encoder.decode.property_metadata_loader'), param('.json_encoder.decoders_dir'), param('.json_encoder.lazy_ghosts_dir'), @@ -63,7 +63,7 @@ ->decorate('json_encoder.encode.property_metadata_loader') ->args([ service('.inner'), - tagged_locator('json_encoder.normalizer'), + tagged_locator('json_encoder.value_transformer'), service('type_info.resolver'), ]) @@ -86,23 +86,16 @@ ->decorate('json_encoder.decode.property_metadata_loader') ->args([ service('.inner'), - tagged_locator('json_encoder.normalizer'), + tagged_locator('json_encoder.value_transformer'), service('type_info.resolver'), ]) - // normalizers/denormalizers - ->set('json_encoder.normalizer.date_time', DateTimeNormalizer::class) - ->tag('json_encoder.normalizer') - ->set('json_encoder.denormalizer.date_time', DateTimeDenormalizer::class) - ->args([ - false, - ]) - ->tag('json_encoder.denormalizer') - ->set('json_encoder.denormalizer.date_time_immutable', DateTimeDenormalizer::class) - ->args([ - true, - ]) - ->tag('json_encoder.denormalizer') + // value transformers + ->set('json_encoder.value_transformer.date_time_to_string', DateTimeToStringValueTransformer::class) + ->tag('json_encoder.value_transformer') + + ->set('json_encoder.value_transformer.string_to_date_time', StringToDateTimeValueTransformer::class) + ->tag('json_encoder.value_transformer') // cache ->set('.json_encoder.cache_warmer.encoder_decoder', EncoderDecoderCacheWarmer::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php index 8610de049fa28..a6b4d8e91f9b0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/Dto/Dummy.php @@ -11,11 +11,11 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto; -use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer; -use Symfony\Component\JsonEncoder\Attribute\Denormalizer; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer; +use Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer; use Symfony\Component\JsonEncoder\Attribute\EncodedName; use Symfony\Component\JsonEncoder\Attribute\JsonEncodable; -use Symfony\Component\JsonEncoder\Attribute\Normalizer; +use Symfony\Component\JsonEncoder\Attribute\ValueTransformer; /** * @author Mathias Arlaud @@ -24,11 +24,9 @@ class Dummy { #[EncodedName('@name')] - #[Normalizer('strtoupper')] - #[Denormalizer('strtolower')] + #[ValueTransformer(toJsonValue: 'strtoupper', toNativeValue: 'strtolower')] public string $name = 'dummy'; - #[Normalizer(RangeNormalizer::class)] - #[Denormalizer(RangeNormalizer::class)] + #[ValueTransformer(toJsonValue: RangeToStringValueTransformer::class, toNativeValue: StringToRangeValueTransformer::class)] public array $range = [10, 20]; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php new file mode 100644 index 0000000000000..93be1fad94a2d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeToStringValueTransformer.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; + +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; +use Symfony\Component\TypeInfo\Type; +use Symfony\Component\TypeInfo\Type\BuiltinType; + +/** + * @author Mathias Arlaud + */ +class RangeToStringValueTransformer implements ValueTransformerInterface +{ + public function transform(mixed $value, array $options = []): string + { + return $value[0].'..'.$value[1]; + } + + public static function getJsonValueType(): BuiltinType + { + return Type::string(); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeNormalizer.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php similarity index 51% rename from src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeNormalizer.php rename to src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php index beb9e81888ce4..46e3dd30590bb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/RangeNormalizer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/StringToRangeValueTransformer.php @@ -11,27 +11,21 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\Type\BuiltinType; /** * @author Mathias Arlaud */ -class RangeNormalizer implements NormalizerInterface, DenormalizerInterface +class StringToRangeValueTransformer implements ValueTransformerInterface { - public function normalize(mixed $denormalized, array $options = []): string + public function transform(mixed $value, array $options = []): array { - return $denormalized[0].'..'.$denormalized[1]; + return array_map(static fn (string $v): int => (int) $v, explode('..', $value)); } - public function denormalize(mixed $normalized, array $options = []): array - { - return array_map(static fn (string $v): int => (int) $v, explode('..', $normalized)); - } - - public static function getNormalizedType(): BuiltinType + public static function getJsonValueType(): BuiltinType { return Type::string(); } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml index 13b68adef54c5..5eb5d49961110 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/JsonEncoder/config.yml @@ -23,4 +23,5 @@ services: public: true Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\Dto\Dummy: ~ - Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeNormalizer: ~ + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\StringToRangeValueTransformer: ~ + Symfony\Bundle\FrameworkBundle\Tests\Functional\app\JsonEncoder\RangeToStringValueTransformer: ~ diff --git a/src/Symfony/Component/JsonEncoder/Attribute/Denormalizer.php b/src/Symfony/Component/JsonEncoder/Attribute/Denormalizer.php deleted file mode 100644 index c48da727265d7..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Attribute/Denormalizer.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonEncoder\Attribute; - -/** - * Defines a callable or a {@see \Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface} service id - * that will be used to denormalize the property data during decoding. - * - * @author Mathias Arlaud - * - * @experimental - */ -#[\Attribute(\Attribute::TARGET_PROPERTY)] -class Denormalizer -{ - private string|\Closure $denormalizer; - - /** - * @param string|(callable(mixed, array?): mixed)|(callable(mixed): mixed) $denormalizer - */ - public function __construct(mixed $denormalizer) - { - if (\is_callable($denormalizer)) { - $denormalizer = \Closure::fromCallable($denormalizer); - } - - $this->denormalizer = $denormalizer; - } - - public function getDenormalizer(): string|\Closure - { - return $this->denormalizer; - } -} diff --git a/src/Symfony/Component/JsonEncoder/Attribute/Normalizer.php b/src/Symfony/Component/JsonEncoder/Attribute/Normalizer.php deleted file mode 100644 index e8c1ea314dcdf..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Attribute/Normalizer.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonEncoder\Attribute; - -/** - * Defines a callable or a {@see \Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface} service id - * that will be used to normalize the property data during encoding. - * - * @author Mathias Arlaud - * - * @experimental - */ -#[\Attribute(\Attribute::TARGET_PROPERTY)] -class Normalizer -{ - private string|\Closure $normalizer; - - /** - * @param string|(callable(mixed, array?): mixed)|(callable(mixed): mixed) $normalizer - */ - public function __construct(mixed $normalizer) - { - if (\is_callable($normalizer)) { - $normalizer = \Closure::fromCallable($normalizer); - } - - $this->normalizer = $normalizer; - } - - public function getNormalizer(): string|\Closure - { - return $this->normalizer; - } -} diff --git a/src/Symfony/Component/JsonEncoder/Attribute/ValueTransformer.php b/src/Symfony/Component/JsonEncoder/Attribute/ValueTransformer.php new file mode 100644 index 0000000000000..3c64817aea759 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Attribute/ValueTransformer.php @@ -0,0 +1,63 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonEncoder\Attribute; + +use Symfony\Component\JsonEncoder\Exception\LogicException; + +/** + * Defines a callable or a {@see \Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface} service id + * that will be used to transform the property data during encoding and decoding. + * + * @author Mathias Arlaud + * + * @experimental + */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] +class ValueTransformer +{ + private \Closure|string|null $toNativeValue; + private \Closure|string|null $toJsonValue; + + /** + * @param (callable(mixed, array=): mixed)|string|null $toNativeValue + * @param (callable(mixed, array=): mixed)|string|null $toJsonValue + */ + public function __construct( + callable|string|null $toNativeValue = null, + callable|string|null $toJsonValue = null, + ) { + if (!$toNativeValue && !$toJsonValue) { + throw new LogicException('#[ValueTransformer] attribute must declare either $toNativeValue or $toJsonValue.'); + } + + if (\is_callable($toNativeValue)) { + $toNativeValue = $toNativeValue(...); + } + + if (\is_callable($toJsonValue)) { + $toJsonValue = $toJsonValue(...); + } + + $this->toNativeValue = $toNativeValue; + $this->toJsonValue = $toJsonValue; + } + + public function getToNativeValueTransformer(): string|\Closure|null + { + return $this->toNativeValue; + } + + public function getToJsonValueTransformer(): string|\Closure|null + { + return $this->toJsonValue; + } +} diff --git a/src/Symfony/Component/JsonEncoder/Decode/DecoderGenerator.php b/src/Symfony/Component/JsonEncoder/Decode/DecoderGenerator.php index 78bafadb629dd..3d58ceaf2a1ea 100644 --- a/src/Symfony/Component/JsonEncoder/Decode/DecoderGenerator.php +++ b/src/Symfony/Component/JsonEncoder/Decode/DecoderGenerator.php @@ -136,23 +136,23 @@ public function createDataModel(Type $type, array $options = [], array $context 'name' => $propertyMetadata->getName(), 'value' => $this->createDataModel($propertyMetadata->getType(), $options, $context), 'accessor' => function (DataAccessorInterface $accessor) use ($propertyMetadata): DataAccessorInterface { - foreach ($propertyMetadata->getDenormalizers() as $denormalizer) { - if (\is_string($denormalizer)) { - $denormalizerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($denormalizer)], new VariableDataAccessor('denormalizers')); - $accessor = new FunctionDataAccessor('denormalize', [$accessor, new VariableDataAccessor('options')], $denormalizerServiceAccessor); + foreach ($propertyMetadata->getToNativeValueTransformers() as $valueTransformer) { + if (\is_string($valueTransformer)) { + $valueTransformerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($valueTransformer)], new VariableDataAccessor('valueTransformers')); + $accessor = new FunctionDataAccessor('transform', [$accessor, new VariableDataAccessor('options')], $valueTransformerServiceAccessor); continue; } try { - $functionReflection = new \ReflectionFunction($denormalizer); + $functionReflection = new \ReflectionFunction($valueTransformer); } catch (\ReflectionException $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } - $functionName = !$functionReflection->getClosureScopeClass() + $functionName = !$functionReflection->getClosureCalledClass() ? $functionReflection->getName() - : \sprintf('%s::%s', $functionReflection->getClosureScopeClass()->getName(), $functionReflection->getName()); + : \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName()); $arguments = $functionReflection->isUserDefined() ? [$accessor, new VariableDataAccessor('options')] : [$accessor]; $accessor = new FunctionDataAccessor($functionName, $arguments); diff --git a/src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DenormalizerInterface.php b/src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DenormalizerInterface.php deleted file mode 100644 index 2291b0879413f..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DenormalizerInterface.php +++ /dev/null @@ -1,31 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonEncoder\Decode\Denormalizer; - -use Symfony\Component\TypeInfo\Type; - -/** - * Denormalizes data during the decoding process. - * - * @author Mathias Arlaud - * - * @experimental - */ -interface DenormalizerInterface -{ - /** - * @param array $options - */ - public function denormalize(mixed $normalized, array $options = []): mixed; - - public static function getNormalizedType(): Type; -} diff --git a/src/Symfony/Component/JsonEncoder/Decode/PhpAstBuilder.php b/src/Symfony/Component/JsonEncoder/Decode/PhpAstBuilder.php index 1a445a9554c5c..fcc738efa4bcf 100644 --- a/src/Symfony/Component/JsonEncoder/Decode/PhpAstBuilder.php +++ b/src/Symfony/Component/JsonEncoder/Decode/PhpAstBuilder.php @@ -85,7 +85,7 @@ public function build(DataModelNodeInterface $dataModel, bool $decodeFromStream, 'static' => true, 'params' => [ new Param($this->builder->var('stream'), type: new Identifier('mixed')), - new Param($this->builder->var('denormalizers'), type: new FullyQualified(ContainerInterface::class)), + new Param($this->builder->var('valueTransformers'), type: new FullyQualified(ContainerInterface::class)), new Param($this->builder->var('instantiator'), type: new FullyQualified(LazyInstantiator::class)), new Param($this->builder->var('options'), type: new Identifier('array')), ], @@ -113,7 +113,7 @@ public function build(DataModelNodeInterface $dataModel, bool $decodeFromStream, 'static' => true, 'params' => [ new Param($this->builder->var('string'), type: new Identifier('string|\\Stringable')), - new Param($this->builder->var('denormalizers'), type: new FullyQualified(ContainerInterface::class)), + new Param($this->builder->var('valueTransformers'), type: new FullyQualified(ContainerInterface::class)), new Param($this->builder->var('instantiator'), type: new FullyQualified(Instantiator::class)), new Param($this->builder->var('options'), type: new Identifier('array')), ], @@ -290,7 +290,7 @@ private function buildCompositeNodeStatements(CompositeNode $node, bool $decodeF 'params' => $params, 'uses' => [ new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('denormalizers')), + new ClosureUse($this->builder->var('valueTransformers')), new ClosureUse($this->builder->var('instantiator')), new ClosureUse($this->builder->var('providers'), byRef: true), ], @@ -352,7 +352,7 @@ private function buildCollectionNodeStatements(CollectionNode $node, bool $decod 'params' => $iterableClosureParams, 'uses' => [ new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('denormalizers')), + new ClosureUse($this->builder->var('valueTransformers')), new ClosureUse($this->builder->var('instantiator')), new ClosureUse($this->builder->var('providers'), byRef: true), ], @@ -390,7 +390,7 @@ private function buildCollectionNodeStatements(CollectionNode $node, bool $decod 'params' => $params, 'uses' => [ new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('denormalizers')), + new ClosureUse($this->builder->var('valueTransformers')), new ClosureUse($this->builder->var('instantiator')), new ClosureUse($this->builder->var('providers'), byRef: true), ], @@ -490,7 +490,7 @@ private function buildObjectNodeStatements(ObjectNode $node, bool $decodeFromStr new ClosureUse($this->builder->var('stream')), new ClosureUse($this->builder->var('data')), new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('denormalizers')), + new ClosureUse($this->builder->var('valueTransformers')), new ClosureUse($this->builder->var('instantiator')), new ClosureUse($this->builder->var('providers'), byRef: true), ], @@ -530,7 +530,7 @@ private function buildObjectNodeStatements(ObjectNode $node, bool $decodeFromStr 'params' => $params, 'uses' => [ new ClosureUse($this->builder->var('options')), - new ClosureUse($this->builder->var('denormalizers')), + new ClosureUse($this->builder->var('valueTransformers')), new ClosureUse($this->builder->var('instantiator')), new ClosureUse($this->builder->var('providers'), byRef: true), ], diff --git a/src/Symfony/Component/JsonEncoder/Encode/EncoderGenerator.php b/src/Symfony/Component/JsonEncoder/Encode/EncoderGenerator.php index cc0fbf93580aa..51a1eb0d79773 100644 --- a/src/Symfony/Component/JsonEncoder/Encode/EncoderGenerator.php +++ b/src/Symfony/Component/JsonEncoder/Encode/EncoderGenerator.php @@ -151,23 +151,23 @@ private function createDataModel(Type $type, DataAccessorInterface $accessor, ar foreach ($propertiesMetadata as $encodedName => $propertyMetadata) { $propertyAccessor = new PropertyDataAccessor($accessor, $propertyMetadata->getName()); - foreach ($propertyMetadata->getNormalizers() as $normalizer) { - if (\is_string($normalizer)) { - $normalizerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($normalizer)], new VariableDataAccessor('normalizers')); - $propertyAccessor = new FunctionDataAccessor('normalize', [$propertyAccessor, new VariableDataAccessor('options')], $normalizerServiceAccessor); + foreach ($propertyMetadata->getToJsonValueTransformer() as $valueTransformer) { + if (\is_string($valueTransformer)) { + $valueTransformerServiceAccessor = new FunctionDataAccessor('get', [new ScalarDataAccessor($valueTransformer)], new VariableDataAccessor('valueTransformers')); + $propertyAccessor = new FunctionDataAccessor('transform', [$propertyAccessor, new VariableDataAccessor('options')], $valueTransformerServiceAccessor); continue; } try { - $functionReflection = new \ReflectionFunction($normalizer); + $functionReflection = new \ReflectionFunction($valueTransformer); } catch (\ReflectionException $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } - $functionName = !$functionReflection->getClosureScopeClass() + $functionName = !$functionReflection->getClosureCalledClass() ? $functionReflection->getName() - : \sprintf('%s::%s', $functionReflection->getClosureScopeClass()->getName(), $functionReflection->getName()); + : \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName()); $arguments = $functionReflection->isUserDefined() ? [$propertyAccessor, new VariableDataAccessor('options')] : [$propertyAccessor]; $propertyAccessor = new FunctionDataAccessor($functionName, $arguments); diff --git a/src/Symfony/Component/JsonEncoder/Encode/PhpAstBuilder.php b/src/Symfony/Component/JsonEncoder/Encode/PhpAstBuilder.php index 443961307e1f2..36d054649d596 100644 --- a/src/Symfony/Component/JsonEncoder/Encode/PhpAstBuilder.php +++ b/src/Symfony/Component/JsonEncoder/Encode/PhpAstBuilder.php @@ -78,7 +78,7 @@ public function build(DataModelNodeInterface $dataModel, array $options = [], ar 'static' => true, 'params' => [ new Param($this->builder->var('data'), type: new Identifier('mixed')), - new Param($this->builder->var('normalizers'), type: new FullyQualified(ContainerInterface::class)), + new Param($this->builder->var('valueTransformers'), type: new FullyQualified(ContainerInterface::class)), new Param($this->builder->var('options'), type: new Identifier('array')), ], 'returnType' => new FullyQualified(\Traversable::class), diff --git a/src/Symfony/Component/JsonEncoder/JsonDecoder.php b/src/Symfony/Component/JsonEncoder/JsonDecoder.php index 6e317fb9f1f7b..304b01f28cde9 100644 --- a/src/Symfony/Component/JsonEncoder/JsonDecoder.php +++ b/src/Symfony/Component/JsonEncoder/JsonDecoder.php @@ -14,8 +14,6 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use Psr\Container\ContainerInterface; use Symfony\Component\JsonEncoder\Decode\DecoderGenerator; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface; use Symfony\Component\JsonEncoder\Decode\Instantiator; use Symfony\Component\JsonEncoder\Decode\LazyInstantiator; use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader; @@ -23,6 +21,8 @@ use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory; use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver; @@ -42,7 +42,7 @@ final class JsonDecoder implements DecoderInterface private LazyInstantiator $lazyInstantiator; public function __construct( - private ContainerInterface $denormalizers, + private ContainerInterface $valueTransformers, PropertyMetadataLoaderInterface $propertyMetadataLoader, string $decodersDir, string $lazyGhostsDir, @@ -57,35 +57,34 @@ public function decode($input, Type $type, array $options = []): mixed $isStream = \is_resource($input); $path = $this->decoderGenerator->generate($type, $isStream, $options); - return (require $path)($input, $this->denormalizers, $isStream ? $this->lazyInstantiator : $this->instantiator, $options); + return (require $path)($input, $this->valueTransformers, $isStream ? $this->lazyInstantiator : $this->instantiator, $options); } /** - * @param array $denormalizers + * @param array $valueTransformers */ - public static function create(array $denormalizers = [], ?string $decodersDir = null, ?string $lazyGhostsDir = null): self + public static function create(array $valueTransformers = [], ?string $decodersDir = null, ?string $lazyGhostsDir = null): self { $decodersDir ??= sys_get_temp_dir().'/json_encoder/decoder'; $lazyGhostsDir ??= sys_get_temp_dir().'/json_encoder/lazy_ghost'; - $denormalizers += [ - 'json_encoder.denormalizer.date_time' => new DateTimeDenormalizer(immutable: false), - 'json_encoder.denormalizer.date_time_immutable' => new DateTimeDenormalizer(immutable: true), + $valueTransformers += [ + 'json_encoder.value_transformer.string_to_date_time' => new StringToDateTimeValueTransformer(), ]; - $denormalizersContainer = new class($denormalizers) implements ContainerInterface { + $valueTransformersContainer = new class($valueTransformers) implements ContainerInterface { public function __construct( - private array $denormalizers, + private array $valueTransformers, ) { } public function has(string $id): bool { - return isset($this->denormalizers[$id]); + return isset($this->valueTransformers[$id]); } - public function get(string $id): DenormalizerInterface + public function get(string $id): ValueTransformerInterface { - return $this->denormalizers[$id]; + return $this->valueTransformers[$id]; } }; @@ -95,13 +94,13 @@ public function get(string $id): DenormalizerInterface new DateTimeTypePropertyMetadataLoader( new AttributePropertyMetadataLoader( new PropertyMetadataLoader(TypeResolver::create()), - $denormalizersContainer, + $valueTransformersContainer, TypeResolver::create(), ), ), $typeContextFactory, ); - return new self($denormalizersContainer, $propertyMetadataLoader, $decodersDir, $lazyGhostsDir); + return new self($valueTransformersContainer, $propertyMetadataLoader, $decodersDir, $lazyGhostsDir); } } diff --git a/src/Symfony/Component/JsonEncoder/JsonEncoder.php b/src/Symfony/Component/JsonEncoder/JsonEncoder.php index f518bb08d49fd..cbeb6a7406b83 100644 --- a/src/Symfony/Component/JsonEncoder/JsonEncoder.php +++ b/src/Symfony/Component/JsonEncoder/JsonEncoder.php @@ -14,13 +14,13 @@ use PHPStan\PhpDocParser\Parser\PhpDocParser; use Psr\Container\ContainerInterface; use Symfony\Component\JsonEncoder\Encode\EncoderGenerator; -use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; use Symfony\Component\JsonEncoder\Mapping\Encode\AttributePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\Encode\DateTimeTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory; use Symfony\Component\TypeInfo\TypeResolver\StringTypeResolver; @@ -38,7 +38,7 @@ final class JsonEncoder implements EncoderInterface private EncoderGenerator $encoderGenerator; public function __construct( - private ContainerInterface $normalizers, + private ContainerInterface $valueTransformers, PropertyMetadataLoaderInterface $propertyMetadataLoader, string $encodersDir, ) { @@ -49,33 +49,33 @@ public function encode(mixed $data, Type $type, array $options = []): \Traversab { $path = $this->encoderGenerator->generate($type, $options); - return new Encoded((require $path)($data, $this->normalizers, $options)); + return new Encoded((require $path)($data, $this->valueTransformers, $options)); } /** - * @param array $normalizers + * @param array $valueTransformers */ - public static function create(array $normalizers = [], ?string $encodersDir = null): self + public static function create(array $valueTransformers = [], ?string $encodersDir = null): self { $encodersDir ??= sys_get_temp_dir().'/json_encoder/encoder'; - $normalizers += [ - 'json_encoder.normalizer.date_time' => new DateTimeNormalizer(), + $valueTransformers += [ + 'json_encoder.value_transformer.date_time_to_string' => new DateTimeToStringValueTransformer(), ]; - $normalizersContainer = new class($normalizers) implements ContainerInterface { + $valueTransformersContainer = new class($valueTransformers) implements ContainerInterface { public function __construct( - private array $normalizers, + private array $valueTransformers, ) { } public function has(string $id): bool { - return isset($this->normalizers[$id]); + return isset($this->valueTransformers[$id]); } - public function get(string $id): NormalizerInterface + public function get(string $id): ValueTransformerInterface { - return $this->normalizers[$id]; + return $this->valueTransformers[$id]; } }; @@ -85,13 +85,13 @@ public function get(string $id): NormalizerInterface new DateTimeTypePropertyMetadataLoader( new AttributePropertyMetadataLoader( new PropertyMetadataLoader(TypeResolver::create()), - $normalizersContainer, + $valueTransformersContainer, TypeResolver::create(), ), ), $typeContextFactory, ); - return new self($normalizersContainer, $propertyMetadataLoader, $encodersDir); + return new self($valueTransformersContainer, $propertyMetadataLoader, $encodersDir); } } diff --git a/src/Symfony/Component/JsonEncoder/Mapping/Decode/AttributePropertyMetadataLoader.php b/src/Symfony/Component/JsonEncoder/Mapping/Decode/AttributePropertyMetadataLoader.php index 511182b37148c..ac6d4c09f8415 100644 --- a/src/Symfony/Component/JsonEncoder/Mapping/Decode/AttributePropertyMetadataLoader.php +++ b/src/Symfony/Component/JsonEncoder/Mapping/Decode/AttributePropertyMetadataLoader.php @@ -12,12 +12,12 @@ namespace Symfony\Component\JsonEncoder\Mapping\Decode; use Psr\Container\ContainerInterface; -use Symfony\Component\JsonEncoder\Attribute\Denormalizer; use Symfony\Component\JsonEncoder\Attribute\EncodedName; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface; +use Symfony\Component\JsonEncoder\Attribute\ValueTransformer; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Exception\RuntimeException; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; /** @@ -31,7 +31,7 @@ final class AttributePropertyMetadataLoader implements PropertyMetadataLoaderInt { public function __construct( private PropertyMetadataLoaderInterface $decorated, - private ContainerInterface $denormalizers, + private ContainerInterface $valueTransformers, private TypeResolverInterface $typeResolver, ) { } @@ -51,45 +51,42 @@ public function load(string $className, array $options = [], array $context = [] $attributesMetadata = $this->getPropertyAttributesMetadata($propertyReflection); $encodedName = $attributesMetadata['name'] ?? $initialEncodedName; - if (null === $denormalizer = $attributesMetadata['denormalizer'] ?? null) { + if (null === $valueTransformer = $attributesMetadata['toNativeValueTransformer'] ?? null) { $result[$encodedName] = $initialMetadata; continue; } - if (\is_string($denormalizer)) { - $denormalizerService = $this->getAndValidateDenormalizerService($denormalizer); - $normalizedType = $denormalizerService::getNormalizedType(); + if (\is_string($valueTransformer)) { + $valueTransformerService = $this->getAndValidateValueTransformerService($valueTransformer); $result[$encodedName] = $initialMetadata - ->withType($normalizedType) - ->withAdditionalDenormalizer($denormalizer); + ->withType($valueTransformerService::getJsonValueType()) + ->withAdditionalToNativeValueTransformer($valueTransformer); continue; } try { - $denormalizerReflection = new \ReflectionFunction($denormalizer); + $valueTransformerReflection = new \ReflectionFunction($valueTransformer); } catch (\ReflectionException $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } - if (null === ($parameterReflection = $denormalizerReflection->getParameters()[0] ?? null)) { - throw new InvalidArgumentException(\sprintf('"%s" property\'s denormalizer callable has no parameter.', $initialEncodedName)); + if (null === ($parameterReflection = $valueTransformerReflection->getParameters()[0] ?? null)) { + throw new InvalidArgumentException(\sprintf('"%s" property\'s toNativeValue callable has no parameter.', $initialEncodedName)); } - $normalizedType = $this->typeResolver->resolve($parameterReflection); - $result[$encodedName] = $initialMetadata - ->withType($normalizedType) - ->withAdditionalDenormalizer($denormalizer); + ->withType($this->typeResolver->resolve($parameterReflection)) + ->withAdditionalToNativeValueTransformer($valueTransformer); } return $result; } /** - * @return array{name?: string, denormalizer?: string|\Closure} + * @return array{name?: string, toNativeValueTransformer?: string|\Closure} */ private function getPropertyAttributesMetadata(\ReflectionProperty $reflectionProperty): array { @@ -100,25 +97,25 @@ private function getPropertyAttributesMetadata(\ReflectionProperty $reflectionPr $metadata['name'] = $reflectionAttribute->newInstance()->getName(); } - $reflectionAttribute = $reflectionProperty->getAttributes(Denormalizer::class, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null; + $reflectionAttribute = $reflectionProperty->getAttributes(ValueTransformer::class, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null; if (null !== $reflectionAttribute) { - $metadata['denormalizer'] = $reflectionAttribute->newInstance()->getDenormalizer(); + $metadata['toNativeValueTransformer'] = $reflectionAttribute->newInstance()->getToNativeValueTransformer(); } return $metadata; } - private function getAndValidateDenormalizerService(string $denormalizerId): DenormalizerInterface + private function getAndValidateValueTransformerService(string $valueTransformerId): ValueTransformerInterface { - if (!$this->denormalizers->has($denormalizerId)) { - throw new InvalidArgumentException(\sprintf('You have requested a non-existent denormalizer service "%s". Did you implement "%s"?', $denormalizerId, DenormalizerInterface::class)); + if (!$this->valueTransformers->has($valueTransformerId)) { + throw new InvalidArgumentException(\sprintf('You have requested a non-existent value transformer service "%s". Did you implement "%s"?', $valueTransformerId, ValueTransformerInterface::class)); } - $denormalizer = $this->denormalizers->get($denormalizerId); - if (!$denormalizer instanceof DenormalizerInterface) { - throw new InvalidArgumentException(\sprintf('The "%s" denormalizer service does not implement "%s".', $denormalizerId, DenormalizerInterface::class)); + $valueTransformer = $this->valueTransformers->get($valueTransformerId); + if (!$valueTransformer instanceof ValueTransformerInterface) { + throw new InvalidArgumentException(\sprintf('The "%s" value transformer service does not implement "%s".', $valueTransformerId, ValueTransformerInterface::class)); } - return $denormalizer; + return $valueTransformer; } } diff --git a/src/Symfony/Component/JsonEncoder/Mapping/Decode/DateTimeTypePropertyMetadataLoader.php b/src/Symfony/Component/JsonEncoder/Mapping/Decode/DateTimeTypePropertyMetadataLoader.php index 719df2914574f..b3c2ad878ed6b 100644 --- a/src/Symfony/Component/JsonEncoder/Mapping/Decode/DateTimeTypePropertyMetadataLoader.php +++ b/src/Symfony/Component/JsonEncoder/Mapping/Decode/DateTimeTypePropertyMetadataLoader.php @@ -11,12 +11,13 @@ namespace Symfony\Component\JsonEncoder\Mapping\Decode; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer; +use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer; use Symfony\Component\TypeInfo\Type\ObjectType; /** - * Casts DateTime properties to string properties. + * Transforms string to DateTimeInterface for properties with DateTimeInterface type. * * @author Mathias Arlaud * @@ -37,13 +38,13 @@ public function load(string $className, array $options = [], array $context = [] $type = $metadata->getType(); if ($type instanceof ObjectType && is_a($type->getClassName(), \DateTimeInterface::class, true)) { - $dateTimeDenormalizer = match ($type->getClassName()) { - \DateTimeInterface::class, \DateTimeImmutable::class => 'json_encoder.denormalizer.date_time_immutable', - default => 'json_encoder.denormalizer.date_time', - }; + if (\DateTime::class === $type->getClassName()) { + throw new InvalidArgumentException('The "DateTime" class is not supported. Use "DateTimeImmutable" instead.'); + } + $metadata = $metadata - ->withType(DateTimeDenormalizer::getNormalizedType()) - ->withAdditionalDenormalizer($dateTimeDenormalizer); + ->withType(StringToDateTimeValueTransformer::getJsonValueType()) + ->withAdditionalToNativeValueTransformer('json_encoder.value_transformer.string_to_date_time'); } } diff --git a/src/Symfony/Component/JsonEncoder/Mapping/Encode/AttributePropertyMetadataLoader.php b/src/Symfony/Component/JsonEncoder/Mapping/Encode/AttributePropertyMetadataLoader.php index 47a3ff4a2d200..96af5f4a42495 100644 --- a/src/Symfony/Component/JsonEncoder/Mapping/Encode/AttributePropertyMetadataLoader.php +++ b/src/Symfony/Component/JsonEncoder/Mapping/Encode/AttributePropertyMetadataLoader.php @@ -13,11 +13,11 @@ use Psr\Container\ContainerInterface; use Symfony\Component\JsonEncoder\Attribute\EncodedName; -use Symfony\Component\JsonEncoder\Attribute\Normalizer; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; +use Symfony\Component\JsonEncoder\Attribute\ValueTransformer; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Exception\RuntimeException; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\TypeResolver\TypeResolverInterface; /** @@ -31,7 +31,7 @@ final class AttributePropertyMetadataLoader implements PropertyMetadataLoaderInt { public function __construct( private PropertyMetadataLoaderInterface $decorated, - private ContainerInterface $normalizers, + private ContainerInterface $valueTransformers, private TypeResolverInterface $typeResolver, ) { } @@ -51,41 +51,38 @@ public function load(string $className, array $options = [], array $context = [] $attributesMetadata = $this->getPropertyAttributesMetadata($propertyReflection); $encodedName = $attributesMetadata['name'] ?? $initialEncodedName; - if (null === $normalizer = $attributesMetadata['normalizer'] ?? null) { + if (null === $valueTransformer = $attributesMetadata['toJsonValueTransformer'] ?? null) { $result[$encodedName] = $initialMetadata; continue; } - if (\is_string($normalizer)) { - $normalizerService = $this->getAndValidateNormalizerService($normalizer); - $normalizedType = $normalizerService::getNormalizedType(); + if (\is_string($valueTransformer)) { + $valueTransformerService = $this->getAndValidateValueTransformerService($valueTransformer); $result[$encodedName] = $initialMetadata - ->withType($normalizedType) - ->withAdditionalNormalizer($normalizer); + ->withType($valueTransformerService::getJsonValueType()) + ->withAdditionalToJsonValueTransformer($valueTransformer); continue; } try { - $normalizerReflection = new \ReflectionFunction($normalizer); + $valueTransformerReflection = new \ReflectionFunction($valueTransformer); } catch (\ReflectionException $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } - $normalizedType = $this->typeResolver->resolve($normalizerReflection); - $result[$encodedName] = $initialMetadata - ->withType($normalizedType) - ->withAdditionalNormalizer($normalizer); + ->withType($this->typeResolver->resolve($valueTransformerReflection)) + ->withAdditionalToJsonValueTransformer($valueTransformer); } return $result; } /** - * @return array{name?: string, normalizer?: string|\Closure} + * @return array{name?: string, toJsonValueTransformer?: string|\Closure} */ private function getPropertyAttributesMetadata(\ReflectionProperty $reflectionProperty): array { @@ -96,25 +93,25 @@ private function getPropertyAttributesMetadata(\ReflectionProperty $reflectionPr $metadata['name'] = $reflectionAttribute->newInstance()->getName(); } - $reflectionAttribute = $reflectionProperty->getAttributes(Normalizer::class, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null; + $reflectionAttribute = $reflectionProperty->getAttributes(ValueTransformer::class, \ReflectionAttribute::IS_INSTANCEOF)[0] ?? null; if (null !== $reflectionAttribute) { - $metadata['normalizer'] = $reflectionAttribute->newInstance()->getNormalizer(); + $metadata['toJsonValueTransformer'] = $reflectionAttribute->newInstance()->getToJsonValueTransformer(); } return $metadata; } - private function getAndValidateNormalizerService(string $normalizerId): NormalizerInterface + private function getAndValidateValueTransformerService(string $valueTransformerId): ValueTransformerInterface { - if (!$this->normalizers->has($normalizerId)) { - throw new InvalidArgumentException(\sprintf('You have requested a non-existent normalizer service "%s". Did you implement "%s"?', $normalizerId, NormalizerInterface::class)); + if (!$this->valueTransformers->has($valueTransformerId)) { + throw new InvalidArgumentException(\sprintf('You have requested a non-existent value transformer service "%s". Did you implement "%s"?', $valueTransformerId, ValueTransformerInterface::class)); } - $normalizer = $this->normalizers->get($normalizerId); - if (!$normalizer instanceof NormalizerInterface) { - throw new InvalidArgumentException(\sprintf('The "%s" normalizer service does not implement "%s".', $normalizerId, NormalizerInterface::class)); + $valueTransformer = $this->valueTransformers->get($valueTransformerId); + if (!$valueTransformer instanceof ValueTransformerInterface) { + throw new InvalidArgumentException(\sprintf('The "%s" value transformer service does not implement "%s".', $valueTransformerId, ValueTransformerInterface::class)); } - return $normalizer; + return $valueTransformer; } } diff --git a/src/Symfony/Component/JsonEncoder/Mapping/Encode/DateTimeTypePropertyMetadataLoader.php b/src/Symfony/Component/JsonEncoder/Mapping/Encode/DateTimeTypePropertyMetadataLoader.php index 5fa327765f1a0..5edceb6c91545 100644 --- a/src/Symfony/Component/JsonEncoder/Mapping/Encode/DateTimeTypePropertyMetadataLoader.php +++ b/src/Symfony/Component/JsonEncoder/Mapping/Encode/DateTimeTypePropertyMetadataLoader.php @@ -11,12 +11,12 @@ namespace Symfony\Component\JsonEncoder\Mapping\Encode; -use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; +use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; use Symfony\Component\TypeInfo\Type\ObjectType; /** - * Casts DateTime properties to string properties. + * Transforms DateTimeInterface to string for properties with DateTimeInterface type. * * @author Mathias Arlaud * @@ -38,8 +38,8 @@ public function load(string $className, array $options = [], array $context = [] if ($type instanceof ObjectType && is_a($type->getClassName(), \DateTimeInterface::class, true)) { $metadata = $metadata - ->withType(DateTimeNormalizer::getNormalizedType()) - ->withAdditionalNormalizer('json_encoder.normalizer.date_time'); + ->withType(DateTimeToStringValueTransformer::getJsonValueType()) + ->withAdditionalToJsonValueTransformer('json_encoder.value_transformer.date_time_to_string'); } } diff --git a/src/Symfony/Component/JsonEncoder/Mapping/PropertyMetadata.php b/src/Symfony/Component/JsonEncoder/Mapping/PropertyMetadata.php index af129d55626c0..0716d85cc0a78 100644 --- a/src/Symfony/Component/JsonEncoder/Mapping/PropertyMetadata.php +++ b/src/Symfony/Component/JsonEncoder/Mapping/PropertyMetadata.php @@ -23,14 +23,14 @@ final class PropertyMetadata { /** - * @param list $normalizers - * @param list $denormalizers + * @param list $toJsonValueTransformers + * @param list $toNativeValueTransformers */ public function __construct( private string $name, private Type $type, - private array $normalizers = [], - private array $denormalizers = [], + private array $toJsonValueTransformers = [], + private array $toNativeValueTransformers = [], ) { } @@ -41,7 +41,7 @@ public function getName(): string public function withName(string $name): self { - return new self($name, $this->type, $this->normalizers, $this->denormalizers); + return new self($name, $this->type, $this->toJsonValueTransformers, $this->toNativeValueTransformers); } public function getType(): Type @@ -51,58 +51,58 @@ public function getType(): Type public function withType(Type $type): self { - return new self($this->name, $type, $this->normalizers, $this->denormalizers); + return new self($this->name, $type, $this->toJsonValueTransformers, $this->toNativeValueTransformers); } /** * @return list */ - public function getNormalizers(): array + public function getToJsonValueTransformer(): array { - return $this->normalizers; + return $this->toJsonValueTransformers; } /** - * @param list $normalizers + * @param list $toJsonValueTransformers */ - public function withNormalizers(array $normalizers): self + public function withToJsonValueTransformers(array $toJsonValueTransformers): self { - return new self($this->name, $this->type, $normalizers, $this->denormalizers); + return new self($this->name, $this->type, $toJsonValueTransformers, $this->toNativeValueTransformers); } - public function withAdditionalNormalizer(string|\Closure $normalizer): self + public function withAdditionalToJsonValueTransformer(string|\Closure $toJsonValueTransformer): self { - $normalizers = $this->normalizers; + $toJsonValueTransformers = $this->toJsonValueTransformers; - $normalizers[] = $normalizer; - $normalizers = array_values(array_unique($normalizers)); + $toJsonValueTransformers[] = $toJsonValueTransformer; + $toJsonValueTransformers = array_values(array_unique($toJsonValueTransformers)); - return $this->withNormalizers($normalizers); + return $this->withToJsonValueTransformers($toJsonValueTransformers); } /** * @return list */ - public function getDenormalizers(): array + public function getToNativeValueTransformers(): array { - return $this->denormalizers; + return $this->toNativeValueTransformers; } /** - * @param list $denormalizers + * @param list $toNativeValueTransformers */ - public function withDenormalizers(array $denormalizers): self + public function withToNativeValueTransformers(array $toNativeValueTransformers): self { - return new self($this->name, $this->type, $this->normalizers, $denormalizers); + return new self($this->name, $this->type, $this->toJsonValueTransformers, $toNativeValueTransformers); } - public function withAdditionalDenormalizer(string|\Closure $denormalizer): self + public function withAdditionalToNativeValueTransformer(string|\Closure $toNativeValueTransformer): self { - $denormalizers = $this->denormalizers; + $toNativeValueTransformers = $this->toNativeValueTransformers; - $denormalizers[] = $denormalizer; - $denormalizers = array_values(array_unique($denormalizers)); + $toNativeValueTransformers[] = $toNativeValueTransformer; + $toNativeValueTransformers = array_values(array_unique($toNativeValueTransformers)); - return $this->withDenormalizers($denormalizers); + return $this->withToNativeValueTransformers($toNativeValueTransformers); } } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Attribute/ValueTransformerTest.php b/src/Symfony/Component/JsonEncoder/Tests/Attribute/ValueTransformerTest.php new file mode 100644 index 0000000000000..f3b30e48a5609 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/Attribute/ValueTransformerTest.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonEncoder\Tests\Attribute; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\JsonEncoder\Attribute\ValueTransformer; +use Symfony\Component\JsonEncoder\Exception\LogicException; + +class ValueTransformerTest extends TestCase +{ + public function testCannotCreateWithoutAnything() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('#[ValueTransformer] attribute must declare either $toNativeValue or $toJsonValue.'); + + new ValueTransformer(); + } +} diff --git a/src/Symfony/Component/JsonEncoder/Tests/Decode/DecoderGeneratorTest.php b/src/Symfony/Component/JsonEncoder/Tests/Decode/DecoderGeneratorTest.php index 20437eb492d94..b91fd9f091d66 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Decode/DecoderGeneratorTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Decode/DecoderGeneratorTest.php @@ -19,16 +19,16 @@ use Symfony\Component\JsonEncoder\Mapping\GenericTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\BooleanStringDenormalizer; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\DivideStringAndCastToIntDenormalizer; use Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum; use Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyEnum; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNullableProperties; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithOtherDummies; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithUnionProperties; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DivideStringAndCastToIntValueTransformer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\StringToBooleanValueTransformer; use Symfony\Component\JsonEncoder\Tests\ServiceContainer; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory; @@ -60,8 +60,8 @@ public function testGeneratedDecoder(string $fixture, Type $type) new DateTimeTypePropertyMetadataLoader(new AttributePropertyMetadataLoader( new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer([ - DivideStringAndCastToIntDenormalizer::class => new DivideStringAndCastToIntDenormalizer(), - BooleanStringDenormalizer::class => new BooleanStringDenormalizer(), + DivideStringAndCastToIntValueTransformer::class => new DivideStringAndCastToIntValueTransformer(), + StringToBooleanValueTransformer::class => new StringToBooleanValueTransformer(), ]), TypeResolver::create(), )), @@ -107,7 +107,7 @@ public static function generatedDecoderDataProvider(): iterable yield ['nullable_object', Type::nullable(Type::object(ClassicDummy::class))]; yield ['object_in_object', Type::object(DummyWithOtherDummies::class)]; yield ['object_with_nullable_properties', Type::object(DummyWithNullableProperties::class)]; - yield ['object_with_denormalizer', Type::object(DummyWithNormalizerAttributes::class)]; + yield ['object_with_value_transformer', Type::object(DummyWithValueTransformerAttributes::class)]; yield ['union', Type::union(Type::int(), Type::list(Type::enum(DummyBackedEnum::class)), Type::object(DummyWithNameAttributes::class))]; yield ['object_with_union', Type::object(DummyWithUnionProperties::class)]; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Decode/Denormalizer/DateTimeDenormalizerTest.php b/src/Symfony/Component/JsonEncoder/Tests/Decode/Denormalizer/DateTimeDenormalizerTest.php deleted file mode 100644 index 9f9a2c6cc4548..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Tests/Decode/Denormalizer/DateTimeDenormalizerTest.php +++ /dev/null @@ -1,76 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonEncoder\Tests\Decode\Denormalizer; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DateTimeDenormalizer; -use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; - -class DateTimeDenormalizerTest extends TestCase -{ - public function testDenormalizeImmutable() - { - $denormalizer = new DateTimeDenormalizer(immutable: true); - - $this->assertEquals( - new \DateTimeImmutable('2023-07-26'), - $denormalizer->denormalize('2023-07-26'), - ); - - $this->assertEquals( - (new \DateTimeImmutable('2023-07-26'))->setTime(0, 0), - $denormalizer->denormalize('26/07/2023 00:00:00', [DateTimeDenormalizer::FORMAT_KEY => 'd/m/Y H:i:s']), - ); - } - - public function testDenormalizeMutable() - { - $denormalizer = new DateTimeDenormalizer(immutable: false); - - $this->assertEquals( - new \DateTime('2023-07-26'), - $denormalizer->denormalize('2023-07-26'), - ); - - $this->assertEquals( - (new \DateTime('2023-07-26'))->setTime(0, 0), - $denormalizer->denormalize('26/07/2023 00:00:00', [DateTimeDenormalizer::FORMAT_KEY => 'd/m/Y H:i:s']), - ); - } - - public function testThrowWhenInvalidNormalized() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('The normalized data is either not an string, or an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); - - (new DateTimeDenormalizer(immutable: true))->denormalize(true); - } - - public function testThrowWhenInvalidDateTimeString() - { - $denormalizer = new DateTimeDenormalizer(immutable: true); - - try { - $denormalizer->denormalize('0'); - $this->fail(\sprintf('A "%s" exception must have been thrown.', InvalidArgumentException::class)); - } catch (InvalidArgumentException $e) { - $this->assertEquals("Parsing datetime string \"0\" resulted in 1 errors: \nat position 0: Unexpected character", $e->getMessage()); - } - - try { - $denormalizer->denormalize('0', [DateTimeDenormalizer::FORMAT_KEY => 'Y-m-d']); - $this->fail(\sprintf('A "%s" exception must have been thrown.', InvalidArgumentException::class)); - } catch (InvalidArgumentException $e) { - $this->assertEquals("Parsing datetime string \"0\" using format \"Y-m-d\" resulted in 1 errors: \nat position 1: Not enough data available to satisfy format", $e->getMessage()); - } - } -} diff --git a/src/Symfony/Component/JsonEncoder/Tests/Decode/LazyInstantiatorTest.php b/src/Symfony/Component/JsonEncoder/Tests/Decode/LazyInstantiatorTest.php index 926e6d52a048b..13cd60f5d1884 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Decode/LazyInstantiatorTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Decode/LazyInstantiatorTest.php @@ -15,7 +15,7 @@ use Symfony\Component\JsonEncoder\Decode\LazyInstantiator; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes; class LazyInstantiatorTest extends TestCase { @@ -50,7 +50,7 @@ public function testCreateLazyGhostUsingVarExporter() */ public function testCreateCacheFile() { - (new LazyInstantiator($this->lazyGhostsDir))->instantiate(DummyWithNormalizerAttributes::class, function (ClassicDummy $object): void {}); + (new LazyInstantiator($this->lazyGhostsDir))->instantiate(DummyWithValueTransformerAttributes::class, function (ClassicDummy $object): void {}); $this->assertCount(1, glob($this->lazyGhostsDir.'/*')); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Encode/EncoderGeneratorTest.php b/src/Symfony/Component/JsonEncoder/Tests/Encode/EncoderGeneratorTest.php index e0a9b187e73e9..1d7e1b326febc 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Encode/EncoderGeneratorTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Encode/EncoderGeneratorTest.php @@ -23,11 +23,11 @@ use Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyEnum; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithOtherDummies; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithUnionProperties; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\BooleanStringNormalizer; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\DoubleIntAndCastToStringNormalizer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\BooleanToStringValueTransformer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DoubleIntAndCastToStringValueTransformer; use Symfony\Component\JsonEncoder\Tests\ServiceContainer; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeContext\TypeContextFactory; @@ -59,8 +59,8 @@ public function testGeneratedEncoder(string $fixture, Type $type) new DateTimeTypePropertyMetadataLoader(new AttributePropertyMetadataLoader( new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer([ - DoubleIntAndCastToStringNormalizer::class => new DoubleIntAndCastToStringNormalizer(), - BooleanStringNormalizer::class => new BooleanStringNormalizer(), + DoubleIntAndCastToStringValueTransformer::class => new DoubleIntAndCastToStringValueTransformer(), + BooleanToStringValueTransformer::class => new BooleanToStringValueTransformer(), ]), TypeResolver::create(), )), @@ -103,7 +103,7 @@ public static function generatedEncoderDataProvider(): iterable yield ['object', Type::object(DummyWithNameAttributes::class)]; yield ['nullable_object', Type::nullable(Type::object(DummyWithNameAttributes::class))]; yield ['object_in_object', Type::object(DummyWithOtherDummies::class)]; - yield ['object_with_normalizer', Type::object(DummyWithNormalizerAttributes::class)]; + yield ['object_with_value_transformer', Type::object(DummyWithValueTransformerAttributes::class)]; yield ['union', Type::union(Type::int(), Type::list(Type::enum(DummyBackedEnum::class)), Type::object(DummyWithNameAttributes::class))]; yield ['object_with_union', Type::object(DummyWithUnionProperties::class)]; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Encode/Normalizer/DateTimeNormalizerTest.php b/src/Symfony/Component/JsonEncoder/Tests/Encode/Normalizer/DateTimeNormalizerTest.php deleted file mode 100644 index 605311097a7ec..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Tests/Encode/Normalizer/DateTimeNormalizerTest.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\JsonEncoder\Tests\Encode\Normalizer; - -use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; -use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; - -class DateTimeNormalizerTest extends TestCase -{ - public function testNormalize() - { - $normalizer = new DateTimeNormalizer(); - - $this->assertEquals( - '2023-07-26T00:00:00+00:00', - $normalizer->normalize(new \DateTimeImmutable('2023-07-26', new \DateTimeZone('UTC'))), - ); - - $this->assertEquals( - '26/07/2023 00:00:00', - $normalizer->normalize((new \DateTimeImmutable('2023-07-26', new \DateTimeZone('UTC')))->setTime(0, 0), [DateTimeNormalizer::FORMAT_KEY => 'd/m/Y H:i:s']), - ); - } - - public function testThrowWhenInvalidDenormalized() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('The denormalized data must implement the "\DateTimeInterface".'); - - (new DateTimeNormalizer())->normalize(true); - } -} diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Attribute/BooleanStringDenormalizer.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Attribute/BooleanStringDenormalizer.php deleted file mode 100644 index 5be4206abe4f0..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Attribute/BooleanStringDenormalizer.php +++ /dev/null @@ -1,15 +0,0 @@ - (int) $v, explode('..', $range)); - } -} diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Model/DummyWithValueTransformerAttributes.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Model/DummyWithValueTransformerAttributes.php new file mode 100644 index 0000000000000..ea7a153ad8b54 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Model/DummyWithValueTransformerAttributes.php @@ -0,0 +1,45 @@ + (int) $v, explode('..', $range)); + } +} diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Normalizer/BooleanStringNormalizer.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Normalizer/BooleanStringNormalizer.php deleted file mode 100644 index 6736224698823..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/Normalizer/BooleanStringNormalizer.php +++ /dev/null @@ -1,19 +0,0 @@ -'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/iterable.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/iterable.php index f0645e3f291d1..b52d20ec575a9 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/iterable.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/iterable.php @@ -1,5 +1,5 @@ $v) { yield $k => \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/list.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/list.php index f0645e3f291d1..b52d20ec575a9 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/list.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/list.php @@ -1,5 +1,5 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitList($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/mixed.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/mixed.php index f0645e3f291d1..b52d20ec575a9 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/mixed.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/mixed.php @@ -1,5 +1,5 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy|null'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy|null'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { if (\is_array($data)) { return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($data); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object.stream.php index a7f614070baad..6fd33c206d897 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object.stream.php @@ -1,9 +1,9 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -13,7 +13,7 @@ } }); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy|null'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy|null'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length); if (\is_array($data)) { return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($data); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.php index 21990e4bacaa8..3ea9636ceb8e8 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.php @@ -1,20 +1,20 @@ '] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { - $iterable = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\Instantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { + $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($v); } }; return \iterator_to_array($iterable($data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); }; - $providers['array|null'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['array|null'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { if (\is_array($data)) { return $providers['array']($data); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.stream.php index 0189600e61763..84abb98cdeb92 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_dict.stream.php @@ -1,18 +1,18 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($stream, $v[0], $v[1]); } }; return \iterator_to_array($iterable($stream, $data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -22,7 +22,7 @@ } }); }; - $providers['array|null'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['array|null'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length); if (\is_array($data)) { return $providers['array']($data); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.php index 5e30d62fa5b9c..4fd20bb5fda8a 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.php @@ -1,20 +1,20 @@ '] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { - $iterable = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\Instantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { + $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($v); } }; return \iterator_to_array($iterable($data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); }; - $providers['array|null'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['array|null'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { if (\is_array($data) && \array_is_list($data)) { return $providers['array']($data); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.stream.php index ac3e4e3a28957..ff9db52b53d87 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/nullable_object_list.stream.php @@ -1,18 +1,18 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitList($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($stream, $v[0], $v[1]); } }; return \iterator_to_array($iterable($stream, $data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -22,7 +22,7 @@ } }); }; - $providers['array|null'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['array|null'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length); if (\is_array($data) && \array_is_list($data)) { return $providers['array']($data); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.php index 9214a4ac7b60c..5ff0c2a0d0b5c 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.php @@ -1,7 +1,7 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.stream.php index 5e7782673a097..7942f34d1b073 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object.stream.php @@ -1,9 +1,9 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.php index f5f9805ade493..8b841095ea394 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.php @@ -1,15 +1,15 @@ '] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { - $iterable = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\Instantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { + $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($v); } }; return \iterator_to_array($iterable($data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.stream.php index a6da8487c7992..cd029e43f4cfb 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_dict.stream.php @@ -1,18 +1,18 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($stream, $v[0], $v[1]); } }; return \iterator_to_array($iterable($stream, $data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.php index 59b3e7e1f38da..a62c9de14a9e2 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.php @@ -1,17 +1,17 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithOtherDummies::class, \array_filter(['name' => $data['name'] ?? '_symfony_missing_value', 'otherDummyOne' => \array_key_exists('otherDummyOne', $data) ? $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes']($data['otherDummyOne']) : '_symfony_missing_value', 'otherDummyTwo' => \array_key_exists('otherDummyTwo', $data) ? $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($data['otherDummyTwo']) : '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes::class, \array_filter(['id' => $data['@id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.stream.php index cbab332a9b1d9..cae4d3fd36e1f 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_in_object.stream.php @@ -1,9 +1,9 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithOtherDummies::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithOtherDummies::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'name' => $object->name = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -14,9 +14,9 @@ } }); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { '@id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -26,9 +26,9 @@ } }); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.php index 0dfbb689419cb..85d964fc89c7f 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.php @@ -1,15 +1,15 @@ '] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { - $iterable = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\Instantiator $instantiator, array $options): mixed { + $providers['iterable'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { + $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($v); } }; return $iterable($data); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.stream.php index cfdf671d8a9bc..11942db9b632e 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_iterable.stream.php @@ -1,18 +1,18 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['iterable'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($stream, $v[0], $v[1]); } }; return $iterable($stream, $data); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.php index bb0aa363dc887..e13597b2aff78 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.php @@ -1,15 +1,15 @@ '] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { - $iterable = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\Instantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { + $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($v); } }; return \iterator_to_array($iterable($data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, \array_filter(['id' => $data['id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.stream.php index 22d1d55cbc474..5a5342c291d78 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_list.stream.php @@ -1,18 +1,18 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitList($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy']($stream, $v[0], $v[1]); } }; return \iterator_to_array($iterable($stream, $data)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.php deleted file mode 100644 index ea31bddf19f61..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.php +++ /dev/null @@ -1,10 +0,0 @@ -instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes::class, \array_filter(['id' => $denormalizers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\DivideStringAndCastToIntDenormalizer')->denormalize($data['id'] ?? '_symfony_missing_value', $options), 'active' => $denormalizers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\BooleanStringDenormalizer')->denormalize($data['active'] ?? '_symfony_missing_value', $options), 'name' => strtoupper($data['name'] ?? '_symfony_missing_value'), 'range' => Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes::explodeRange($data['range'] ?? '_symfony_missing_value', $options)], static function ($v) { - return '_symfony_missing_value' !== $v; - })); - }; - return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes'](\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeString((string) $string)); -}; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.stream.php deleted file mode 100644 index f7d97892ab8e0..0000000000000 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_denormalizer.stream.php +++ /dev/null @@ -1,19 +0,0 @@ -instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { - foreach ($data as $k => $v) { - match ($k) { - 'id' => $object->id = $denormalizers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\DivideStringAndCastToIntDenormalizer')->denormalize(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), $options), - 'active' => $object->active = $denormalizers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\BooleanStringDenormalizer')->denormalize(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), $options), - 'name' => $object->name = strtoupper(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1])), - 'range' => $object->range = Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes::explodeRange(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), $options), - default => null, - }; - } - }); - }; - return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes']($stream, 0, null); -}; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.php index d6c1669323a38..6f041f99fad36 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.php @@ -1,7 +1,7 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNullableProperties::class, \array_filter(['name' => $data['name'] ?? '_symfony_missing_value', 'enum' => \array_key_exists('enum', $data) ? $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null']($data['enum']) : '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); @@ -9,7 +9,7 @@ $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum'] = static function ($data) { return \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum::from($data); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { if (\is_int($data)) { return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum']($data); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.stream.php index 3f1aaef7023d3..ed3b5b4ab381f 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_nullable_properties.stream.php @@ -1,9 +1,9 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNullableProperties::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNullableProperties::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'name' => $object->name = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -16,7 +16,7 @@ $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum'] = static function ($stream, $offset, $length) { return \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum::from(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length); if (\is_int($data)) { return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum']($data); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.php index b75387cfc5c3d..567c335341460 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.php @@ -1,7 +1,7 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithUnionProperties::class, \array_filter(['value' => \array_key_exists('value', $data) ? $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null|string']($data['value']) : '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); @@ -9,7 +9,7 @@ $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum'] = static function ($data) { return \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum::from($data); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null|string'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null|string'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { if (\is_int($data)) { return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum']($data); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.stream.php index 025751bbb64a8..2526149c8b43b 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_union.stream.php @@ -1,9 +1,9 @@ instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithUnionProperties::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithUnionProperties::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { 'value' => $object->value = $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null|string']($stream, $v[0], $v[1]), @@ -15,7 +15,7 @@ $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum'] = static function ($stream, $offset, $length) { return \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum::from(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null|string'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum|null|string'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length); if (\is_int($data)) { return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum']($data); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.php new file mode 100644 index 0000000000000..a84a95463e626 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.php @@ -0,0 +1,10 @@ +instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::class, \array_filter(['id' => $valueTransformers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DivideStringAndCastToIntValueTransformer')->transform($data['id'] ?? '_symfony_missing_value', $options), 'active' => $valueTransformers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\StringToBooleanValueTransformer')->transform($data['active'] ?? '_symfony_missing_value', $options), 'name' => strtoupper($data['name'] ?? '_symfony_missing_value'), 'range' => Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::explodeRange($data['range'] ?? '_symfony_missing_value', $options)], static function ($v) { + return '_symfony_missing_value' !== $v; + })); + }; + return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes'](\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeString((string) $string)); +}; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.stream.php new file mode 100644 index 0000000000000..6fb3fdab1a00c --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/object_with_value_transformer.stream.php @@ -0,0 +1,19 @@ +instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { + foreach ($data as $k => $v) { + match ($k) { + 'id' => $object->id = $valueTransformers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DivideStringAndCastToIntValueTransformer')->transform(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), $options), + 'active' => $object->active = $valueTransformers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\StringToBooleanValueTransformer')->transform(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), $options), + 'name' => $object->name = strtoupper(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1])), + 'range' => $object->range = Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::explodeRange(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), $options), + default => null, + }; + } + }); + }; + return $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes']($stream, 0, null); +}; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/scalar.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/scalar.php index f0645e3f291d1..b52d20ec575a9 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/scalar.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/scalar.php @@ -1,5 +1,5 @@ '] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { - $iterable = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (string|\Stringable $string, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\Instantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { + $iterable = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum']($v); } @@ -12,12 +12,12 @@ $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum'] = static function ($data) { return \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum::from($data); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes::class, \array_filter(['id' => $data['@id'] ?? '_symfony_missing_value', 'name' => $data['name'] ?? '_symfony_missing_value'], static function ($v) { return '_symfony_missing_value' !== $v; })); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes|array|int'] = static function ($data) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes|array|int'] = static function ($data) use ($options, $valueTransformers, $instantiator, &$providers) { if (\is_array($data) && \array_is_list($data)) { return $providers['array']($data); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/union.stream.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/union.stream.php index 38228a55075d6..8fe29d0707ab9 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/union.stream.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/decoder/union.stream.php @@ -1,9 +1,9 @@ '] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { +return static function (mixed $stream, \Psr\Container\ContainerInterface $valueTransformers, \Symfony\Component\JsonEncoder\Decode\LazyInstantiator $instantiator, array $options): mixed { + $providers['array'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitList($stream, $offset, $length); - $iterable = static function ($stream, $data) use ($options, $denormalizers, $instantiator, &$providers) { + $iterable = static function ($stream, $data) use ($options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { yield $k => $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum']($stream, $v[0], $v[1]); } @@ -13,9 +13,9 @@ $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum'] = static function ($stream, $offset, $length) { return \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum::from(\Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length)); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\Splitter::splitDict($stream, $offset, $length); - return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes::class, static function ($object) use ($stream, $data, $options, $denormalizers, $instantiator, &$providers) { + return $instantiator->instantiate(\Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes::class, static function ($object) use ($stream, $data, $options, $valueTransformers, $instantiator, &$providers) { foreach ($data as $k => $v) { match ($k) { '@id' => $object->id = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $v[0], $v[1]), @@ -25,7 +25,7 @@ } }); }; - $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes|array|int'] = static function ($stream, $offset, $length) use ($options, $denormalizers, $instantiator, &$providers) { + $providers['Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes|array|int'] = static function ($stream, $offset, $length) use ($options, $valueTransformers, $instantiator, &$providers) { $data = \Symfony\Component\JsonEncoder\Decode\NativeDecoder::decodeStream($stream, $offset, $length); if (\is_array($data) && \array_is_list($data)) { return $providers['array']($data); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/backed_enum.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/backed_enum.php index a1a44fe635a11..6f92d380a89df 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/backed_enum.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/backed_enum.php @@ -1,5 +1,5 @@ value); }; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/bool.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/bool.php index 2695b4beea962..e3d7a957734d4 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/bool.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/bool.php @@ -1,5 +1,5 @@ value); } elseif (null === $data) { diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object.php index 69cc96454706f..f42e35601c5fa 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object.php @@ -1,6 +1,6 @@ id); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object_dict.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object_dict.php index d52de84897efc..a62d9b76f6bf6 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object_dict.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/nullable_object_dict.php @@ -1,6 +1,6 @@ id); yield ',"name":'; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_dict.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_dict.php index 7297d6eee139b..81d92f22a04f3 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_dict.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_dict.php @@ -1,6 +1,6 @@ $value) { diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_in_object.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_in_object.php index 8815a1c2d2f63..3b4651722d8c6 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_in_object.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_in_object.php @@ -1,6 +1,6 @@ name); yield ',"otherDummyOne":{"@id":'; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_iterable.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_iterable.php index e1780548e7a35..c7c859fdc2cb9 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_iterable.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_iterable.php @@ -1,6 +1,6 @@ $value) { diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_list.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_list.php index 73c8517f7b755..967e6351b4ac8 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_list.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_list.php @@ -1,6 +1,6 @@ get('Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\DoubleIntAndCastToStringNormalizer')->normalize($data->id, $options)); - yield ',"active":'; - yield \json_encode($normalizers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\BooleanStringNormalizer')->normalize($data->active, $options)); - yield ',"name":'; - yield \json_encode(strtolower($data->name)); - yield ',"range":'; - yield \json_encode(Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes::concatRange($data->range, $options)); - yield '}'; -}; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_union.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_union.php index b1dd0c6480b2a..b3bce12cf189f 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_union.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_union.php @@ -1,6 +1,6 @@ value instanceof \Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum) { yield \json_encode($data->value->value); diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_value_transformer.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_value_transformer.php new file mode 100644 index 0000000000000..3a9d5d182bc33 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/object_with_value_transformer.php @@ -0,0 +1,13 @@ +get('Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DoubleIntAndCastToStringValueTransformer')->transform($data->id, $options)); + yield ',"active":'; + yield \json_encode($valueTransformers->get('Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\BooleanToStringValueTransformer')->transform($data->active, $options)); + yield ',"name":'; + yield \json_encode(strtolower($data->name)); + yield ',"range":'; + yield \json_encode(Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::concatRange($data->range, $options)); + yield '}'; +}; diff --git a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/scalar.php b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/scalar.php index 6eec711284d61..c46da0946cf27 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/scalar.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Fixtures/encoder/scalar.php @@ -1,5 +1,5 @@ new BooleanStringDenormalizer(), - DivideStringAndCastToIntDenormalizer::class => new DivideStringAndCastToIntDenormalizer(), + valueTransformers: [ + StringToBooleanValueTransformer::class => new StringToBooleanValueTransformer(), + DivideStringAndCastToIntValueTransformer::class => new DivideStringAndCastToIntValueTransformer(), ], decodersDir: $this->decodersDir, lazyGhostsDir: $this->lazyGhostsDir, ); $this->assertDecoded($decoder, function (mixed $decoded) { - $this->assertInstanceOf(DummyWithNormalizerAttributes::class, $decoded); + $this->assertInstanceOf(DummyWithValueTransformerAttributes::class, $decoded); $this->assertSame(10, $decoded->id); $this->assertTrue($decoded->active); $this->assertSame('LOWERCASE NAME', $decoded->name); $this->assertSame([0, 1], $decoded->range); - }, '{"id": "20", "active": "true", "name": "lowercase name", "range": "0..1"}', Type::object(DummyWithNormalizerAttributes::class), ['scale' => 1]); + }, '{"id": "20", "active": "true", "name": "lowercase name", "range": "0..1"}', Type::object(DummyWithValueTransformerAttributes::class), ['scale' => 1]); } public function testDecodeObjectWithPhpDoc() @@ -161,8 +161,7 @@ public function testDecodeObjectWithDateTimes() $this->assertInstanceOf(DummyWithDateTimes::class, $decoded); $this->assertEquals(new \DateTimeImmutable('2024-11-20'), $decoded->interface); $this->assertEquals(new \DateTimeImmutable('2025-11-20'), $decoded->immutable); - $this->assertEquals(new \DateTime('2024-10-05'), $decoded->mutable); - }, '{"interface":"2024-11-20","immutable":"2025-11-20","mutable":"2024-10-05"}', Type::object(DummyWithDateTimes::class)); + }, '{"interface":"2024-11-20","immutable":"2025-11-20"}', Type::object(DummyWithDateTimes::class)); } public function testCreateDecoderFile() diff --git a/src/Symfony/Component/JsonEncoder/Tests/JsonEncoderTest.php b/src/Symfony/Component/JsonEncoder/Tests/JsonEncoderTest.php index 8cab0f3474c7b..65bd91dbe6c0c 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/JsonEncoderTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/JsonEncoderTest.php @@ -12,21 +12,21 @@ namespace Symfony\Component\JsonEncoder\Tests; use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonEncoder\Encode\Normalizer\DateTimeNormalizer; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; use Symfony\Component\JsonEncoder\Exception\MaxDepthException; use Symfony\Component\JsonEncoder\JsonEncoder; use Symfony\Component\JsonEncoder\Tests\Fixtures\Enum\DummyBackedEnum; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\ClassicDummy; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithDateTimes; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNullableProperties; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithPhpDoc; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithUnionProperties; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\SelfReferencingDummy; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\BooleanStringNormalizer; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\DoubleIntAndCastToStringNormalizer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\BooleanToStringValueTransformer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DoubleIntAndCastToStringValueTransformer; +use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; class JsonEncoderTest extends TestCase @@ -126,20 +126,20 @@ public function testEncodeObjectWithEncodedName() $this->assertEncoded('{"@id":10,"name":"dummy name"}', $dummy, Type::object(DummyWithNameAttributes::class)); } - public function testEncodeObjectWithNormalizer() + public function testEncodeObjectWithValueTransformer() { - $dummy = new DummyWithNormalizerAttributes(); + $dummy = new DummyWithValueTransformerAttributes(); $dummy->id = 10; $dummy->active = true; $this->assertEncoded( '{"id":"20","active":"true","name":"dummy","range":"10..20"}', $dummy, - Type::object(DummyWithNormalizerAttributes::class), + Type::object(DummyWithValueTransformerAttributes::class), options: ['scale' => 1], - normalizers: [ - BooleanStringNormalizer::class => new BooleanStringNormalizer(), - DoubleIntAndCastToStringNormalizer::class => new DoubleIntAndCastToStringNormalizer(), + valueTransformers: [ + BooleanToStringValueTransformer::class => new BooleanToStringValueTransformer(), + DoubleIntAndCastToStringValueTransformer::class => new DoubleIntAndCastToStringValueTransformer(), ], ); } @@ -161,19 +161,15 @@ public function testEncodeObjectWithNullableProperties() public function testEncodeObjectWithDateTimes() { - $mutableDate = new \DateTime('2024-11-20'); - $immutableDate = \DateTimeImmutable::createFromMutable($mutableDate); - $dummy = new DummyWithDateTimes(); - $dummy->interface = $immutableDate; - $dummy->immutable = $immutableDate; - $dummy->mutable = $mutableDate; + $dummy->interface = new \DateTimeImmutable('2024-11-20'); + $dummy->immutable = new \DateTimeImmutable('2025-11-20'); $this->assertEncoded( - '{"interface":"2024-11-20","immutable":"2024-11-20","mutable":"2024-11-20"}', + '{"interface":"2024-11-20","immutable":"2025-11-20"}', $dummy, Type::object(DummyWithDateTimes::class), - options: [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'], + options: [DateTimeToStringValueTransformer::FORMAT_KEY => 'Y-m-d'], ); } @@ -222,12 +218,12 @@ public function testCreateEncoderFileOnlyIfNotExists() } /** - * @param array $options - * @param array $normalizers + * @param array $options + * @param array $valueTransformers */ - private function assertEncoded(string $expected, mixed $data, Type $type, array $options = [], array $normalizers = []): void + private function assertEncoded(string $expected, mixed $data, Type $type, array $options = [], array $valueTransformers = []): void { - $encoder = JsonEncoder::create(encodersDir: $this->encodersDir, normalizers: $normalizers); + $encoder = JsonEncoder::create(encodersDir: $this->encodersDir, valueTransformers: $valueTransformers); $this->assertSame($expected, (string) $encoder->encode($data, $type, $options)); } } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/AttributePropertyMetadataLoaderTest.php b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/AttributePropertyMetadataLoaderTest.php index 7925a610a1cc3..59affb8ea6709 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/AttributePropertyMetadataLoaderTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/AttributePropertyMetadataLoaderTest.php @@ -12,16 +12,16 @@ namespace Symfony\Component\JsonEncoder\Tests\Mapping\Decode; use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonEncoder\Decode\Denormalizer\DenormalizerInterface; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Mapping\Decode\AttributePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadata; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\BooleanStringDenormalizer; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Denormalizer\DivideStringAndCastToIntDenormalizer; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DivideStringAndCastToIntValueTransformer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\StringToBooleanValueTransformer; use Symfony\Component\JsonEncoder\Tests\ServiceContainer; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; @@ -34,41 +34,41 @@ public function testRetrieveEncodedName() $this->assertSame(['@id', 'name'], array_keys($loader->load(DummyWithNameAttributes::class))); } - public function testRetrieveDenormalizer() + public function testRetrieveValueTransformer() { $loader = new AttributePropertyMetadataLoader(new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer([ - DivideStringAndCastToIntDenormalizer::class => new DivideStringAndCastToIntDenormalizer(), - BooleanStringDenormalizer::class => new BooleanStringDenormalizer(), + DivideStringAndCastToIntValueTransformer::class => new DivideStringAndCastToIntValueTransformer(), + StringToBooleanValueTransformer::class => new StringToBooleanValueTransformer(), ]), TypeResolver::create()); $this->assertEquals([ - 'id' => new PropertyMetadata('id', Type::string(), [], [DivideStringAndCastToIntDenormalizer::class]), - 'active' => new PropertyMetadata('active', Type::string(), [], [BooleanStringDenormalizer::class]), + 'id' => new PropertyMetadata('id', Type::string(), [], [DivideStringAndCastToIntValueTransformer::class]), + 'active' => new PropertyMetadata('active', Type::string(), [], [StringToBooleanValueTransformer::class]), 'name' => new PropertyMetadata('name', Type::string(), [], [\Closure::fromCallable('strtolower')]), - 'range' => new PropertyMetadata('range', Type::string(), [], [\Closure::fromCallable(DummyWithNormalizerAttributes::concatRange(...))]), - ], $loader->load(DummyWithNormalizerAttributes::class)); + 'range' => new PropertyMetadata('range', Type::string(), [], [\Closure::fromCallable(DummyWithValueTransformerAttributes::concatRange(...))]), + ], $loader->load(DummyWithValueTransformerAttributes::class)); } - public function testThrowWhenCannotRetrieveDenormalizer() + public function testThrowWhenCannotRetrieveValueTransformer() { $loader = new AttributePropertyMetadataLoader(new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer(), TypeResolver::create()); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage(\sprintf('You have requested a non-existent denormalizer service "%s". Did you implement "%s"?', DivideStringAndCastToIntDenormalizer::class, DenormalizerInterface::class)); + $this->expectExceptionMessage(\sprintf('You have requested a non-existent value transformer service "%s". Did you implement "%s"?', DivideStringAndCastToIntValueTransformer::class, ValueTransformerInterface::class)); - $loader->load(DummyWithNormalizerAttributes::class); + $loader->load(DummyWithValueTransformerAttributes::class); } - public function testThrowWhenInvaliDenormalizer() + public function testThrowWhenInvaliValueTransformer() { $loader = new AttributePropertyMetadataLoader(new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer([ - DivideStringAndCastToIntDenormalizer::class => true, - BooleanStringDenormalizer::class => new BooleanStringDenormalizer(), + DivideStringAndCastToIntValueTransformer::class => true, + StringToBooleanValueTransformer::class => new StringToBooleanValueTransformer(), ]), TypeResolver::create()); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage(\sprintf('The "%s" denormalizer service does not implement "%s".', DivideStringAndCastToIntDenormalizer::class, DenormalizerInterface::class)); + $this->expectExceptionMessage(\sprintf('The "%s" value transformer service does not implement "%s".', DivideStringAndCastToIntValueTransformer::class, ValueTransformerInterface::class)); - $loader->load(DummyWithNormalizerAttributes::class); + $loader->load(DummyWithValueTransformerAttributes::class); } } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/DateTimeTypePropertyMetadataLoaderTest.php b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/DateTimeTypePropertyMetadataLoaderTest.php index 223eb053e85ef..fd94c1a73979c 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/DateTimeTypePropertyMetadataLoaderTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Decode/DateTimeTypePropertyMetadataLoaderTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\JsonEncoder\Tests\Mapping\Decode; use PHPUnit\Framework\TestCase; +use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Mapping\Decode\DateTimeTypePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadata; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface; @@ -19,23 +20,33 @@ class DateTimeTypePropertyMetadataLoaderTest extends TestCase { - public function testAddDateTimeDenormalizer() + public function testAddStringToDateTimeValueTransformer() { $loader = new DateTimeTypePropertyMetadataLoader(self::propertyMetadataLoader([ 'interface' => new PropertyMetadata('interface', Type::object(\DateTimeInterface::class)), 'immutable' => new PropertyMetadata('immutable', Type::object(\DateTimeImmutable::class)), - 'mutable' => new PropertyMetadata('mutable', Type::object(\DateTime::class)), 'other' => new PropertyMetadata('other', Type::object(self::class)), ])); $this->assertEquals([ - 'interface' => new PropertyMetadata('interface', Type::string(), [], ['json_encoder.denormalizer.date_time_immutable']), - 'immutable' => new PropertyMetadata('immutable', Type::string(), [], ['json_encoder.denormalizer.date_time_immutable']), - 'mutable' => new PropertyMetadata('mutable', Type::string(), [], ['json_encoder.denormalizer.date_time']), + 'interface' => new PropertyMetadata('interface', Type::string(), [], ['json_encoder.value_transformer.string_to_date_time']), + 'immutable' => new PropertyMetadata('immutable', Type::string(), [], ['json_encoder.value_transformer.string_to_date_time']), 'other' => new PropertyMetadata('other', Type::object(self::class)), ], $loader->load(self::class)); } + public function testThrowWhenDateTimeType() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The "DateTime" class is not supported. Use "DateTimeImmutable" instead.'); + + $loader = new DateTimeTypePropertyMetadataLoader(self::propertyMetadataLoader([ + 'mutable' => new PropertyMetadata('mutable', Type::object(\DateTime::class)), + ])); + + $loader->load(self::class); + } + /** * @param array $propertiesMetadata */ diff --git a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/AttributePropertyMetadataLoaderTest.php b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/AttributePropertyMetadataLoaderTest.php index 0567d7456a296..db9c96cd11535 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/AttributePropertyMetadataLoaderTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/AttributePropertyMetadataLoaderTest.php @@ -12,16 +12,16 @@ namespace Symfony\Component\JsonEncoder\Tests\Mapping\Encode; use PHPUnit\Framework\TestCase; -use Symfony\Component\JsonEncoder\Encode\Normalizer\NormalizerInterface; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\JsonEncoder\Mapping\Encode\AttributePropertyMetadataLoader; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadata; use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoader; use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNameAttributes; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithNormalizerAttributes; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\BooleanStringNormalizer; -use Symfony\Component\JsonEncoder\Tests\Fixtures\Normalizer\DoubleIntAndCastToStringNormalizer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\Model\DummyWithValueTransformerAttributes; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\BooleanToStringValueTransformer; +use Symfony\Component\JsonEncoder\Tests\Fixtures\ValueTransformer\DoubleIntAndCastToStringValueTransformer; use Symfony\Component\JsonEncoder\Tests\ServiceContainer; +use Symfony\Component\JsonEncoder\ValueTransformer\ValueTransformerInterface; use Symfony\Component\TypeInfo\Type; use Symfony\Component\TypeInfo\TypeResolver\TypeResolver; @@ -34,41 +34,41 @@ public function testRetrieveEncodedName() $this->assertSame(['@id', 'name'], array_keys($loader->load(DummyWithNameAttributes::class))); } - public function testRetrieveNormalizer() + public function testRetrieveValueTransformer() { $loader = new AttributePropertyMetadataLoader(new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer([ - DoubleIntAndCastToStringNormalizer::class => new DoubleIntAndCastToStringNormalizer(), - BooleanStringNormalizer::class => new BooleanStringNormalizer(), + DoubleIntAndCastToStringValueTransformer::class => new DoubleIntAndCastToStringValueTransformer(), + BooleanToStringValueTransformer::class => new BooleanToStringValueTransformer(), ]), TypeResolver::create()); $this->assertEquals([ - 'id' => new PropertyMetadata('id', Type::string(), [DoubleIntAndCastToStringNormalizer::class]), - 'active' => new PropertyMetadata('active', Type::string(), [BooleanStringNormalizer::class]), + 'id' => new PropertyMetadata('id', Type::string(), [DoubleIntAndCastToStringValueTransformer::class]), + 'active' => new PropertyMetadata('active', Type::string(), [BooleanToStringValueTransformer::class]), 'name' => new PropertyMetadata('name', Type::string(), [\Closure::fromCallable('strtolower')]), - 'range' => new PropertyMetadata('range', Type::string(), [\Closure::fromCallable(DummyWithNormalizerAttributes::concatRange(...))]), - ], $loader->load(DummyWithNormalizerAttributes::class)); + 'range' => new PropertyMetadata('range', Type::string(), [\Closure::fromCallable(DummyWithValueTransformerAttributes::concatRange(...))]), + ], $loader->load(DummyWithValueTransformerAttributes::class)); } - public function testThrowWhenCannotRetrieveNormalizer() + public function testThrowWhenCannotRetrieveValueTransformer() { $loader = new AttributePropertyMetadataLoader(new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer(), TypeResolver::create()); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage(\sprintf('You have requested a non-existent normalizer service "%s". Did you implement "%s"?', DoubleIntAndCastToStringNormalizer::class, NormalizerInterface::class)); + $this->expectExceptionMessage(\sprintf('You have requested a non-existent value transformer service "%s". Did you implement "%s"?', DoubleIntAndCastToStringValueTransformer::class, ValueTransformerInterface::class)); - $loader->load(DummyWithNormalizerAttributes::class); + $loader->load(DummyWithValueTransformerAttributes::class); } - public function testThrowWhenInvalidNormalizer() + public function testThrowWhenInvalidValueTransformer() { $loader = new AttributePropertyMetadataLoader(new PropertyMetadataLoader(TypeResolver::create()), new ServiceContainer([ - DoubleIntAndCastToStringNormalizer::class => true, - BooleanStringNormalizer::class => new BooleanStringNormalizer(), + DoubleIntAndCastToStringValueTransformer::class => true, + BooleanToStringValueTransformer::class => new BooleanToStringValueTransformer(), ]), TypeResolver::create()); $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage(\sprintf('The "%s" normalizer service does not implement "%s".', DoubleIntAndCastToStringNormalizer::class, NormalizerInterface::class)); + $this->expectExceptionMessage(\sprintf('The "%s" value transformer service does not implement "%s".', DoubleIntAndCastToStringValueTransformer::class, ValueTransformerInterface::class)); - $loader->load(DummyWithNormalizerAttributes::class); + $loader->load(DummyWithValueTransformerAttributes::class); } } diff --git a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/DateTimeTypePropertyMetadataLoaderTest.php b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/DateTimeTypePropertyMetadataLoaderTest.php index 580f48f11ee26..6f3078679671c 100644 --- a/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/DateTimeTypePropertyMetadataLoaderTest.php +++ b/src/Symfony/Component/JsonEncoder/Tests/Mapping/Encode/DateTimeTypePropertyMetadataLoaderTest.php @@ -19,7 +19,7 @@ class DateTimeTypePropertyMetadataLoaderTest extends TestCase { - public function testAddDateTimeNormalizer() + public function testAddDateTimeToStringValueTransformer() { $loader = new DateTimeTypePropertyMetadataLoader(self::propertyMetadataLoader([ 'dateTime' => new PropertyMetadata('dateTime', Type::object(\DateTimeImmutable::class)), @@ -27,7 +27,7 @@ public function testAddDateTimeNormalizer() ])); $this->assertEquals([ - 'dateTime' => new PropertyMetadata('dateTime', Type::string(), ['json_encoder.normalizer.date_time']), + 'dateTime' => new PropertyMetadata('dateTime', Type::string(), ['json_encoder.value_transformer.date_time_to_string']), 'other' => new PropertyMetadata('other', Type::object(self::class)), ], $loader->load(self::class)); } diff --git a/src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/DateTimeToStringValueTransformerTest.php b/src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/DateTimeToStringValueTransformerTest.php new file mode 100644 index 0000000000000..cc0a501cf4871 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/DateTimeToStringValueTransformerTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonEncoder\Tests\ValueTransformer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; +use Symfony\Component\JsonEncoder\ValueTransformer\DateTimeToStringValueTransformer; + +class DateTimeToStringValueTransformerTest extends TestCase +{ + public function testTransform() + { + $valueTransformer = new DateTimeToStringValueTransformer(); + + $this->assertSame( + '2023-07-26T00:00:00+00:00', + $valueTransformer->transform(new \DateTimeImmutable('2023-07-26', new \DateTimeZone('UTC')), []), + ); + + $this->assertSame( + '26/07/2023 00:00:00', + $valueTransformer->transform((new \DateTimeImmutable('2023-07-26', new \DateTimeZone('UTC')))->setTime(0, 0), [DateTimeToStringValueTransformer::FORMAT_KEY => 'd/m/Y H:i:s']), + ); + } + + public function testTransformThrowWhenInvalidNativeValue() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The native value must implement the "\DateTimeInterface".'); + + (new DateTimeToStringValueTransformer())->transform(true, []); + } +} diff --git a/src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/StringToDateTimeValueTransformerTest.php b/src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/StringToDateTimeValueTransformerTest.php new file mode 100644 index 0000000000000..3340adb8fc3b3 --- /dev/null +++ b/src/Symfony/Component/JsonEncoder/Tests/ValueTransformer/StringToDateTimeValueTransformerTest.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\JsonEncoder\Tests\ValueTransformer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; +use Symfony\Component\JsonEncoder\ValueTransformer\StringToDateTimeValueTransformer; + +class StringToDateTimeValueTransformerTest extends TestCase +{ + public function testTransform() + { + $valueTransformer = new StringToDateTimeValueTransformer(); + + $this->assertEquals( + new \DateTimeImmutable('2023-07-26'), + $valueTransformer->transform('2023-07-26', []), + ); + + $this->assertEquals( + (new \DateTimeImmutable('2023-07-26'))->setTime(0, 0), + $valueTransformer->transform('26/07/2023 00:00:00', [StringToDateTimeValueTransformer::FORMAT_KEY => 'd/m/Y H:i:s']), + ); + } + + public function testTransformThrowWhenInvalidJsonValue() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('The JSON value is either not an string, or an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); + + (new StringToDateTimeValueTransformer())->transform(true, []); + } + + public function testTransformThrowWhenInvalidDateTimeString() + { + $valueTransformer = new StringToDateTimeValueTransformer(); + + try { + $valueTransformer->transform('0', []); + $this->fail(\sprintf('A "%s" exception must have been thrown.', InvalidArgumentException::class)); + } catch (InvalidArgumentException $e) { + $this->assertEquals("Parsing datetime string \"0\" resulted in 1 errors: \nat position 0: Unexpected character", $e->getMessage()); + } + + try { + $valueTransformer->transform('0', [StringToDateTimeValueTransformer::FORMAT_KEY => 'Y-m-d']); + $this->fail(\sprintf('A "%s" exception must have been thrown.', InvalidArgumentException::class)); + } catch (InvalidArgumentException $e) { + $this->assertEquals("Parsing datetime string \"0\" using format \"Y-m-d\" resulted in 1 errors: \nat position 1: Not enough data available to satisfy format", $e->getMessage()); + } + } +} diff --git a/src/Symfony/Component/JsonEncoder/Encode/Normalizer/DateTimeNormalizer.php b/src/Symfony/Component/JsonEncoder/ValueTransformer/DateTimeToStringValueTransformer.php similarity index 53% rename from src/Symfony/Component/JsonEncoder/Encode/Normalizer/DateTimeNormalizer.php rename to src/Symfony/Component/JsonEncoder/ValueTransformer/DateTimeToStringValueTransformer.php index 35aca2d95951a..36e577d8f5cd2 100644 --- a/src/Symfony/Component/JsonEncoder/Encode/Normalizer/DateTimeNormalizer.php +++ b/src/Symfony/Component/JsonEncoder/ValueTransformer/DateTimeToStringValueTransformer.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\JsonEncoder\Encode\Normalizer; +namespace Symfony\Component\JsonEncoder\ValueTransformer; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\TypeInfo\Type; @@ -17,29 +17,29 @@ use Symfony\Component\TypeInfo\TypeIdentifier; /** - * Casts DateTimeInterface to string. + * Transforms DateTimeInterface to string during encoding. * * @author Mathias Arlaud * - * @internal + * @experimental */ -final class DateTimeNormalizer implements NormalizerInterface +final class DateTimeToStringValueTransformer implements ValueTransformerInterface { public const FORMAT_KEY = 'date_time_format'; - public function normalize(mixed $denormalized, array $options = []): string + public function transform(mixed $value, array $options = []): string { - if (!$denormalized instanceof \DateTimeInterface) { - throw new InvalidArgumentException('The denormalized data must implement the "\DateTimeInterface".'); + if (!$value instanceof \DateTimeInterface) { + throw new InvalidArgumentException('The native value must implement the "\DateTimeInterface".'); } - return $denormalized->format($options[self::FORMAT_KEY] ?? \DateTimeInterface::RFC3339); + return $value->format($options[self::FORMAT_KEY] ?? \DateTimeInterface::RFC3339); } /** * @return BuiltinType */ - public static function getNormalizedType(): BuiltinType + public static function getJsonValueType(): BuiltinType { return Type::string(); } diff --git a/src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DateTimeDenormalizer.php b/src/Symfony/Component/JsonEncoder/ValueTransformer/StringToDateTimeValueTransformer.php similarity index 51% rename from src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DateTimeDenormalizer.php rename to src/Symfony/Component/JsonEncoder/ValueTransformer/StringToDateTimeValueTransformer.php index 90c335c1b8237..502f9b03e9269 100644 --- a/src/Symfony/Component/JsonEncoder/Decode/Denormalizer/DateTimeDenormalizer.php +++ b/src/Symfony/Component/JsonEncoder/ValueTransformer/StringToDateTimeValueTransformer.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\JsonEncoder\Decode\Denormalizer; +namespace Symfony\Component\JsonEncoder\ValueTransformer; use Symfony\Component\JsonEncoder\Exception\InvalidArgumentException; use Symfony\Component\TypeInfo\Type; @@ -17,53 +17,47 @@ use Symfony\Component\TypeInfo\TypeIdentifier; /** - * Casts string to DateTimeInterface. + * Transforms string to DateTimeImmutable during decoding. * * @author Mathias Arlaud * - * @internal + * @experimental */ -final class DateTimeDenormalizer implements DenormalizerInterface +final class StringToDateTimeValueTransformer implements ValueTransformerInterface { public const FORMAT_KEY = 'date_time_format'; - public function __construct( - private bool $immutable, - ) { - } - - public function denormalize(mixed $normalized, array $options = []): \DateTime|\DateTimeImmutable + public function transform(mixed $value, array $options = []): \DateTimeImmutable { - if (!\is_string($normalized) || '' === trim($normalized)) { - throw new InvalidArgumentException('The normalized data is either not an string, or an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); + if (!\is_string($value) || '' === trim($value)) { + throw new InvalidArgumentException('The JSON value is either not an string, or an empty string, or null; you should pass a string that can be parsed with the passed format or a valid DateTime string.'); } $dateTimeFormat = $options[self::FORMAT_KEY] ?? null; - $dateTimeClassName = $this->immutable ? \DateTimeImmutable::class : \DateTime::class; if (null !== $dateTimeFormat) { - if (false !== $dateTime = $dateTimeClassName::createFromFormat($dateTimeFormat, $normalized)) { + if (false !== $dateTime = \DateTimeImmutable::createFromFormat($dateTimeFormat, $value)) { return $dateTime; } - $dateTimeErrors = $dateTimeClassName::getLastErrors(); + $dateTimeErrors = \DateTimeImmutable::getLastErrors(); - throw new InvalidArgumentException(\sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors: ', $normalized, $dateTimeFormat, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors']))); + throw new InvalidArgumentException(\sprintf('Parsing datetime string "%s" using format "%s" resulted in %d errors: ', $value, $dateTimeFormat, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors']))); } try { - return new $dateTimeClassName($normalized); + return new \DateTimeImmutable($value); } catch (\Throwable) { - $dateTimeErrors = $dateTimeClassName::getLastErrors(); + $dateTimeErrors = \DateTimeImmutable::getLastErrors(); - throw new InvalidArgumentException(\sprintf('Parsing datetime string "%s" resulted in %d errors: ', $normalized, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors']))); + throw new InvalidArgumentException(\sprintf('Parsing datetime string "%s" resulted in %d errors: ', $value, $dateTimeErrors['error_count'])."\n".implode("\n", $this->formatDateTimeErrors($dateTimeErrors['errors']))); } } /** * @return BuiltinType */ - public static function getNormalizedType(): BuiltinType + public static function getJsonValueType(): BuiltinType { return Type::string(); } diff --git a/src/Symfony/Component/JsonEncoder/Encode/Normalizer/NormalizerInterface.php b/src/Symfony/Component/JsonEncoder/ValueTransformer/ValueTransformerInterface.php similarity index 55% rename from src/Symfony/Component/JsonEncoder/Encode/Normalizer/NormalizerInterface.php rename to src/Symfony/Component/JsonEncoder/ValueTransformer/ValueTransformerInterface.php index 49c4b25a5811a..8aed47f0eab4a 100644 --- a/src/Symfony/Component/JsonEncoder/Encode/Normalizer/NormalizerInterface.php +++ b/src/Symfony/Component/JsonEncoder/ValueTransformer/ValueTransformerInterface.php @@ -9,23 +9,24 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\JsonEncoder\Encode\Normalizer; +namespace Symfony\Component\JsonEncoder\ValueTransformer; use Symfony\Component\TypeInfo\Type; /** - * Normalizes data during the encoding process. + * Transforms a native value so it's ready to be JSON encoded during encoding + * and to other way around during decoding. * * @author Mathias Arlaud * * @experimental */ -interface NormalizerInterface +interface ValueTransformerInterface { /** * @param array $options */ - public function normalize(mixed $denormalized, array $options = []): mixed; + public function transform(mixed $value, array $options = []): mixed; - public static function getNormalizedType(): Type; + public static function getJsonValueType(): Type; }