Skip to content

Commit

Permalink
fix: fix property normalization order in classes with inheritance
Browse files Browse the repository at this point in the history
  • Loading branch information
romm committed Dec 22, 2023
1 parent abba5eb commit eec221c
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 5 deletions.
29 changes: 29 additions & 0 deletions src/Normalizer/RecursiveNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
Expand Down
16 changes: 11 additions & 5 deletions tests/Integration/Normalizer/NormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
],
];

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit eec221c

Please sign in to comment.