Skip to content

Commit ab51bdb

Browse files
committed
Merge branch '7.4' into 8.0
* 7.4: [OptionsResolver] Optimize splitOutsideParenthesis() - 5.9x faster Use unique identifier for RequestContextProvider [DependencyInjection] Update `ResolveClassPass` to check class existence [Validator] Add Japanese translation for Twig template validator fix doc url [FrameworkBundle] Fix `lint:container --resolve-env-vars`
2 parents d1ecd82 + 40e2961 commit ab51bdb

File tree

2 files changed

+162
-27
lines changed

2 files changed

+162
-27
lines changed

OptionsResolver.php

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,33 +1182,29 @@ private function verifyTypes(string $type, mixed $value, ?array &$invalidTypes =
11821182
*/
11831183
private function splitOutsideParenthesis(string $type): array
11841184
{
1185-
$parts = [];
1186-
$currentPart = '';
1187-
$parenthesisLevel = 0;
1188-
1189-
$typeLength = \strlen($type);
1190-
for ($i = 0; $i < $typeLength; ++$i) {
1191-
$char = $type[$i];
1192-
1193-
if ('(' === $char) {
1194-
++$parenthesisLevel;
1195-
} elseif (')' === $char) {
1196-
--$parenthesisLevel;
1197-
}
1198-
1199-
if ('|' === $char && 0 === $parenthesisLevel) {
1200-
$parts[] = $currentPart;
1201-
$currentPart = '';
1202-
} else {
1203-
$currentPart .= $char;
1204-
}
1205-
}
1206-
1207-
if ('' !== $currentPart) {
1208-
$parts[] = $currentPart;
1209-
}
1210-
1211-
return $parts;
1185+
return preg_split(<<<'EOF'
1186+
/
1187+
# Define a recursive subroutine for matching balanced parentheses
1188+
(?(DEFINE)
1189+
(?<balanced>
1190+
\( # Match an opening parenthesis
1191+
(?: # Start a non-capturing group for the contents
1192+
[^()] # Match any character that is not a parenthesis
1193+
| # OR
1194+
(?&balanced) # Recursively match a nested balanced group
1195+
)* # Repeat the group for all contents
1196+
\) # Match the final closing parenthesis
1197+
)
1198+
)
1199+
1200+
# Match any balanced parenthetical group, then skip it
1201+
(?&balanced)(*SKIP)(*FAIL) # Use the defined subroutine and discard the match
1202+
1203+
| # OR
1204+
1205+
\| # Match the pipe delimiter (only if not inside a skipped group)
1206+
/x
1207+
EOF, $type);
12121208
}
12131209

12141210
/**

Tests/OptionsResolverTest.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,145 @@ public function testNestedArraysException()
20012001
]);
20022002
}
20032003

2004+
/**
2005+
* @dataProvider provideValidDeeplyNestedUnionTypes
2006+
*/
2007+
public function testDeeplyNestedUnionTypes(string $type, $validValue)
2008+
{
2009+
$this->resolver->setDefined('option');
2010+
$this->resolver->setAllowedTypes('option', $type);
2011+
$this->assertEquals(['option' => $validValue], $this->resolver->resolve(['option' => $validValue]));
2012+
}
2013+
2014+
/**
2015+
* @dataProvider provideInvalidDeeplyNestedUnionTypes
2016+
*/
2017+
public function testDeeplyNestedUnionTypesException(string $type, $invalidValue, string $expectedExceptionMessage)
2018+
{
2019+
$this->resolver->setDefined('option');
2020+
$this->resolver->setAllowedTypes('option', $type);
2021+
2022+
$this->expectException(InvalidOptionsException::class);
2023+
$this->expectExceptionMessage($expectedExceptionMessage);
2024+
2025+
$this->resolver->resolve(['option' => $invalidValue]);
2026+
}
2027+
2028+
public function provideValidDeeplyNestedUnionTypes(): array
2029+
{
2030+
$resource = fopen('php://memory', 'r');
2031+
$object = new \stdClass();
2032+
2033+
return [
2034+
// Test 1 level of nesting
2035+
['string|(int|bool)', 'test'],
2036+
['string|(int|bool)', 42],
2037+
['string|(int|bool)', true],
2038+
2039+
// Test 2 levels of nesting
2040+
['string|(int|(bool|float))', 'test'],
2041+
['string|(int|(bool|float))', 42],
2042+
['string|(int|(bool|float))', true],
2043+
['string|(int|(bool|float))', 3.14],
2044+
2045+
// Test 3 levels of nesting
2046+
['string|(int|(bool|(float|null)))', 'test'],
2047+
['string|(int|(bool|(float|null)))', 42],
2048+
['string|(int|(bool|(float|null)))', true],
2049+
['string|(int|(bool|(float|null)))', 3.14],
2050+
['string|(int|(bool|(float|null)))', null],
2051+
2052+
// Test 4 levels of nesting
2053+
['string|(int|(bool|(float|(null|object))))', 'test'],
2054+
['string|(int|(bool|(float|(null|object))))', 42],
2055+
['string|(int|(bool|(float|(null|object))))', true],
2056+
['string|(int|(bool|(float|(null|object))))', 3.14],
2057+
['string|(int|(bool|(float|(null|object))))', null],
2058+
['string|(int|(bool|(float|(null|object))))', $object],
2059+
2060+
// Test complex case with multiple deep nesting
2061+
['(string|(int|bool))|(float|(null|object))', 'test'],
2062+
['(string|(int|bool))|(float|(null|object))', 42],
2063+
['(string|(int|bool))|(float|(null|object))', true],
2064+
['(string|(int|bool))|(float|(null|object))', 3.14],
2065+
['(string|(int|bool))|(float|(null|object))', null],
2066+
['(string|(int|bool))|(float|(null|object))', $object],
2067+
2068+
// Test nested at the beginning
2069+
['((string|int)|bool)|float', 'test'],
2070+
['((string|int)|bool)|float', 42],
2071+
['((string|int)|bool)|float', true],
2072+
['((string|int)|bool)|float', 3.14],
2073+
2074+
// Test multiple unions at different levels
2075+
['string|(int|(bool|float))|null|(object|(array|resource))', 'test'],
2076+
['string|(int|(bool|float))|null|(object|(array|resource))', 42],
2077+
['string|(int|(bool|float))|null|(object|(array|resource))', true],
2078+
['string|(int|(bool|float))|null|(object|(array|resource))', 3.14],
2079+
['string|(int|(bool|float))|null|(object|(array|resource))', null],
2080+
['string|(int|(bool|float))|null|(object|(array|resource))', $object],
2081+
['string|(int|(bool|float))|null|(object|(array|resource))', []],
2082+
['string|(int|(bool|float))|null|(object|(array|resource))', $resource],
2083+
2084+
// Test arrays with nested union types:
2085+
['(string|int)[]|(bool|float)[]', ['test', 42]],
2086+
['(string|int)[]|(bool|float)[]', [true, 3.14]],
2087+
2088+
// Test deeply nested arrays with unions
2089+
['((string|int)|(bool|float))[]', ['test', 42, true, 3.14]],
2090+
2091+
// Test complex nested array types
2092+
['(string|(int|bool)[])|(float|(null|object)[])', 'test'],
2093+
['(string|(int|bool)[])|(float|(null|object)[])', [42, true]],
2094+
['(string|(int|bool)[])|(float|(null|object)[])', 3.14],
2095+
['(string|(int|bool)[])|(float|(null|object)[])', [null, $object]],
2096+
2097+
// Test multi-dimensional arrays with nesting
2098+
['((string|int)[]|(bool|float)[])|null', ['test', 42]],
2099+
['((string|int)[]|(bool|float)[])|null', [true, 3.14]],
2100+
['((string|int)[]|(bool|float)[])|null', null],
2101+
];
2102+
}
2103+
2104+
public function provideInvalidDeeplyNestedUnionTypes(): array
2105+
{
2106+
$resource = fopen('php://memory', 'r');
2107+
$object = new \stdClass();
2108+
2109+
return [
2110+
// Test 1 level of nesting
2111+
['string|(int|bool)', [], 'The option "option" with value array is expected to be of type "string|(int|bool)", but is of type "array".'],
2112+
['string|(int|bool)', $object, 'The option "option" with value stdClass is expected to be of type "string|(int|bool)", but is of type "stdClass".'],
2113+
['string|(int|bool)', $resource, 'The option "option" with value resource is expected to be of type "string|(int|bool)", but is of type "resource (stream)".'],
2114+
['string|(int|bool)', null, 'The option "option" with value null is expected to be of type "string|(int|bool)", but is of type "null".'],
2115+
['string|(int|bool)', 3.14, 'The option "option" with value 3.14 is expected to be of type "string|(int|bool)", but is of type "float".'],
2116+
2117+
// Test 2 levels of nesting
2118+
['string|(int|(bool|float))', [], 'The option "option" with value array is expected to be of type "string|(int|(bool|float))", but is of type "array".'],
2119+
['string|(int|(bool|float))', $object, 'The option "option" with value stdClass is expected to be of type "string|(int|(bool|float))", but is of type "stdClass".'],
2120+
['string|(int|(bool|float))', $resource, 'The option "option" with value resource is expected to be of type "string|(int|(bool|float))", but is of type "resource (stream)".'],
2121+
['string|(int|(bool|float))', null, 'The option "option" with value null is expected to be of type "string|(int|(bool|float))", but is of type "null".'],
2122+
2123+
// Test 3 levels of nesting
2124+
['string|(int|(bool|(float|null)))', [], 'The option "option" with value array is expected to be of type "string|(int|(bool|(float|null)))", but is of type "array".'],
2125+
['string|(int|(bool|(float|null)))', $object, 'The option "option" with value stdClass is expected to be of type "string|(int|(bool|(float|null)))", but is of type "stdClass".'],
2126+
['string|(int|(bool|(float|null)))', $resource, 'The option "option" with value resource is expected to be of type "string|(int|(bool|(float|null)))", but is of type "resource (stream)".'],
2127+
2128+
// Test arrays with nested union types
2129+
['(string|int)[]|(bool|float)[]', ['test', true], 'The option "option" with value array is expected to be of type "(string|int)[]|(bool|float)[]", but one of the elements is of type "array".'],
2130+
['(string|int)[]|(bool|float)[]', [42, 3.14], 'The option "option" with value array is expected to be of type "(string|int)[]|(bool|float)[]", but one of the elements is of type "array".'],
2131+
2132+
// Test deeply nested arrays with unions
2133+
['((string|int)|(bool|float))[]', 'test', 'The option "option" with value "test" is expected to be of type "((string|int)|(bool|float))[]", but is of type "string".'],
2134+
['((string|int)|(bool|float))[]', [null], 'The option "option" with value array is expected to be of type "((string|int)|(bool|float))[]", but one of the elements is of type "null".'],
2135+
['((string|int)|(bool|float))[]', [$object], 'The option "option" with value array is expected to be of type "((string|int)|(bool|float))[]", but one of the elements is of type "stdClass".'],
2136+
2137+
// Test complex nested array types
2138+
['(string|(int|bool)[])|(float|(null|object)[])', ['test'], 'The option "option" with value array is expected to be of type "(string|(int|bool)[])|(float|(null|object)[])", but is of type "array".'],
2139+
['(string|(int|bool)[])|(float|(null|object)[])', [3.14], 'The option "option" with value array is expected to be of type "(string|(int|bool)[])|(float|(null|object)[])", but is of type "array".'],
2140+
];
2141+
}
2142+
20042143
public function testNestedArrayException1()
20052144
{
20062145
$this->expectException(InvalidOptionsException::class);

0 commit comments

Comments
 (0)