diff --git a/src/Type/Types/ShapedArrayType.php b/src/Type/Types/ShapedArrayType.php index 476f657f..435c08a6 100644 --- a/src/Type/Types/ShapedArrayType.php +++ b/src/Type/Types/ShapedArrayType.php @@ -9,7 +9,10 @@ use CuyZ\Valinor\Type\TraversableType; use CuyZ\Valinor\Type\Type; +use function array_diff; use function array_key_exists; +use function array_keys; +use function count; use function implode; use function in_array; use function is_array; @@ -45,9 +48,11 @@ public function accepts($value): bool return false; } + $keys = []; + foreach ($this->elements as $shape) { $type = $shape->type(); - $key = $shape->key()->value(); + $keys[] = $key = $shape->key()->value(); $valueExists = array_key_exists($key, $value); if (! $valueExists && ! $shape->isOptional()) { @@ -59,7 +64,9 @@ public function accepts($value): bool } } - return true; + $excess = array_diff(array_keys($value), $keys); + + return count($excess) === 0; } public function matches(Type $other): bool diff --git a/tests/Integration/Mapping/Type/ShapedArrayValuesMappingTest.php b/tests/Integration/Mapping/Type/ShapedArrayValuesMappingTest.php index a155c090..e5e3e8bf 100644 --- a/tests/Integration/Mapping/Type/ShapedArrayValuesMappingTest.php +++ b/tests/Integration/Mapping/Type/ShapedArrayValuesMappingTest.php @@ -15,6 +15,10 @@ final class ShapedArrayValuesMappingTest extends IntegrationTest public function test_values_are_mapped_properly(): void { $source = [ + 'basicShapedArrayWithExcessiveKey' => [ + 'foo' => 'foo', + 'bar' => 42, + ], 'basicShapedArrayWithStringKeys' => [ 'foo' => 'fiz', 'bar' => 42, @@ -47,6 +51,7 @@ public function test_values_are_mapped_properly(): void $this->mappingFail($error); } + self::assertSame(['foo' => 'foo'], $result->basicShapedArrayWithExcessiveKey); self::assertSame($source['basicShapedArrayWithStringKeys'], $result->basicShapedArrayWithStringKeys); self::assertSame($source['basicShapedArrayWithIntegerKeys'], $result->basicShapedArrayWithIntegerKeys); self::assertInstanceOf(SimpleObject::class, $result->shapedArrayWithObject['foo']); @@ -79,6 +84,9 @@ public function test_value_that_cannot_be_casted_throws_exception(): void class ShapedArrayValues { + /** @var array{foo: string} */ + public array $basicShapedArrayWithExcessiveKey; + /** @var array{foo: string, bar: int} */ public array $basicShapedArrayWithStringKeys; @@ -106,6 +114,7 @@ class ShapedArrayValues class ShapedArrayValuesWithConstructor extends ShapedArrayValues { /** + * @param array{foo: string} $basicShapedArrayWithExcessiveKey * @param array{foo: string, bar: int} $basicShapedArrayWithStringKeys * @param array{0: string, 1: float} $basicShapedArrayWithIntegerKeys * @param array{foo: SimpleObject} $shapedArrayWithObject @@ -117,6 +126,7 @@ class ShapedArrayValuesWithConstructor extends ShapedArrayValues * @param array{0: int, float, optionalString?: string, mandatoryString: string} $advancedShapedArray */ public function __construct( + array $basicShapedArrayWithExcessiveKey, array $basicShapedArrayWithStringKeys, array $basicShapedArrayWithIntegerKeys, array $shapedArrayWithObject, @@ -124,6 +134,7 @@ public function __construct( array $shapedArrayOnSeveralLines, array $advancedShapedArray ) { + $this->basicShapedArrayWithExcessiveKey = $basicShapedArrayWithExcessiveKey; $this->basicShapedArrayWithStringKeys = $basicShapedArrayWithStringKeys; $this->basicShapedArrayWithIntegerKeys = $basicShapedArrayWithIntegerKeys; $this->shapedArrayWithObject = $shapedArrayWithObject; diff --git a/tests/Unit/Type/Types/ShapedArrayTypeTest.php b/tests/Unit/Type/Types/ShapedArrayTypeTest.php index 89487776..2d4c7e22 100644 --- a/tests/Unit/Type/Types/ShapedArrayTypeTest.php +++ b/tests/Unit/Type/Types/ShapedArrayTypeTest.php @@ -69,6 +69,7 @@ public function test_accepts_correct_values(): void public function test_does_not_accept_incorrect_values(): void { + self::assertFalse($this->type->accepts(['foo' => 42])); self::assertFalse($this->type->accepts(['foo' => new stdClass()])); self::assertFalse($this->type->accepts(['bar' => 'foo'])); @@ -80,6 +81,11 @@ public function test_does_not_accept_incorrect_values(): void self::assertFalse($this->type->accepts(new stdClass())); } + public function test_does_not_accept_array_with_excessive_keys(): void + { + self::assertFalse($this->type->accepts(['foo' => 'foo', 'fiz' => 42])); + } + public function test_matches_valid_array_shaped_type(): void { $otherA = new ShapedArrayType(