Skip to content

Commit

Permalink
infer non-empty-string on strstr() comparison with constant string
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed May 30, 2022
1 parent 27f523f commit 82ef69d
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/Analyser/TypeSpecifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ public function specifyTypesInCondition(
if (
$exprNode instanceof FuncCall
&& $exprNode->name instanceof Name
&& strtolower($exprNode->name->toString()) === 'substr'
&& in_array(strtolower($exprNode->name->toString()), ['substr', 'strstr', 'stristr', 'strchr', 'strrchr'], true)
&& isset($exprNode->getArgs()[0])
&& $constantType instanceof ConstantStringType
&& $constantType->getValue() !== ''
Expand Down
2 changes: 2 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,8 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/conditional-types-inference.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7210.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7341.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-string-strstr-specifying.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-string-strrchr-specifying.php');
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,11 @@ public function variants(string $s) {
assertType('string', $s);

if (strstr($s, ':') === 'hallo') {
assertType('string', $s); // could be non-empty-string
assertType('non-empty-string', $s);
}
assertType('string', $s);
if (strstr($s, ':', true) === 'hallo') {
assertType('string', $s); // could be non-empty-string
assertType('non-empty-string', $s);
}
assertType('string', $s);
if (strstr($s, ':', true) !== false) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace NonEmptyStringStrrchr;

use function PHPStan\Testing\assertType;

class Foo {
public function nonEmptyStrrchr(string $s, string $needle): void
{
if (strrchr($s, 'abc') === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);
if ('hallo' === strrchr($s, 'abc')) {
assertType('non-empty-string', $s);
}
assertType('string', $s);

if (strrchr($s, $needle) === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);
if ('hallo' === strrchr($s, $needle)) {
assertType('non-empty-string', $s);
}
assertType('string', $s);

$x = (strrchr($s, $needle) === 'hallo');
assertType('string', $s);
var_dump($x);

$x = (strrchr($s, $needle) !== 'hallo');
assertType('string', $s);
var_dump($x);
}
}
124 changes: 124 additions & 0 deletions tests/PHPStan/Analyser/data/non-empty-string-strstr-specifying.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

namespace NonEmptyStringStrstr;

use function PHPStan\Testing\assertType;

class Foo {
public function nonEmptyStrstr(string $s, string $needle, bool $before_needle): void
{
if (strstr($s, 'abc') === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);
if ('hallo' === strstr($s, 'abc')) {
assertType('non-empty-string', $s);
}
assertType('string', $s);

if (strstr($s, $needle) === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);
if ('hallo' === strstr($s, $needle)) {
assertType('non-empty-string', $s);
}
assertType('string', $s);

if (strstr($s, $needle, true) === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);

if (strstr($s, $needle, false) === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);

if (strstr($s, $needle, $before_needle) === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);

if (strstr($s, $needle, $before_needle) !== 'hallo') {
assertType('string', $s);
}
assertType('string', $s);

if (strstr($s, $needle, $before_needle) === '') {
assertType('string', $s);
}
assertType('string', $s);
if ('' === strstr($s, $needle, $before_needle)) {
assertType('string', $s);
}
assertType('string', $s);

if (strstr($s, $needle, $before_needle) == '') {
assertType('string', $s);
}
assertType('string', $s);
if ('' == strstr($s, $needle, $before_needle)) {
assertType('string', $s);
}
assertType('string', $s);

$x = (strstr($s, $needle) === 'hallo');
assertType('string', $s);
var_dump($x);

$x = (strstr($s, $needle) !== 'hallo');
assertType('string', $s);
var_dump($x);
}

public function nonEmptyStristr(string $s, string $needle, bool $before_needle): void
{
if (stristr($s, 'abc') === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);
if ('hallo' === stristr($s, 'abc')) {
assertType('non-empty-string', $s);
}

if (stristr($s, $needle, $before_needle) == '') {
assertType('string', $s);
}
assertType('string', $s);
if ('' == stristr($s, $needle, $before_needle)) {
assertType('string', $s);
}
assertType('string', $s);

$x = (stristr($s, $needle) === 'hallo');
assertType('string', $s);
var_dump($x);
}


// strchr() is an alias of strstr()
public function nonEmptyStrchr(string $s, string $needle, bool $before_needle): void
{
if (strchr($s, 'abc') === 'hallo') {
assertType('non-empty-string', $s);
}
assertType('string', $s);
if ('hallo' === strchr($s, 'abc')) {
assertType('non-empty-string', $s);
}

if (strchr($s, $needle, $before_needle) == '') {
assertType('string', $s);
}
assertType('string', $s);
if ('' == strchr($s, $needle, $before_needle)) {
assertType('string', $s);
}
assertType('string', $s);

$x = (strchr($s, $needle) === 'hallo');
assertType('string', $s);
var_dump($x);
}
}

0 comments on commit 82ef69d

Please sign in to comment.