Skip to content

Commit 81d8daa

Browse files
mvhirschshish
authored andcommitted
reproducer
1 parent a5aa2cd commit 81d8daa

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

tests/Type/Php/TypeAssertionsTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public static function dataFileAsserts(): iterable
1515
yield from self::gatherAssertTypes(__DIR__ . '/data/preg_match_checked.php');
1616
yield from self::gatherAssertTypes(__DIR__ . '/data/preg_replace_return.php');
1717
yield from self::gatherAssertTypes(__DIR__ . '/data/json_decode_return.php');
18+
yield from self::gatherAssertTypes(__DIR__ . '/data/preg_match_identity_check.php');
1819
}
1920

2021
/**
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace TheCodingMachine\Safe\PHPStan\Type\Php\data;
4+
5+
use function Safe\preg_match;
6+
7+
// PHPStan bug report: Safe\preg_match with "1 ===" identity check does NOT narrow $matches,
8+
// even though plain truthy if (preg_match(...)) works correctly.
9+
//
10+
// Root cause: PHPStan's TypeSpecifier.php has a hardcoded literal-name check for 'preg_match'
11+
// in resolveNormalizedIdentical(). This triggers specifyTypesInCondition() on the FuncCall, but
12+
// the FuncCall is wrapped in AlwaysRememberedExpr at that point, so FunctionTypeSpecifyingExtension
13+
// is never called and $matches is not narrowed.
14+
//
15+
// This affects ANY extension-based preg_match wrapper (e.g. Safe\preg_match) — not only native.
16+
17+
$pattern = '/H(.)ll(o) (World)?/';
18+
$string = 'Hello World';
19+
20+
// This is what SHOULD happen (same as the truthy check in preg_match_checked.php):
21+
$expectedType = "array{0: non-falsy-string, 1: non-empty-string, 2: 'o', 3?: 'World'}";
22+
23+
// BUG: $matches is NOT narrowed — actual type is array{}|array{...} instead of array{...}
24+
if (1 === preg_match($pattern, $string, $matches)) {
25+
\PHPStan\Testing\assertSuperType($expectedType, $matches);
26+
}
27+
28+
// BUG: same issue via FQCN
29+
if (1 === \Safe\preg_match($pattern, $string, $matches2)) {
30+
\PHPStan\Testing\assertSuperType($expectedType, $matches2);
31+
}

0 commit comments

Comments
 (0)