18
18
use PHPStan \Type \Regex \RegexAlternation ;
19
19
use PHPStan \Type \Regex \RegexCapturingGroup ;
20
20
use PHPStan \Type \Regex \RegexExpressionHelper ;
21
+ use PHPStan \Type \Regex \RegexGroupList ;
21
22
use PHPStan \Type \Regex \RegexGroupParser ;
22
23
use PHPStan \Type \StringType ;
23
24
use PHPStan \Type \Type ;
24
25
use PHPStan \Type \TypeCombinator ;
25
- use function array_reverse ;
26
26
use function count ;
27
27
use function in_array ;
28
28
use function is_string ;
@@ -115,13 +115,8 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
115
115
}
116
116
[$ groupList , $ markVerbs ] = $ parseResult ;
117
117
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 ();
125
120
126
121
$ onlyOptionalTopLevelGroupId = $ this ->getOnlyOptionalTopLevelGroupId ($ groupList );
127
122
$ onlyTopLevelAlternation = $ this ->getOnlyTopLevelAlternation ($ groupList );
@@ -134,10 +129,10 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
134
129
) {
135
130
// if only one top level capturing optional group exists
136
131
// 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 );
138
133
139
134
$ combiType = $ this ->buildArrayType (
140
- $ groupList ,
135
+ $ regexGroupList ,
141
136
$ wasMatched ,
142
137
$ trailingOptionals ,
143
138
$ flags ,
@@ -165,25 +160,24 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
165
160
$ combiTypes = [];
166
161
$ isOptionalAlternation = false ;
167
162
foreach ($ onlyTopLevelAlternation ->getGroupCombinations () as $ groupCombo ) {
168
- $ comboList = $ groupList ;
163
+ $ comboList = new RegexGroupList ( $ groupList) ;
169
164
170
165
$ beforeCurrentCombo = true ;
171
166
foreach ($ comboList as $ groupId => $ group ) {
172
167
if (in_array ($ groupId , $ groupCombo , true )) {
173
168
$ isOptionalAlternation = $ group ->inOptionalAlternation ();
174
- $ forcedGroup = $ group ->forceNonOptional ( );
169
+ $ comboList = $ comboList -> forceGroupIdNonOptional ( $ group ->getId () );
175
170
$ beforeCurrentCombo = false ;
176
- $ comboList [$ groupId ] = $ forcedGroup ;
177
171
} elseif ($ beforeCurrentCombo && !$ group ->resetsGroupCounter ()) {
178
- $ forcedGroup = $ group ->forceNonOptional ()->forceType (
172
+ $ comboList = $ comboList ->forceGroupIdTypeAndNonOptional (
173
+ $ group ->getId (),
179
174
$ this ->containsUnmatchedAsNull ($ flags , $ matchesAll ) ? new NullType () : new ConstantStringType ('' ),
180
175
);
181
- $ comboList [$ groupId ] = $ forcedGroup ;
182
176
} elseif (
183
177
$ group ->getAlternationId () === $ onlyTopLevelAlternation ->getId ()
184
178
&& !$ this ->containsUnmatchedAsNull ($ flags , $ matchesAll )
185
179
) {
186
- unset( $ comboList[ $ groupId] );
180
+ $ comboList = $ comboList -> removeGroup ( $ groupId );
187
181
}
188
182
}
189
183
@@ -216,7 +210,7 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
216
210
// the general case, which should work in all cases but does not yield the most
217
211
// precise result possible in some cases
218
212
return $ this ->buildArrayType (
219
- $ groupList ,
213
+ $ regexGroupList ,
220
214
$ wasMatched ,
221
215
$ trailingOptionals ,
222
216
$ flags ,
@@ -280,11 +274,10 @@ private function getOnlyTopLevelAlternation(array $captureGroups): ?RegexAlterna
280
274
}
281
275
282
276
/**
283
- * @param array<RegexCapturingGroup> $captureGroups
284
277
* @param list<string> $markVerbs
285
278
*/
286
279
private function buildArrayType (
287
- array $ captureGroups ,
280
+ RegexGroupList $ captureGroups ,
288
281
TrinaryLogic $ wasMatched ,
289
282
int $ trailingOptionals ,
290
283
int $ flags ,
0 commit comments