@@ -38,6 +38,25 @@ public static function match(string $pattern, string $subject, ?array &$matches
38
38
return $ result ;
39
39
}
40
40
41
+ /**
42
+ * Variant of `match()` which outputs non-null matches (or throws)
43
+ *
44
+ * @param non-empty-string $pattern
45
+ * @param array<string> $matches Set by method
46
+ * @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
47
+ * @return 0|1
48
+ * @throws UnexpectedNullMatchException
49
+ *
50
+ * @param-out array<int|string, string> $matches
51
+ */
52
+ public static function matchStrictGroups (string $ pattern , string $ subject , ?array &$ matches = null , int $ flags = 0 , int $ offset = 0 ): int
53
+ {
54
+ $ result = self ::match ($ pattern , $ subject , $ matchesInternal , $ flags , $ offset );
55
+ $ matches = self ::enforceNonNullMatches ($ pattern , $ matchesInternal , 'match ' );
56
+
57
+ return $ result ;
58
+ }
59
+
41
60
/**
42
61
* Runs preg_match with PREG_OFFSET_CAPTURE
43
62
*
@@ -61,18 +80,15 @@ public static function matchWithOffsets(string $pattern, string $subject, ?array
61
80
/**
62
81
* @param non-empty-string $pattern
63
82
* @param array<int|string, list<string|null>> $matches Set by method
64
- * @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_SET_ORDER > $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
83
+ * @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
65
84
* @return 0|positive-int
66
85
*
67
86
* @param-out array<int|string, list<string|null>> $matches
68
87
*/
69
88
public static function matchAll (string $ pattern , string $ subject , ?array &$ matches = null , int $ flags = 0 , int $ offset = 0 ): int
70
89
{
71
90
self ::checkOffsetCapture ($ flags , 'matchAllWithOffsets ' );
72
-
73
- if (($ flags & PREG_SET_ORDER ) !== 0 ) {
74
- throw new \InvalidArgumentException ('PREG_SET_ORDER is not supported as it changes the type of $matches ' );
75
- }
91
+ self ::checkSetOrder ($ flags );
76
92
77
93
$ result = preg_match_all ($ pattern , $ subject , $ matches , $ flags | PREG_UNMATCHED_AS_NULL , $ offset );
78
94
if (!is_int ($ result )) { // PHP < 8 may return null, 8+ returns int|false
@@ -82,6 +98,25 @@ public static function matchAll(string $pattern, string $subject, ?array &$match
82
98
return $ result ;
83
99
}
84
100
101
+ /**
102
+ * Variant of `match()` which outputs non-null matches (or throws)
103
+ *
104
+ * @param non-empty-string $pattern
105
+ * @param array<int|string, list<string|null>> $matches Set by method
106
+ * @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
107
+ * @return 0|positive-int
108
+ * @throws UnexpectedNullMatchException
109
+ *
110
+ * @param-out array<int|string, list<string>> $matches
111
+ */
112
+ public static function matchAllStrictGroups (string $ pattern , string $ subject , ?array &$ matches = null , int $ flags = 0 , int $ offset = 0 ): int
113
+ {
114
+ $ result = self ::matchAll ($ pattern , $ subject , $ matchesInternal , $ flags , $ offset );
115
+ $ matches = self ::enforceNonNullMatchAll ($ pattern , $ matchesInternal , 'matchAll ' );
116
+
117
+ return $ result ;
118
+ }
119
+
85
120
/**
86
121
* Runs preg_match_all with PREG_OFFSET_CAPTURE
87
122
*
@@ -94,6 +129,8 @@ public static function matchAll(string $pattern, string $subject, ?array &$match
94
129
*/
95
130
public static function matchAllWithOffsets (string $ pattern , string $ subject , ?array &$ matches , int $ flags = 0 , int $ offset = 0 ): int
96
131
{
132
+ self ::checkSetOrder ($ flags );
133
+
97
134
$ result = preg_match_all ($ pattern , $ subject , $ matches , $ flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE , $ offset );
98
135
if (!is_int ($ result )) { // PHP < 8 may return null, 8+ returns int|false
99
136
throw PcreException::fromFunction ('preg_match_all ' , $ pattern );
@@ -241,6 +278,8 @@ public static function grep(string $pattern, array $array, int $flags = 0): arra
241
278
}
242
279
243
280
/**
281
+ * Variant of match() which returns a bool instead of int
282
+ *
244
283
* @param non-empty-string $pattern
245
284
* @param array<string|null> $matches Set by method
246
285
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
@@ -253,6 +292,23 @@ public static function isMatch(string $pattern, string $subject, ?array &$matche
253
292
}
254
293
255
294
/**
295
+ * Variant of `isMatch()` which outputs non-null matches (or throws)
296
+ *
297
+ * @param non-empty-string $pattern
298
+ * @param array<string> $matches Set by method
299
+ * @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
300
+ * @throws UnexpectedNullMatchException
301
+ *
302
+ * @param-out array<int|string, string> $matches
303
+ */
304
+ public static function isMatchStrictGroups (string $ pattern , string $ subject , ?array &$ matches = null , int $ flags = 0 , int $ offset = 0 ): bool
305
+ {
306
+ return (bool ) self ::matchStrictGroups ($ pattern , $ subject , $ matches , $ flags , $ offset );
307
+ }
308
+
309
+ /**
310
+ * Variant of matchAll() which returns a bool instead of int
311
+ *
256
312
* @param non-empty-string $pattern
257
313
* @param array<int|string, list<string|null>> $matches Set by method
258
314
* @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
@@ -265,6 +321,22 @@ public static function isMatchAll(string $pattern, string $subject, ?array &$mat
265
321
}
266
322
267
323
/**
324
+ * Variant of `isMatchAll()` which outputs non-null matches (or throws)
325
+ *
326
+ * @param non-empty-string $pattern
327
+ * @param array<int|string, list<string>> $matches Set by method
328
+ * @param int-mask<PREG_UNMATCHED_AS_NULL> $flags PREG_UNMATCHED_AS_NULL is always set, no other flags are supported
329
+ *
330
+ * @param-out array<int|string, list<string>> $matches
331
+ */
332
+ public static function isMatchAllStrictGroups (string $ pattern , string $ subject , ?array &$ matches = null , int $ flags = 0 , int $ offset = 0 ): bool
333
+ {
334
+ return (bool ) self ::matchAllStrictGroups ($ pattern , $ subject , $ matches , $ flags , $ offset );
335
+ }
336
+
337
+ /**
338
+ * Variant of matchWithOffsets() which returns a bool instead of int
339
+ *
268
340
* Runs preg_match with PREG_OFFSET_CAPTURE
269
341
*
270
342
* @param non-empty-string $pattern
@@ -279,6 +351,8 @@ public static function isMatchWithOffsets(string $pattern, string $subject, ?arr
279
351
}
280
352
281
353
/**
354
+ * Variant of matchAllWithOffsets() which returns a bool instead of int
355
+ *
282
356
* Runs preg_match_all with PREG_OFFSET_CAPTURE
283
357
*
284
358
* @param non-empty-string $pattern
@@ -298,4 +372,47 @@ private static function checkOffsetCapture(int $flags, string $useFunctionName):
298
372
throw new \InvalidArgumentException ('PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use ' . $ useFunctionName . '() instead ' );
299
373
}
300
374
}
375
+
376
+ private static function checkSetOrder (int $ flags ): void
377
+ {
378
+ if (($ flags & PREG_SET_ORDER ) !== 0 ) {
379
+ throw new \InvalidArgumentException ('PREG_SET_ORDER is not supported as it changes the type of $matches ' );
380
+ }
381
+ }
382
+
383
+ /**
384
+ * @param array<int|string, string|null> $matches
385
+ * @return array<int|string, string>
386
+ * @throws UnexpectedNullMatchException
387
+ */
388
+ private static function enforceNonNullMatches (string $ pattern , array $ matches , string $ variantMethod )
389
+ {
390
+ foreach ($ matches as $ group => $ match ) {
391
+ if (null === $ match ) {
392
+ throw new UnexpectedNullMatchException ('Pattern " ' .$ pattern .'" had an unexpected unmatched group " ' .$ group .'", make sure the pattern always matches or use ' .$ variantMethod .'() instead. ' );
393
+ }
394
+ }
395
+
396
+ /** @var array<string> */
397
+ return $ matches ;
398
+ }
399
+
400
+ /**
401
+ * @param array<int|string, list<string|null>> $matches
402
+ * @return array<int|string, list<string>>
403
+ * @throws UnexpectedNullMatchException
404
+ */
405
+ private static function enforceNonNullMatchAll (string $ pattern , array $ matches , string $ variantMethod )
406
+ {
407
+ foreach ($ matches as $ group => $ groupMatches ) {
408
+ foreach ($ groupMatches as $ match ) {
409
+ if (null === $ match ) {
410
+ throw new UnexpectedNullMatchException ('Pattern " ' .$ pattern .'" had an unexpected unmatched group " ' .$ group .'", make sure the pattern always matches or use ' .$ variantMethod .'() instead. ' );
411
+ }
412
+ }
413
+ }
414
+
415
+ /** @var array<int|string, list<string>> */
416
+ return $ matches ;
417
+ }
301
418
}
0 commit comments