From 5ba87a6e61a717334bc84ddd643b14469f0fc629 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Tue, 5 Mar 2024 19:58:12 +0100 Subject: [PATCH] Failing test: constructors with input `array{key: T}` and `array{key: list}` considered colliding In this test, Valinor considers two disjoint input types as colliding. Similar to https://github.com/CuyZ/Valinor/pull/487 ( ffe0f0f21290ff6469da5c723172b9afc6aeba7d ), this scenario was detected while trying to map multiple constructors for XML structures that may present different data depending on singular/plural entries found: ```xml 123 456 789 ``` In #487, we attempted to map a single constructor using `array{foo: T|list}`, while in this patch, we found the issue because we attempted to attack the problem by declaring separate constructors that would work on `array{foo: T}` and `array{foo: list}` disjointly, but failed to do so due to aggressive collision detection logic. Initially discovered by @Tigerman55 --- .../ConstructorRegistrationMappingTest.php | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php b/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php index 9282923f..402e745a 100644 --- a/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php +++ b/tests/Integration/Mapping/ConstructorRegistrationMappingTest.php @@ -633,6 +633,33 @@ public function test_identical_registered_constructors_with_several_argument_thr ->map(stdClass::class, []); } + public function test_non_intersecting_hashmap_type_constructors_do_not_lead_to_collisions(): void + { + $mapper = $this->mapperBuilder() + ->registerConstructor( + /** @param array{key: SimpleObject} $input */ + static fn (array $input): stdClass => (object)['single-item' => $input], + /** @param array{key: list} $input */ + static fn (array $input): stdClass => (object)['multiple-items' => $input], + ) + ->mapper(); + + $hello = new SimpleObject(); + $world = new SimpleObject(); + + $hello->value = 'hello'; + $world->value = 'world'; + + try { + self::assertEquals( + (object) ['multiple-items' => ['key' => [$hello, $world]]], + $mapper->map(stdClass::class, ['key' => ['hello', 'world']]) + ); + } catch (MappingError $error) { + $this->mappingFail($error); + } + } + public function test_source_not_matching_registered_constructors_throws_exception(): void { try {