Skip to content

Commit

Permalink
Failing test: constructors with input array{key: T} and `array{key:…
Browse files Browse the repository at this point in the history
… list<T>}` considered colliding

In this test, Valinor considers two disjoint input types as colliding.

Similar to #487 ( ffe0f0f ), 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
<Invoices>
  <Invoice>
    <!-- in this example, `Item` is singular, and needs to be handled as `Item` -->
    <Item><Price>123</Price></Item>
  </Invoice>
  <Invoice>
    <!-- in this example, `Item` is plural, and needs to be handled as `list<Item>` -->
    <Item><Price>456</Price></Item>
    <Item><Price>789</Price></Item>
  </Invoice>
</Invoices>
```

In #487, we attempted to map a single constructor using `array{foo: T|list<T>}`, 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<T>}` disjointly,
but failed to do so due to aggressive collision detection logic.

Initially discovered by @Tigerman55
  • Loading branch information
Ocramius committed Mar 5, 2024
1 parent 4b68835 commit a7f3d58
Showing 1 changed file with 31 additions and 0 deletions.
31 changes: 31 additions & 0 deletions tests/Integration/Mapping/ConstructorRegistrationMappingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,37 @@ 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 function (array $input): stdClass {
return (object)['single-item' => $input];
},
/** @param array{key: list<SimpleObject>} $input */
static function (array $input): stdClass {
return (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 {
Expand Down

0 comments on commit a7f3d58

Please sign in to comment.