@@ -30,7 +30,7 @@ public static function match(string $pattern, string $subject, ?array &$matches
30
30
{
31
31
self ::checkOffsetCapture ($ flags , 'matchWithOffsets ' );
32
32
33
- $ result = preg_match ($ pattern , $ subject , $ matches , $ flags | PREG_UNMATCHED_AS_NULL , $ offset );
33
+ $ result = self :: pregMatch ($ pattern , $ subject , $ matches , $ flags | PREG_UNMATCHED_AS_NULL , $ offset );
34
34
if ($ result === false ) {
35
35
throw PcreException::fromFunction ('preg_match ' , $ pattern );
36
36
}
@@ -69,7 +69,7 @@ public static function matchStrictGroups(string $pattern, string $subject, ?arra
69
69
*/
70
70
public static function matchWithOffsets (string $ pattern , string $ subject , ?array &$ matches , int $ flags = 0 , int $ offset = 0 ): int
71
71
{
72
- $ result = preg_match ($ pattern , $ subject , $ matches , $ flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE , $ offset );
72
+ $ result = self :: pregMatch ($ pattern , $ subject , $ matches , $ flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE , $ offset );
73
73
if ($ result === false ) {
74
74
throw PcreException::fromFunction ('preg_match ' , $ pattern );
75
75
}
@@ -415,4 +415,40 @@ private static function enforceNonNullMatchAll(string $pattern, array $matches,
415
415
/** @var array<int|string, list<string>> */
416
416
return $ matches ;
417
417
}
418
+
419
+ /**
420
+ * @param non-empty-string $pattern
421
+ * @param array<string|null> $matches Set by method
422
+ * @param int-mask<PREG_UNMATCHED_AS_NULL|PREG_OFFSET_CAPTURE> $flags
423
+ * @return 0|1|false
424
+ *
425
+ * @param-out array<int|string, string|null> $matches
426
+ */
427
+ private static function pregMatch (string $ pattern , string $ subject , ?array &$ matches = null , int $ flags = 0 , int $ offset = 0 )
428
+ {
429
+ if (PHP_VERSION_ID >= 70400 ) {
430
+ return preg_match ($ pattern , $ subject , $ matches , $ flags , $ offset );
431
+ }
432
+
433
+ // On PHP 7.2 and 7.3, PREG_UNMATCHED_AS_NULL only works correctly in preg_match_all
434
+ // as preg_match does not set trailing unmatched groups to null
435
+ // e.g. preg_match('/(a)(b)*(c)(d)*/', 'ac', $matches, PREG_UNMATCHED_AS_NULL); would
436
+ // have $match[4] unset instead of null
437
+ //
438
+ // So we use preg_match_all here as workaround to ensure old-PHP meets the expectations
439
+ // set by the library's documentation
440
+ $ result = preg_match_all ($ pattern , $ subject , $ matchesInternal , $ flags , $ offset );
441
+ if (!is_int ($ result )) { // PHP < 8 may return null, 8+ returns int|false
442
+ throw PcreException::fromFunction ('preg_match ' , $ pattern );
443
+ }
444
+
445
+ if ($ result === 0 ) {
446
+ $ matches = [];
447
+ } else {
448
+ $ matches = array_map (function ($ m ) { return reset ($ m ); }, $ matchesInternal );
449
+ $ result = min ($ result , 1 );
450
+ }
451
+
452
+ return $ result ;
453
+ }
418
454
}
0 commit comments