Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 74dfd0a

Browse files
Add support for array<> syntax
1 parent 887757e commit 74dfd0a

File tree

2 files changed

+119
-17
lines changed

2 files changed

+119
-17
lines changed

src/Symfony/Component/OptionsResolver/OptionsResolver.php

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,7 @@ public function offsetGet(mixed $option, bool $triggerDeprecation = true): mixed
11421142
private function verifyTypes(string $type, mixed $value, ?array &$invalidTypes = null, int $level = 0): bool
11431143
{
11441144
$type = trim($type);
1145-
$allowedTypes = $this->splitOutsideParenthesis($type);
1145+
$allowedTypes = $this->splitTypes($type);
11461146
if (\count($allowedTypes) > 1) {
11471147
foreach ($allowedTypes as $allowedType) {
11481148
if ($this->verifyTypes($allowedType, $value)) {
@@ -1162,17 +1162,29 @@ private function verifyTypes(string $type, mixed $value, ?array &$invalidTypes =
11621162
return $this->verifyTypes(substr($type, 1, -1), $value, $invalidTypes, $level);
11631163
}
11641164

1165-
if (\is_array($value) && str_ends_with($type, '[]')) {
1166-
$type = substr($type, 0, -2);
1167-
$valid = true;
1165+
if (\is_array($value)) {
1166+
if (str_starts_with($type, 'array<') && str_ends_with($type, '>')) {
1167+
$types = $this->splitTypes(substr($type, 6, -1), ',');
1168+
$allowedValueType = $types[1] ?? $types[0];
1169+
$allowedKeyType = isset($types[1]) ? $types[0] : null;
1170+
} elseif (str_ends_with($type, '[]')) {
1171+
$allowedValueType = substr($type, 0, -2);
1172+
}
1173+
1174+
if (isset($allowedValueType)) {
1175+
$valid = true;
1176+
foreach ($value as $key => $val) {
1177+
if (isset($allowedKeyType) && !$this->verifyTypes($allowedKeyType, $key, $invalidTypes, $level + 1)) {
1178+
$valid = false;
1179+
}
11681180

1169-
foreach ($value as $val) {
1170-
if (!$this->verifyTypes($type, $val, $invalidTypes, $level + 1)) {
1171-
$valid = false;
1181+
if (!$this->verifyTypes($allowedValueType, $val, $invalidTypes, $level + 1)) {
1182+
$valid = false;
1183+
}
11721184
}
1173-
}
11741185

1175-
return $valid;
1186+
return $valid;
1187+
}
11761188
}
11771189

11781190
if (('null' === $type && null === $value) || (isset(self::VALIDATION_FUNCTIONS[$type]) ? self::VALIDATION_FUNCTIONS[$type]($value) : $value instanceof $type)) {
@@ -1189,27 +1201,31 @@ private function verifyTypes(string $type, mixed $value, ?array &$invalidTypes =
11891201
/**
11901202
* @return list<string>
11911203
*/
1192-
private function splitOutsideParenthesis(string $type): array
1204+
private function splitTypes(string $type, string $separator = '|'): array
11931205
{
11941206
$parts = [];
11951207
$currentPart = '';
11961208
$parenthesisLevel = 0;
1209+
$arrayLevel = 0;
11971210

11981211
$typeLength = \strlen($type);
11991212
for ($i = 0; $i < $typeLength; ++$i) {
12001213
$char = $type[$i];
1214+
if ($separator === $char && 0 === $parenthesisLevel && 0 === $arrayLevel) {
1215+
$parts[] = $currentPart;
1216+
$currentPart = '';
1217+
continue;
1218+
}
12011219

1220+
$currentPart .= $char;
12021221
if ('(' === $char) {
12031222
++$parenthesisLevel;
12041223
} elseif (')' === $char) {
12051224
--$parenthesisLevel;
1206-
}
1207-
1208-
if ('|' === $char && 0 === $parenthesisLevel) {
1209-
$parts[] = $currentPart;
1210-
$currentPart = '';
1211-
} else {
1212-
$currentPart .= $char;
1225+
} elseif ('<' === $char) {
1226+
++$arrayLevel;
1227+
} elseif ('>' === $char) {
1228+
--$arrayLevel;
12131229
}
12141230
}
12151231

src/Symfony/Component/OptionsResolver/Tests/OptionsResolverTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,18 @@ public function testResolveTypedWithUnionOfArray()
828828
$this->assertSame(['foo' => [1, true]], $options);
829829
}
830830

831+
public function testResolveTypedWithUnionOfArray2()
832+
{
833+
$this->resolver->setDefined('foo');
834+
$this->resolver->setAllowedTypes('foo', 'array<string|int>|array<bool|int>');
835+
836+
$options = $this->resolver->resolve(['foo' => [1, '1']]);
837+
$this->assertSame(['foo' => [1, '1']], $options);
838+
839+
$options = $this->resolver->resolve(['foo' => [1, true]]);
840+
$this->assertSame(['foo' => [1, true]], $options);
841+
}
842+
831843
public function testResolveTypedArray()
832844
{
833845
$this->resolver->setDefined('foo');
@@ -837,6 +849,33 @@ public function testResolveTypedArray()
837849
$this->assertSame(['foo' => ['bar', 'baz']], $options);
838850
}
839851

852+
public function testResolveTypedArray2()
853+
{
854+
$this->resolver->setDefined('foo');
855+
$this->resolver->setAllowedTypes('foo', 'array<string>');
856+
$options = $this->resolver->resolve(['foo' => ['bar', 'baz']]);
857+
858+
$this->assertSame(['foo' => ['bar', 'baz']], $options);
859+
}
860+
861+
public function testResolveTypedArrayWithKeys()
862+
{
863+
$this->resolver->setDefined('foo');
864+
$this->resolver->setAllowedTypes('foo', 'array<int, string>');
865+
$options = $this->resolver->resolve(['foo' => ['bar', 'baz']]);
866+
867+
$this->assertSame(['foo' => ['bar', 'baz']], $options);
868+
}
869+
870+
public function testResolveTypedArrayWithInvalidKeys()
871+
{
872+
$this->resolver->setDefined('foo');
873+
$this->resolver->setAllowedTypes('foo', 'array<string, string>');
874+
875+
$this->expectException(InvalidOptionsException::class);
876+
$this->resolver->resolve(['foo' => ['bar', 'baz']]);
877+
}
878+
840879
public function testResolveTypedArrayWithUnion()
841880
{
842881
$this->resolver->setDefined('foo');
@@ -846,6 +885,15 @@ public function testResolveTypedArrayWithUnion()
846885
$this->assertSame(['foo' => ['bar', 1]], $options);
847886
}
848887

888+
public function testResolveTypedArrayWithUnion2()
889+
{
890+
$this->resolver->setDefined('foo');
891+
$this->resolver->setAllowedTypes('foo', 'array<string|int>');
892+
$options = $this->resolver->resolve(['foo' => ['bar', 1]]);
893+
894+
$this->assertSame(['foo' => ['bar', 1]], $options);
895+
}
896+
849897
public function testFailIfSetAllowedTypesFromLazyOption()
850898
{
851899
$this->expectException(AccessException::class);
@@ -1963,6 +2011,24 @@ public function testNestedArrays()
19632011
]));
19642012
}
19652013

2014+
public function testNestedArrays2()
2015+
{
2016+
$this->resolver->setDefined('foo');
2017+
$this->resolver->setAllowedTypes('foo', 'array<array<int>>');
2018+
2019+
$this->assertEquals([
2020+
'foo' => [
2021+
[
2022+
1, 2,
2023+
],
2024+
],
2025+
], $this->resolver->resolve([
2026+
'foo' => [
2027+
[1, 2],
2028+
],
2029+
]));
2030+
}
2031+
19662032
public function testNestedArraysWithUnions()
19672033
{
19682034
$this->resolver->setDefined('foo');
@@ -1983,6 +2049,26 @@ public function testNestedArraysWithUnions()
19832049
]));
19842050
}
19852051

2052+
public function testNestedArraysWithUnions2()
2053+
{
2054+
$this->resolver->setDefined('foo');
2055+
$this->resolver->setAllowedTypes('foo', 'array<int|float|array<int|float>>');
2056+
2057+
$this->assertEquals([
2058+
'foo' => [
2059+
1,
2060+
2.0,
2061+
[1, 2.0],
2062+
],
2063+
], $this->resolver->resolve([
2064+
'foo' => [
2065+
1,
2066+
2.0,
2067+
[1, 2.0],
2068+
],
2069+
]));
2070+
}
2071+
19862072
public function testNested2Arrays()
19872073
{
19882074
$this->resolver->setDefined('foo');

0 commit comments

Comments
 (0)