Skip to content

Commit

Permalink
fix: detect circular references linearly through objects
Browse files Browse the repository at this point in the history
The same object can now exist several times in an array, an iterable or
in several properties of a class.

The normalizer will not detect it as a circular reference anymore.
  • Loading branch information
romm committed Feb 2, 2024
1 parent 4b8cf65 commit 36aead9
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Normalizer/Transformer/RecursiveTransformer.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ private function doTransform(mixed $value, WeakMap $references, array $attribute
throw new CircularReferenceFoundDuringNormalization($value);
}

$references = clone $references;

// @infection-ignore-all
$references[$value] = true;
}
Expand Down
68 changes: 68 additions & 0 deletions tests/Integration/Normalizer/NormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,74 @@ public function test_no_priority_given_is_set_to_0(): void
self::assertSame('foo!?*', $result);
}

public function test_can_normalize_same_object_in_array_without_throwing_circular_reference_exception(): void
{
$objectA = new stdClass();
$objectA->foo = 'foo';

$objectB = new stdClass();
$objectB->bar = 'bar';

$result = (new MapperBuilder())
->normalizer(Format::array())
->normalize([$objectA, $objectB, $objectA]);

self::assertSame([['foo' => 'foo'], ['bar' => 'bar'], ['foo' => 'foo']], $result);
}

public function test_can_normalize_same_object_in_iterable_without_throwing_circular_reference_exception(): void
{
$iterable = (function () {
$objectA = new stdClass();
$objectA->foo = 'foo';

$objectB = new stdClass();
$objectB->bar = 'bar';

yield $objectA;
yield $objectB;
yield $objectA;
})();

$result = (new MapperBuilder())
->normalizer(Format::array())
->normalize($iterable);

self::assertSame([['foo' => 'foo'], ['bar' => 'bar'], ['foo' => 'foo']], $result);
}

public function test_can_normalize_same_object_in_properties_without_throwing_circular_reference_exception(): void
{
$object = new class () {
public stdClass $object1;
public stdClass $object2;
public stdClass $object3;

public function __construct()
{
$objectA = new stdClass();
$objectA->foo = 'foo';

$objectB = new stdClass();
$objectB->bar = 'bar';

$this->object1 = $objectA;
$this->object2 = $objectB;
$this->object3 = $objectA;
}
};

$result = (new MapperBuilder())
->normalizer(Format::array())
->normalize($object);

self::assertSame([
'object1' => ['foo' => 'foo'],
'object2' => ['bar' => 'bar'],
'object3' => ['foo' => 'foo']
], $result);
}

public function test_no_param_in_transformer_throws_exception(): void
{
$this->expectException(TransformerHasNoParameter::class);
Expand Down

0 comments on commit 36aead9

Please # to comment.