Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 745ae5a

Browse files
VincentLangletondrejmirtes
authored andcommitted
Fix wrong inference on preg_match != false
1 parent f43f8ea commit 745ae5a

File tree

8 files changed

+69
-11
lines changed

8 files changed

+69
-11
lines changed

‎src/Analyser/TypeSpecifier.php‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,13 +339,15 @@ public function specifyTypesInCondition(
339339
&& count($expr->right->getArgs()) >= 3
340340
&& $expr->right->name instanceof Name
341341
&& in_array(strtolower((string) $expr->right->name), ['preg_match'], true)
342-
&& IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($leftType)->yes()
342+
&& (
343+
IntegerRangeType::fromInterval(1, null)->isSuperTypeOf($leftType)->yes()
344+
|| ($expr instanceof Expr\BinaryOp\Smaller && IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($leftType)->yes())
345+
)
343346
) {
344-
return $this->specifyTypesInCondition(
345-
$scope,
346-
new Expr\BinaryOp\NotIdentical($expr->right, new ConstFetch(new Name('false'))),
347-
$context,
348-
)->setRootExpr($expr);
347+
// 0 < preg_match or 1 <= preg_match becomes 1 === preg_match
348+
$newExpr = new Expr\BinaryOp\Identical($expr->right, new Node\Scalar\Int_(1));
349+
350+
return $this->specifyTypesInCondition($scope, $newExpr, $context)->setRootExpr($expr);
349351
}
350352

351353
if (

‎src/Type/Php/PregMatchTypeSpecifyingExtension.php‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,18 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
5555
$flagsType = $scope->getType($flagsArg->value);
5656
}
5757

58+
if ($context->true() && $context->falsey()) {
59+
$wasMatched = TrinaryLogic::createMaybe();
60+
} elseif ($context->true()) {
61+
$wasMatched = TrinaryLogic::createYes();
62+
} else {
63+
$wasMatched = TrinaryLogic::createNo();
64+
}
65+
5866
if ($functionReflection->getName() === 'preg_match') {
59-
$matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope);
67+
$matchedType = $this->regexShapeMatcher->matchExpr($patternArg->value, $flagsType, $wasMatched, $scope);
6068
} else {
61-
$matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, TrinaryLogic::createFromBoolean($context->true()), $scope);
69+
$matchedType = $this->regexShapeMatcher->matchAllExpr($patternArg->value, $flagsType, $wasMatched, $scope);
6270
}
6371
if ($matchedType === null) {
6472
return new SpecifiedTypes();

‎tests/PHPStan/Analyser/nsrt/bug-11293.php‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function sayHello3(string $s): void
3030
public function sayHello4(string $s): void
3131
{
3232
if (preg_match('/data-(\d{6})\.json$/', $s, $matches) <= 0) {
33-
assertType('array{}', $matches);
33+
assertType('list{0?: string, 1?: non-falsy-string&numeric-string}', $matches);
3434

3535
return;
3636
}
@@ -41,7 +41,7 @@ public function sayHello4(string $s): void
4141
public function sayHello5(string $s): void
4242
{
4343
if (preg_match('/data-(\d{6})\.json$/', $s, $matches) < 1) {
44-
assertType('array{}', $matches);
44+
assertType('list{0?: string, 1?: non-falsy-string&numeric-string}', $matches);
4545

4646
return;
4747
}
@@ -52,7 +52,7 @@ public function sayHello5(string $s): void
5252
public function sayHello6(string $s): void
5353
{
5454
if (1 > preg_match('/data-(\d{6})\.json$/', $s, $matches)) {
55-
assertType('array{}', $matches);
55+
assertType('list{0?: string, 1?: non-falsy-string&numeric-string}', $matches);
5656

5757
return;
5858
}

‎tests/PHPStan/Rules/Comparison/BooleanAndConstantConditionRuleTest.php‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ public function testBug5365(): void
432432
$this->analyse([__DIR__ . '/data/bug-5365.php'], []);
433433
}
434434

435+
public function testBug11908(): void
436+
{
437+
$this->treatPhpDocTypesAsCertain = true;
438+
$this->reportAlwaysTrueInLastCondition = true;
439+
$this->analyse([__DIR__ . '/data/bug-11908.php'], []);
440+
}
441+
435442
public function testBug8555(): void
436443
{
437444
$this->treatPhpDocTypesAsCertain = true;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11908;
4+
5+
$matches = false;
6+
if (preg_match('/a/', '', $matches) !== false && $matches) {
7+
var_export($matches);
8+
}

‎tests/PHPStan/Rules/Variables/IssetRuleTest.php‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@ public function testBug12771(): void
473473
$this->analyse([__DIR__ . '/data/bug-12771.php'], []);
474474
}
475475

476+
public function testBug11708(): void
477+
{
478+
$this->treatPhpDocTypesAsCertain = true;
479+
480+
$this->analyse([__DIR__ . '/data/bug-11708.php'], []);
481+
}
482+
476483
public function testIssetAfterRememberedConstructor(): void
477484
{
478485
$this->treatPhpDocTypesAsCertain = true;

‎tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,13 @@ public function testBug10577(): void
342342
$this->analyse([__DIR__ . '/data/bug-10577.php'], []);
343343
}
344344

345+
public function testBug11708(): void
346+
{
347+
$this->treatPhpDocTypesAsCertain = true;
348+
349+
$this->analyse([__DIR__ . '/data/bug-11708.php'], []);
350+
}
351+
345352
public function testBug10610(): void
346353
{
347354
$this->treatPhpDocTypesAsCertain = true;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug11708;
4+
5+
class HelloWorld
6+
{
7+
public function sayHello(): void
8+
{
9+
$xRequestStart = sprintf('t=%s', uniqid('fake_timestamp_'));
10+
11+
$matches = [];
12+
if (false === preg_match('/^t=(\d+)$/', (string) $xRequestStart, $matches)) {
13+
return;
14+
}
15+
16+
$requestStart = $matches[1] ?? null;
17+
$requestStart2 = isset($matches[1]);
18+
}
19+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /