Skip to content

Commit c28d43e

Browse files
committed
non-optional groups cannot be null when PREG_UNMATCHED_AS_NULL
1 parent 4adedba commit c28d43e

File tree

2 files changed

+15
-5
lines changed

2 files changed

+15
-5
lines changed

src/Type/Php/RegexArrayShapeMatcher.php

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,17 @@ private function buildArrayType(
253253
$countGroups = count($captureGroups);
254254
$i = 0;
255255
foreach ($captureGroups as $captureGroup) {
256+
$groupValueType = $valueType;
257+
256258
if (!$wasMatched->yes()) {
257259
$optional = true;
258260
} else {
259261
if ($i < $countGroups - $trailingOptionals) {
260262
$optional = false;
261-
} elseif (($flags & PREG_UNMATCHED_AS_NULL) !== 0 && $this->phpVersion->supportsPregUnmatchedAsNull()) {
263+
if ($this->containsUnmatchedAsNull($flags)) {
264+
$groupValueType = TypeCombinator::removeNull($groupValueType);
265+
}
266+
} elseif ($this->containsUnmatchedAsNull($flags)) {
262267
$optional = false;
263268
} else {
264269
$optional = $captureGroup->isOptional();
@@ -268,14 +273,14 @@ private function buildArrayType(
268273
if ($captureGroup->isNamed()) {
269274
$builder->setOffsetValueType(
270275
$this->getKeyType($captureGroup->getName()),
271-
$valueType,
276+
$groupValueType,
272277
$optional,
273278
);
274279
}
275280

276281
$builder->setOffsetValueType(
277282
$this->getKeyType($i + 1),
278-
$valueType,
283+
$groupValueType,
279284
$optional,
280285
);
281286

@@ -285,6 +290,11 @@ private function buildArrayType(
285290
return $builder->getArray();
286291
}
287292

293+
private function containsUnmatchedAsNull(int $flags): bool
294+
{
295+
return ($flags & PREG_UNMATCHED_AS_NULL) !== 0 && $this->phpVersion->supportsPregUnmatchedAsNull();
296+
}
297+
288298
private function getKeyType(int|string $key): Type
289299
{
290300
if (is_string($key)) {
@@ -298,7 +308,7 @@ private function getValueType(int $flags): Type
298308
{
299309
$valueType = new StringType();
300310
$offsetType = IntegerRangeType::fromInterval(0, null);
301-
if (($flags & PREG_UNMATCHED_AS_NULL) !== 0 && $this->phpVersion->supportsPregUnmatchedAsNull()) {
311+
if ($this->containsUnmatchedAsNull($flags)) {
302312
$valueType = TypeCombinator::addNull($valueType);
303313
// unmatched groups return -1 as offset
304314
$offsetType = IntegerRangeType::fromInterval(-1, null);

tests/PHPStan/Analyser/nsrt/bug-11311.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
function doFoo(string $s) {
88
if (1 === preg_match('/(?<major>\d+)\.(?<minor>\d+)(?:\.(?<patch>\d+))?/', $s, $matches, PREG_UNMATCHED_AS_NULL)) {
99

10-
assertType('array{0: string, major: string|null, 1: string|null, minor: string|null, 2: string|null, patch: string|null, 3: string|null}', $matches);
10+
assertType('array{0: string, major: string, 1: string, minor: string, 2: string, patch: string|null, 3: string|null}', $matches);
1111
}
1212
}
1313

0 commit comments

Comments
 (0)