From eec221c361b7f8f9d7628ddc873f9cc2c352c2f4 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Fri, 22 Dec 2023 21:53:14 +0100 Subject: [PATCH] fix: fix property normalization order in classes with inheritance --- src/Normalizer/RecursiveNormalizer.php | 29 +++++++++++++++++++ .../Integration/Normalizer/NormalizerTest.php | 16 ++++++---- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/Normalizer/RecursiveNormalizer.php b/src/Normalizer/RecursiveNormalizer.php index a0877674..b7166859 100644 --- a/src/Normalizer/RecursiveNormalizer.php +++ b/src/Normalizer/RecursiveNormalizer.php @@ -16,11 +16,14 @@ use CuyZ\Valinor\Type\Types\NativeClassType; use DateTimeInterface; use Generator; +use ReflectionClass; use stdClass; use UnitEnum; use WeakMap; use function array_map; +use function array_reverse; +use function get_object_vars; /** @internal */ final class RecursiveNormalizer @@ -120,6 +123,32 @@ private function defaultTransformer(mixed $value, Formatter $formatter, WeakMap } $values = (fn () => get_object_vars($this))->call($value); + + // @infection-ignore-all + if (PHP_VERSION_ID < 8_01_00) { + // In PHP 8.1, behavior changed for `get_object_vars` function: + // the sorting order was taking children properties first, now + // it takes parents properties first. This code is a temporary + // workaround to keep the same behavior in PHP 8.0 and later + // versions. + $sorted = []; + + $parents = array_reverse(class_parents($value)); + $parents[] = $value::class; + + foreach ($parents as $parent) { + foreach ((new ReflectionClass($parent))->getProperties() as $property) { + if (! isset($values[$property->name])) { + continue; + } + + $sorted[$property->name] = $values[$property->name]; + } + } + + $values = $sorted; + } + $transformed = []; $class = $this->classDefinitionRepository->for(NativeClassType::for($value::class)); diff --git a/tests/Integration/Normalizer/NormalizerTest.php b/tests/Integration/Normalizer/NormalizerTest.php index 101e9163..6ce8a820 100644 --- a/tests/Integration/Normalizer/NormalizerTest.php +++ b/tests/Integration/Normalizer/NormalizerTest.php @@ -247,8 +247,9 @@ public function normalize_basic_values_yields_expected_output_data_provider(): i yield 'class with inherited properties' => [ 'input' => new SomeChildClass(), 'output' => [ - 'stringFromParentClass' => 'foo', - 'stringFromChildClass' => 'bar', + 'stringFromGrandParentClass' => 'foo', + 'stringFromParentClass' => 'bar', + 'stringFromChildClass' => 'baz', ], ]; @@ -743,14 +744,19 @@ final class BasicObject public function __construct(public string $value) {} } -class SomeParentClass +class SomeGrandParentClass { - public string $stringFromParentClass = 'foo'; + public string $stringFromGrandParentClass = 'foo'; +} + +class SomeParentClass extends SomeGrandParentClass +{ + public string $stringFromParentClass = 'bar'; } final class SomeChildClass extends SomeParentClass { - public string $stringFromChildClass = 'bar'; + public string $stringFromChildClass = 'baz'; } final class ObjectWithCircularReferenceA