Skip to content

Commit a72ae08

Browse files
committed
RegexArrayShapeMatcher - Fix optional groups with PREG_UNMATCHED_AS_NULL
1 parent 892eb2e commit a72ae08

File tree

2 files changed

+18
-4
lines changed

2 files changed

+18
-4
lines changed

src/Type/Php/RegexArrayShapeMatcher.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,13 @@ private function matchRegex(string $regex, ?int $flags, TrinaryLogic $wasMatched
121121
$flags ?? 0,
122122
);
123123

124-
return TypeCombinator::union(
125-
new ConstantArrayType([new ConstantIntegerType(0)], [new StringType()], [0], [], true),
126-
$combiType,
127-
);
124+
if (!$this->containsUnmatchedAsNull($flags ?? 0)) {
125+
$combiType = TypeCombinator::union(
126+
new ConstantArrayType([new ConstantIntegerType(0)], [new StringType()], [0], [], true),
127+
$combiType,
128+
);
129+
}
130+
return $combiType;
128131
} elseif (
129132
$wasMatched->yes()
130133
&& $onlyTopLevelAlternationId !== null

tests/PHPStan/Analyser/nsrt/preg_match_shapes.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,17 @@ function bug11277b(string $value): void
353353
}
354354
}
355355

356+
// see https://3v4l.org/VeDob
357+
function unmatchedAsNullWithOptionalGroup(string $s): void {
358+
if (preg_match('/Price: (£|€)?\d+/', $s, $matches, PREG_UNMATCHED_AS_NULL)) {
359+
// with PREG_UNMATCHED_AS_NULL the offset 1 will always exist. It is correct that it's nullable because it's optional though
360+
assertType('array{string, string|null}', $matches);
361+
} else {
362+
assertType('array{}', $matches);
363+
}
364+
assertType('array{}|array{string, string|null}', $matches);
365+
}
366+
356367
// https://www.pcre.org/current/doc/html/pcre2pattern.html#dupgroupnumber
357368
// https://3v4l.org/09qdT
358369
function bug11291(string $s): void {

0 commit comments

Comments
 (0)