[true, false, true], 'floats' => [42.404, 404.42], 'integers' => [42, 404, 1337], 'strings' => ['foo', 'bar', 'baz'], 'objects' => [ ['value' => 'foo'], ['value' => 'bar'], ['value' => 'baz'], ], 'objectsWithAlias' => [ ['value' => 'foo'], ['value' => 'bar'], ['value' => 'baz'], ], 'listOfStrings' => ['foo', 'bar', 'baz',], 'nonEmptyListOfStrings' => ['foo', 'bar', 'baz'], ]; foreach ([ListValues::class, ListValuesWithConstructor::class] as $class) { try { $result = (new MapperBuilder())->mapper()->map($class, $source); } catch (MappingError $error) { $this->mappingFail($error); } self::assertSame($source['booleans'], $result->booleans); self::assertSame($source['floats'], $result->floats); self::assertSame($source['integers'], $result->integers); self::assertSame($source['strings'], $result->strings); self::assertSame('foo', $result->objects[0]->value); self::assertSame('bar', $result->objects[1]->value); self::assertSame('baz', $result->objects[2]->value); self::assertSame('foo', $result->objectsWithAlias[0]->value); self::assertSame('bar', $result->objectsWithAlias[1]->value); self::assertSame('baz', $result->objectsWithAlias[2]->value); self::assertSame($source['listOfStrings'], $result->listOfStrings); self::assertSame($source['nonEmptyListOfStrings'], $result->nonEmptyListOfStrings); } } public function test_empty_list_in_non_empty_list_throws_exception(): void { try { (new MapperBuilder())->mapper()->map(ListValues::class, [ 'nonEmptyListOfStrings' => [], ]); } catch (MappingError $exception) { $error = $exception->node()->children()['nonEmptyListOfStrings']->messages()[0]; self::assertSame('1630678334', $error->code()); self::assertSame('Value array (empty) does not match type `non-empty-list`.', (string)$error); } } public function test_map_array_with_non_sequential_keys_to_list_throws_exception(): void { try { (new MapperBuilder())->mapper()->map('list', [ 0 => 'foo', 2 => 'bar', ]); } catch (MappingError $exception) { $error = $exception->node()->children()[2]->messages()[0]; self::assertSame('1654273010', $error->code()); self::assertSame('Invalid sequential key 2, expected 1.', (string)$error); } } public function test_value_with_invalid_type_throws_exception(): void { try { (new MapperBuilder())->mapper()->map(ListValues::class, [ 'integers' => ['foo'], ]); } catch (MappingError $exception) { $error = $exception->node()->children()['integers']->children()['0']->messages()[0]; self::assertSame('1655030601', $error->code()); self::assertSame("Value 'foo' does not match type `int`.", (string)$error); } } } class ListValues { /** @var list */ public array $booleans; /** @var list */ public array $floats; /** @var list */ public array $integers; /** @var list */ public array $strings; /** @var list */ public array $objects; /** @var list */ public array $objectsWithAlias; /** @var list */ public array $listOfStrings; /** @var non-empty-list */ public array $nonEmptyListOfStrings = ['foo']; } class ListValuesWithConstructor extends ListValues { /** * @param list $booleans * @param list $floats * @param list $integers * @param list $strings * @param list $objects * @param list $objectsWithAlias * @param list $listOfStrings * @param non-empty-list $nonEmptyListOfStrings */ public function __construct( array $booleans, array $floats, array $integers, array $strings, array $objects, array $objectsWithAlias, array $listOfStrings, array $nonEmptyListOfStrings ) { $this->booleans = $booleans; $this->floats = $floats; $this->integers = $integers; $this->strings = $strings; $this->objects = $objects; $this->objectsWithAlias = $objectsWithAlias; $this->listOfStrings = $listOfStrings; $this->nonEmptyListOfStrings = $nonEmptyListOfStrings; } }