1818use PHPStan \Type \Regex \RegexAlternation ;
1919use PHPStan \Type \Regex \RegexCapturingGroup ;
2020use PHPStan \Type \Regex \RegexExpressionHelper ;
21+ use PHPStan \Type \Regex \RegexGroupList ;
2122use PHPStan \Type \Regex \RegexGroupParser ;
2223use PHPStan \Type \StringType ;
2324use PHPStan \Type \Type ;
2425use PHPStan \Type \TypeCombinator ;
25- use function array_reverse ;
2626use function count ;
2727use function in_array ;
2828use function is_string ;
@@ -115,13 +115,8 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
115115 }
116116 [$ groupList , $ markVerbs ] = $ parseResult ;
117117
118- $ trailingOptionals = 0 ;
119- foreach (array_reverse ($ groupList ) as $ captureGroup ) {
120- if (!$ captureGroup ->isOptional ()) {
121- break ;
122- }
123- $ trailingOptionals ++;
124- }
118+ $ regexGroupList = new RegexGroupList ($ groupList );
119+ $ trailingOptionals = $ regexGroupList ->countTrailingOptionals ();
125120
126121 $ onlyOptionalTopLevelGroupId = $ this ->getOnlyOptionalTopLevelGroupId ($ groupList );
127122 $ onlyTopLevelAlternation = $ this ->getOnlyTopLevelAlternation ($ groupList );
@@ -134,10 +129,10 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
134129 ) {
135130 // if only one top level capturing optional group exists
136131 // we build a more precise tagged union of a empty-match and a match with the group
137- $ groupList [ $ onlyOptionalTopLevelGroupId ] = $ groupList [ $ onlyOptionalTopLevelGroupId ]-> forceNonOptional ( );
132+ $ regexGroupList = $ regexGroupList -> forceGroupIdNonOptional ( $ onlyOptionalTopLevelGroupId );
138133
139134 $ combiType = $ this ->buildArrayType (
140- $ groupList ,
135+ $ regexGroupList ,
141136 $ wasMatched ,
142137 $ trailingOptionals ,
143138 $ flags ,
@@ -165,25 +160,24 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
165160 $ combiTypes = [];
166161 $ isOptionalAlternation = false ;
167162 foreach ($ onlyTopLevelAlternation ->getGroupCombinations () as $ groupCombo ) {
168- $ comboList = $ groupList ;
163+ $ comboList = new RegexGroupList ( $ groupList) ;
169164
170165 $ beforeCurrentCombo = true ;
171166 foreach ($ comboList as $ groupId => $ group ) {
172167 if (in_array ($ groupId , $ groupCombo , true )) {
173168 $ isOptionalAlternation = $ group ->inOptionalAlternation ();
174- $ forcedGroup = $ group ->forceNonOptional ( );
169+ $ comboList = $ comboList -> forceGroupIdNonOptional ( $ group ->getId () );
175170 $ beforeCurrentCombo = false ;
176- $ comboList [$ groupId ] = $ forcedGroup ;
177171 } elseif ($ beforeCurrentCombo && !$ group ->resetsGroupCounter ()) {
178- $ forcedGroup = $ group ->forceNonOptional ()->forceType (
172+ $ comboList = $ comboList ->forceGroupIdTypeAndNonOptional (
173+ $ group ->getId (),
179174 $ this ->containsUnmatchedAsNull ($ flags , $ matchesAll ) ? new NullType () : new ConstantStringType ('' ),
180175 );
181- $ comboList [$ groupId ] = $ forcedGroup ;
182176 } elseif (
183177 $ group ->getAlternationId () === $ onlyTopLevelAlternation ->getId ()
184178 && !$ this ->containsUnmatchedAsNull ($ flags , $ matchesAll )
185179 ) {
186- unset( $ comboList[ $ groupId] );
180+ $ comboList = $ comboList -> removeGroup ( $ groupId );
187181 }
188182 }
189183
@@ -216,7 +210,7 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
216210 // the general case, which should work in all cases but does not yield the most
217211 // precise result possible in some cases
218212 return $ this ->buildArrayType (
219- $ groupList ,
213+ $ regexGroupList ,
220214 $ wasMatched ,
221215 $ trailingOptionals ,
222216 $ flags ,
@@ -280,11 +274,10 @@ private function getOnlyTopLevelAlternation(array $captureGroups): ?RegexAlterna
280274 }
281275
282276 /**
283- * @param array<RegexCapturingGroup> $captureGroups
284277 * @param list<string> $markVerbs
285278 */
286279 private function buildArrayType (
287- array $ captureGroups ,
280+ RegexGroupList $ captureGroups ,
288281 TrinaryLogic $ wasMatched ,
289282 int $ trailingOptionals ,
290283 int $ flags ,
0 commit comments