Skip to content

Commit a95bb1e

Browse files
committed
Enhance ArrayShapeValidator to support tuple shapes and improve key handling
1 parent 7338608 commit a95bb1e

1 file changed

Lines changed: 24 additions & 14 deletions

File tree

src/Validator/ArrayShapeValidator.php

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44

55
namespace TypePHP\Validator;
66

7+
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
78
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
89
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
10+
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
911
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
1012
use TypePHP\Internal\ErrorFactory;
1113
use TypePHP\Internal\ErrorMessage;
1214
use TypePHP\Internal\TypeFormatter;
1315

1416
/**
15-
* @internal Class for validating array shapes like array<1:string,2:int>.
17+
* @internal Class for validating array shapes and tuple shapes like array{0: string, 1: int} or array{string, int}.
1618
*/
1719
final class ArrayShapeValidator implements TypeValidatorInterface
1820
{
@@ -24,30 +26,38 @@ public function validate(mixed $value, TypeNode $node, string $context, TypeVali
2426

2527
/** @var ArrayShapeNode $node */
2628
$knownKeys = [];
29+
$nextAutoIndex = 0;
2730

2831
foreach ($node->items as $item) {
2932
$key = null;
33+
3034
if ($item->keyName instanceof ConstExprStringNode) {
3135
$key = $item->keyName->value;
36+
} elseif ($item->keyName instanceof ConstExprIntegerNode) {
37+
$key = (int) $item->keyName->value;
38+
$nextAutoIndex = max($nextAutoIndex, $key + 1);
39+
} elseif ($item->keyName instanceof IdentifierTypeNode) {
40+
$key = $item->keyName->name;
3241
} elseif ($item->keyName !== null) {
3342
$key = (string) $item->keyName;
43+
} else {
44+
$key = $nextAutoIndex;
45+
$nextAutoIndex++;
3446
}
3547

36-
if ($key !== null) {
37-
$knownKeys[$key] = true;
38-
39-
if (! \array_key_exists($key, $value)) {
40-
if (! $item->optional) {
41-
return ErrorFactory::createError($context . " is missing required key '$key'");
42-
}
48+
$knownKeys[$key] = true;
4349

44-
continue;
50+
if (! \array_key_exists($key, $value)) {
51+
if (! $item->optional) {
52+
return ErrorFactory::createError($context . " is missing required key '$key'");
4553
}
4654

47-
$err = $registry->validate($value[$key], $item->valueType, $context . "['" . $key . "']");
48-
if ($err !== null) {
49-
return $err;
50-
}
55+
continue;
56+
}
57+
58+
$err = $registry->validate($value[$key], $item->valueType, $context . "['" . $key . "']");
59+
if ($err !== null) {
60+
return $err;
5161
}
5262
}
5363

@@ -81,4 +91,4 @@ public function validate(mixed $value, TypeNode $node, string $context, TypeVali
8191

8292
return null;
8393
}
84-
}
94+
}

0 commit comments

Comments
 (0)