From e695b29f06720368e471e1101381aa8414556be2 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Wed, 18 Sep 2024 21:47:51 +0200 Subject: [PATCH] fix: prevent cache corruption when normalizing and mapping to enum --- .../Transformer/RecursiveTransformer.php | 9 ++++-- .../NormalizeEnumDoesNotBreakMapperTest.php | 30 +++++++++++++++++++ .../Integration/Normalizer/NormalizerTest.php | 26 ++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tests/Integration/NonRegression/NormalizeEnumDoesNotBreakMapperTest.php diff --git a/src/Normalizer/Transformer/RecursiveTransformer.php b/src/Normalizer/Transformer/RecursiveTransformer.php index 3b61b8e1..e140e468 100644 --- a/src/Normalizer/Transformer/RecursiveTransformer.php +++ b/src/Normalizer/Transformer/RecursiveTransformer.php @@ -12,6 +12,7 @@ use CuyZ\Valinor\Normalizer\AsTransformer; use CuyZ\Valinor\Normalizer\Exception\CircularReferenceFoundDuringNormalization; use CuyZ\Valinor\Normalizer\Exception\TypeUnhandledByNormalizer; +use CuyZ\Valinor\Type\Types\EnumType; use CuyZ\Valinor\Type\Types\NativeClassType; use DateTimeInterface; use DateTimeZone; @@ -63,10 +64,12 @@ private function doTransform(mixed $value, WeakMap $references, array $attribute // @infection-ignore-all $references[$value] = true; - } - if (is_object($value)) { - $classAttributes = $this->classDefinitionRepository->for(new NativeClassType($value::class))->attributes; + $type = $value instanceof UnitEnum + ? EnumType::native($value::class) + : new NativeClassType($value::class); + + $classAttributes = $this->classDefinitionRepository->for($type)->attributes; $classAttributes = $this->filterAttributes($classAttributes); $attributes = [...$attributes, ...$classAttributes]; diff --git a/tests/Integration/NonRegression/NormalizeEnumDoesNotBreakMapperTest.php b/tests/Integration/NonRegression/NormalizeEnumDoesNotBreakMapperTest.php new file mode 100644 index 00000000..efde7977 --- /dev/null +++ b/tests/Integration/NonRegression/NormalizeEnumDoesNotBreakMapperTest.php @@ -0,0 +1,30 @@ +mapperBuilder(); + + $mapperBuilder->normalizer(Format::array())->normalize(BackedStringEnum::FOO); + + $result = $mapperBuilder->mapper()->map(BackedStringEnum::class, 'foo'); + + self::assertSame(BackedStringEnum::FOO, $result); + } +} diff --git a/tests/Integration/Normalizer/NormalizerTest.php b/tests/Integration/Normalizer/NormalizerTest.php index 6599fd70..88e1aa95 100644 --- a/tests/Integration/Normalizer/NormalizerTest.php +++ b/tests/Integration/Normalizer/NormalizerTest.php @@ -276,6 +276,16 @@ public static function normalize_basic_values_yields_expected_output_data_provid 'expected json' => '42', ]; + yield 'enum with transformer attribute' => [ + 'input' => SomeEnumWithTransformerAttribute::FOO, + 'expected array' => 'normalizedValue-foo', + 'expected json' => '"normalizedValue-foo"', + 'transformers' => [], + 'transformerAttributes' => [ + TransformEnumToString::class, + ], + ]; + yield 'class with public properties' => [ 'input' => new class () { public string $string = 'foo'; @@ -1381,3 +1391,19 @@ public function __construct( #[TransformObjectToString] final class SomeClassWithAttributeToTransformObjectToString {} + +#[Attribute(Attribute::TARGET_CLASS)] +final class TransformEnumToString +{ + public function normalize(SomeEnumWithTransformerAttribute $enum): string + { + return 'normalizedValue-' . $enum->value; + } +} + +#[TransformEnumToString] +enum SomeEnumWithTransformerAttribute: string +{ + case FOO = 'foo'; + case BAR = 'bar'; +}