diff --git a/phpstan.neon b/phpstan.neon index 39ec473537f..429e9f6e8d5 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -218,6 +218,8 @@ parameters: - '#Register "Rector\\Php81\\Rector\\ClassMethod\\NewInInitializerRector" service to "php81\.php" config set#' + - '#Register "Rector\\Php80\\Rector\\NotIdentical\\MbStrContainsRector" service to "php80\.php" config set#' + # closure detailed - '#Method Rector\\Config\\RectorConfig\:\:singleton\(\) has parameter \$concrete with no signature specified for Closure#' diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..daef049f323 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture_equal.php.inc new file mode 100644 index 00000000000..044cda46f28 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/fixture_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/identical_mb_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/identical_mb_strpos.php.inc new file mode 100644 index 00000000000..583b2cb371d --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/identical_mb_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_equal_mb_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_equal_mb_strstr.php.inc new file mode 100644 index 00000000000..8e1ca6f4c53 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_equal_mb_strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_strstr.php.inc new file mode 100644 index 00000000000..4e3499ebb67 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/not_strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_equal_strpos.php.inc new file mode 100644 index 00000000000..7a91da6a599 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_equal_strpos.php.inc new file mode 100644 index 00000000000..b57186ea9d6 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_mb_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_mb_strpos.php.inc new file mode 100644 index 00000000000..5202f15e2d8 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_expression_mb_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_equal_strpos.php.inc new file mode 100644 index 00000000000..22c0564cbd9 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_strpos.php.inc new file mode 100644 index 00000000000..5f928270ad7 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_negative_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_strpos.php.inc new file mode 100644 index 00000000000..6f2aadf4607 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_equal_strpos.php.inc new file mode 100644 index 00000000000..defaf7f1515 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_strpos.php.inc new file mode 100644 index 00000000000..9b4945425dc --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc new file mode 100644 index 00000000000..ff3e1bc7e9c --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_equal_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_strpos.php.inc new file mode 100644 index 00000000000..c7cac64b729 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_variable_zero_strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_equal_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_equal_strpos.php.inc new file mode 100644 index 00000000000..54cf829f71e --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_equal_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_strpos.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_strpos.php.inc new file mode 100644 index 00000000000..7938e5ce99e --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/offset_zero_strpos.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strpos_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strpos_equal.php.inc new file mode 100644 index 00000000000..a6129f96ced --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strpos_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr.php.inc new file mode 100644 index 00000000000..613b9fb4480 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr_equal.php.inc new file mode 100644 index 00000000000..039c4b8949e --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/strstr_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way.php.inc new file mode 100644 index 00000000000..f61ee8b3b4a --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way_equal.php.inc b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way_equal.php.inc new file mode 100644 index 00000000000..2e7ef24f8ad --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/Fixture/the_other_way_equal.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/MbStrContainsRectorTest.php b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/MbStrContainsRectorTest.php new file mode 100644 index 00000000000..6c7f78b1f46 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/MbStrContainsRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/config/configured_rule.php b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/config/configured_rule.php new file mode 100644 index 00000000000..559d668df47 --- /dev/null +++ b/rules-tests/Php80/Rector/NotIdentical/MbStrContainsRector/config/configured_rule.php @@ -0,0 +1,13 @@ +phpVersion(PhpVersion::PHP_80); + + $rectorConfig->rule(MbStrContainsRector::class); +}; diff --git a/rules/Php80/NodeResolver/StrFalseComparisonResolver.php b/rules/Php80/NodeResolver/StrFalseComparisonResolver.php new file mode 100644 index 00000000000..9bc44372491 --- /dev/null +++ b/rules/Php80/NodeResolver/StrFalseComparisonResolver.php @@ -0,0 +1,58 @@ +valueResolver->isFalse($expr->left)) { + if (! $expr->right instanceof FuncCall) { + return null; + } + + if (! $this->nodeNameResolver->isNames($expr->right, $oldStrFuncNames)) { + return null; + } + + /** @var FuncCall $funcCall */ + $funcCall = $expr->right; + return $funcCall; + } + + if ($this->valueResolver->isFalse($expr->right)) { + if (! $expr->left instanceof FuncCall) { + return null; + } + + if (! $this->nodeNameResolver->isNames($expr->left, $oldStrFuncNames)) { + return null; + } + + /** @var FuncCall $funcCall */ + $funcCall = $expr->left; + return $funcCall; + } + + return null; + } +} diff --git a/rules/Php80/Rector/NotIdentical/MbStrContainsRector.php b/rules/Php80/Rector/NotIdentical/MbStrContainsRector.php new file mode 100644 index 00000000000..ac0ba3cb985 --- /dev/null +++ b/rules/Php80/Rector/NotIdentical/MbStrContainsRector.php @@ -0,0 +1,135 @@ +> + */ + public function getNodeTypes(): array + { + return [Identical::class, NotIdentical::class, Equal::class, NotEqual::class]; + } + + /** + * @param Identical|NotIdentical|Equal|NotEqual $node + */ + public function refactor(Node $node): ?Node + { + $funcCall = $this->strFalseComparisonResolver->resolve($node, self::OLD_STR_NAMES); + + if (! $funcCall instanceof FuncCall) { + return null; + } + + if ($funcCall->isFirstClassCallable()) { + return null; + } + + if (isset($funcCall->getArgs()[2])) { + $secondArg = $funcCall->getArgs()[2]; + + if ($this->isName($funcCall->name, 'mb_strpos') && ! $this->isIntegerZero($secondArg->value)) { + $funcCall->args[0] = new Arg($this->nodeFactory->createFuncCall( + 'mb_substr', + [$funcCall->args[0], $secondArg] + )); + } + + unset($funcCall->args[2]); + } + + $funcCall->name = new Name('str_contains'); + + if ($node instanceof Identical || $node instanceof Equal) { + return new BooleanNot($funcCall); + } + + return $funcCall; + } + + public function providePolyfillPackage(): string + { + return PolyfillPackage::PHP_80; + } + + private function isIntegerZero(Expr $expr): bool + { + if (! $expr instanceof Int_) { + return false; + } + + return $expr->value === 0; + } +} diff --git a/rules/Php80/Rector/NotIdentical/StrContainsRector.php b/rules/Php80/Rector/NotIdentical/StrContainsRector.php index 035e2ec6d8e..3d3f3710bf6 100644 --- a/rules/Php80/Rector/NotIdentical/StrContainsRector.php +++ b/rules/Php80/Rector/NotIdentical/StrContainsRector.php @@ -15,6 +15,7 @@ use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; use PhpParser\Node\Scalar\Int_; +use Rector\Php80\NodeResolver\StrFalseComparisonResolver; use Rector\PhpParser\Node\Value\ValueResolver; use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; @@ -35,7 +36,7 @@ final class StrContainsRector extends AbstractRector implements MinPhpVersionInt private const OLD_STR_NAMES = ['strpos', 'strstr']; public function __construct( - private readonly ValueResolver $valueResolver + private readonly StrFalseComparisonResolver $strFalseComparisonResolver ) { } @@ -87,7 +88,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $funcCall = $this->matchIdenticalOrNotIdenticalToFalse($node); + $funcCall = $this->strFalseComparisonResolver->resolve($node, self::OLD_STR_NAMES); if (! $funcCall instanceof FuncCall) { return null; @@ -124,39 +125,6 @@ public function providePolyfillPackage(): string return PolyfillPackage::PHP_80; } - private function matchIdenticalOrNotIdenticalToFalse(Identical | NotIdentical | Equal | NotEqual $expr): ?FuncCall - { - if ($this->valueResolver->isFalse($expr->left)) { - if (! $expr->right instanceof FuncCall) { - return null; - } - - if (! $this->isNames($expr->right, self::OLD_STR_NAMES)) { - return null; - } - - /** @var FuncCall $funcCall */ - $funcCall = $expr->right; - return $funcCall; - } - - if ($this->valueResolver->isFalse($expr->right)) { - if (! $expr->left instanceof FuncCall) { - return null; - } - - if (! $this->isNames($expr->left, self::OLD_STR_NAMES)) { - return null; - } - - /** @var FuncCall $funcCall */ - $funcCall = $expr->left; - return $funcCall; - } - - return null; - } - private function isIntegerZero(Expr $expr): bool { if (! $expr instanceof Int_) {