diff --git a/config/set/php/php80.yaml b/config/set/php/php80.yaml index 67ae41decb13..972be2f606b9 100644 --- a/config/set/php/php80.yaml +++ b/config/set/php/php80.yaml @@ -1,3 +1,4 @@ services: Rector\Php80\Rector\FunctionLike\UnionTypesRector: null Rector\Php80\Rector\NotIdentical\StrContainsRector: null + Rector\Php80\Rector\Identical\StrStartsWithRector: null diff --git a/docs/AllRectorsOverview.md b/docs/AllRectorsOverview.md index 7f75848f27c6..96ba6b4f9b86 100644 --- a/docs/AllRectorsOverview.md +++ b/docs/AllRectorsOverview.md @@ -1,4 +1,4 @@ -# All 499 Rectors Overview +# All 500 Rectors Overview - [Projects](#projects) - [General](#general) @@ -7874,6 +7874,29 @@ Replace strpos() !== false and strstr() with str_contains()
+### `StrStartsWithRector` + +- class: [`Rector\Php80\Rector\Identical\StrStartsWithRector`](/../master/rules/php80/src/Rector/Identical/StrStartsWithRector.php) +- [test fixtures](/../master/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture) + +Change helper functions to str_starts_with() + +```diff + class SomeClass + { + public function run() + { +- $isMatch = substr($haystack, 0, strlen($needle)) === $needle; ++ $isMatch = str_starts_with($haystack, $needle); + +- $isNotMatch = substr($haystack, 0, strlen($needle)) !== $needle; ++ $isMatch = ! str_starts_with($haystack, $needle); + } + } +``` + +
+ ### `UnionTypesRector` - class: [`Rector\Php80\Rector\FunctionLike\UnionTypesRector`](/../master/rules/php80/src/Rector/FunctionLike/UnionTypesRector.php) diff --git a/packages/post-rector/src/Collector/UseNodesToAddCollector.php b/packages/post-rector/src/Collector/UseNodesToAddCollector.php index f243cef093f1..e7c3b7baf577 100644 --- a/packages/post-rector/src/Collector/UseNodesToAddCollector.php +++ b/packages/post-rector/src/Collector/UseNodesToAddCollector.php @@ -6,7 +6,6 @@ use PhpParser\Node; use PHPStan\Type\ObjectType; -use Rector\Core\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PHPStan\Type\AliasedObjectType; @@ -60,7 +59,7 @@ public function addUseImport(Node $positionNode, ObjectType $objectType): void // fallback for freshly created Name nodes $fileInfo = $this->currentFileInfoProvider->getSmartFileInfo(); if ($fileInfo === null) { - throw new ShouldNotHappenException(); + return; } } diff --git a/phpstan.neon b/phpstan.neon index d40e140724af..6a82bff319ae 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -250,4 +250,3 @@ parameters: - '#Method Rector\\Caching\\ChangedFilesDetector\:\:hashFile\(\) should return string but returns string\|false#' - '#If condition is always false#' - - '#Parameter \#1 \$stmts of method Rector\\Core\\PhpParser\\Printer\\BetterStandardPrinter\:\:prettyPrintFile\(\) expects array, PhpParser\\Node\\Stmt\\Class_\|PhpParser\\Node\\Stmt\\Namespace_ given#' diff --git a/rector.yaml b/rector.yaml index 4b204f1d2cb9..3badb40734c4 100644 --- a/rector.yaml +++ b/rector.yaml @@ -5,6 +5,8 @@ parameters: # bleeding edge feature is_cache_enabled: true + auto_import_names: true + paths: - src - tests diff --git a/rules/code-quality/src/Rector/Foreach_/ForeachToInArrayRector.php b/rules/code-quality/src/Rector/Foreach_/ForeachToInArrayRector.php index 3600a8da5923..3d0e7b200ab7 100644 --- a/rules/code-quality/src/Rector/Foreach_/ForeachToInArrayRector.php +++ b/rules/code-quality/src/Rector/Foreach_/ForeachToInArrayRector.php @@ -218,7 +218,7 @@ private function createInArrayFunction(Node $node, BinaryOp $binaryOp, Foreach_ $arguments[] = $this->createArg($this->createTrue()); } - return $this->createFunction('in_array', $arguments); + return $this->createFuncCall('in_array', $arguments); } /** diff --git a/rules/code-quality/src/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php b/rules/code-quality/src/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php index f4b9bdd63bfb..02eba43249cd 100644 --- a/rules/code-quality/src/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php +++ b/rules/code-quality/src/Rector/FuncCall/SimplifyFuncGetArgsCountRector.php @@ -51,6 +51,6 @@ public function refactor(Node $node): ?Node return $node; } - return $this->createFunction('func_num_args'); + return $this->createFuncCall('func_num_args'); } } diff --git a/rules/code-quality/src/Rector/Identical/SimplifyArraySearchRector.php b/rules/code-quality/src/Rector/Identical/SimplifyArraySearchRector.php index cbf1d459f1e1..af10120e6403 100644 --- a/rules/code-quality/src/Rector/Identical/SimplifyArraySearchRector.php +++ b/rules/code-quality/src/Rector/Identical/SimplifyArraySearchRector.php @@ -79,7 +79,7 @@ function (Node $node): bool { /** @var ConstFetch $boolConstFetch */ [$arraySearchFuncCall, $boolConstFetch] = $matchedNodes; - $inArrayFuncCall = $this->createFunction('in_array', [ + $inArrayFuncCall = $this->createFuncCall('in_array', [ $arraySearchFuncCall->args[0], $arraySearchFuncCall->args[1], ]); diff --git a/rules/coding-style/src/Rector/FuncCall/CallUserFuncCallToVariadicRector.php b/rules/coding-style/src/Rector/FuncCall/CallUserFuncCallToVariadicRector.php index 1b85582238f6..bf409e53c562 100644 --- a/rules/coding-style/src/Rector/FuncCall/CallUserFuncCallToVariadicRector.php +++ b/rules/coding-style/src/Rector/FuncCall/CallUserFuncCallToVariadicRector.php @@ -70,6 +70,6 @@ public function refactor(Node $node): ?Node $args = []; $args[] = new Arg($node->args[1]->value, false, true); - return $this->createFunction($functionName, $args); + return $this->createFuncCall($functionName, $args); } } diff --git a/rules/oxid/src/Rector/FuncCall/OxidReplaceBackwardsCompatabilityClassRector.php b/rules/oxid/src/Rector/FuncCall/OxidReplaceBackwardsCompatabilityClassRector.php index c9bc1508b85a..039b86c99a23 100644 --- a/rules/oxid/src/Rector/FuncCall/OxidReplaceBackwardsCompatabilityClassRector.php +++ b/rules/oxid/src/Rector/FuncCall/OxidReplaceBackwardsCompatabilityClassRector.php @@ -45,7 +45,7 @@ public function refactor(Node $node): ?Node if (array_key_exists($classname, $this->getClassmap())) { $newClassname = new ClassConstFetch(new FullyQualified($this->getClassmap()[$classname]), 'class'); - return $this->createFunction('oxNew', array_merge([$newClassname], $arguments)); + return $this->createFuncCall('oxNew', array_merge([$newClassname], $arguments)); } return $node; diff --git a/rules/php70/src/Rector/FuncCall/EregToPregMatchRector.php b/rules/php70/src/Rector/FuncCall/EregToPregMatchRector.php index b2e5fedb3bd8..fc05c16416cc 100644 --- a/rules/php70/src/Rector/FuncCall/EregToPregMatchRector.php +++ b/rules/php70/src/Rector/FuncCall/EregToPregMatchRector.php @@ -115,7 +115,7 @@ private function processStringPattern(FuncCall $funcCall, String_ $patternNode, private function processVariablePattern(FuncCall $funcCall, Variable $variable, string $functionName): void { - $pregQuotePatternNode = $this->createFunction('preg_quote', [ + $pregQuotePatternNode = $this->createFuncCall('preg_quote', [ new Arg($variable), new Arg(new String_('#')), ]); @@ -161,7 +161,7 @@ private function processSplitLimitArgument(FuncCall $funcCall, string $functionN private function createTernaryWithStrlenOfFirstMatch(FuncCall $funcCall): Ternary { $arrayDimFetch = new ArrayDimFetch($funcCall->args[2]->value, new LNumber(0)); - $strlenFuncCall = $this->createFunction('strlen', [$arrayDimFetch]); + $strlenFuncCall = $this->createFuncCall('strlen', [$arrayDimFetch]); return new Ternary($funcCall, $strlenFuncCall, $this->createFalse()); } diff --git a/rules/php70/src/Rector/FuncCall/RandomFunctionRector.php b/rules/php70/src/Rector/FuncCall/RandomFunctionRector.php index 7799815ac8f7..f87fecbaa3bd 100644 --- a/rules/php70/src/Rector/FuncCall/RandomFunctionRector.php +++ b/rules/php70/src/Rector/FuncCall/RandomFunctionRector.php @@ -61,7 +61,7 @@ public function refactor(Node $node): ?Node // special case: random_int(); → random_int(0, getrandmax()); if ($newFunctionName === 'random_int' && count($node->args) === 0) { $node->args[0] = new Arg(new LNumber(0)); - $node->args[1] = new Arg($this->createFunction('mt_getrandmax')); + $node->args[1] = new Arg($this->createFuncCall('mt_getrandmax')); } return $node; diff --git a/rules/php70/src/Rector/List_/ListSplitStringRector.php b/rules/php70/src/Rector/List_/ListSplitStringRector.php index f660babd2b1e..577968aa23c9 100644 --- a/rules/php70/src/Rector/List_/ListSplitStringRector.php +++ b/rules/php70/src/Rector/List_/ListSplitStringRector.php @@ -49,7 +49,7 @@ public function refactor(Node $node): ?Node return null; } - $node->expr = $this->createFunction('str_split', [$node->expr]); + $node->expr = $this->createFuncCall('str_split', [$node->expr]); return $node; } diff --git a/rules/php70/src/Rector/List_/ListSwapArrayOrderRector.php b/rules/php70/src/Rector/List_/ListSwapArrayOrderRector.php index a760f087a507..51ba71b1cdf0 100644 --- a/rules/php70/src/Rector/List_/ListSwapArrayOrderRector.php +++ b/rules/php70/src/Rector/List_/ListSwapArrayOrderRector.php @@ -69,7 +69,7 @@ public function refactor(Node $node): ?Node } // wrap with array_reverse, to reflect reverse assign order in left - $node->expr = $this->createFunction('array_reverse', [$node->expr]); + $node->expr = $this->createFuncCall('array_reverse', [$node->expr]); return $node; } diff --git a/rules/php71/src/Rector/FuncCall/CountOnNullRector.php b/rules/php71/src/Rector/FuncCall/CountOnNullRector.php index e97d7887beb4..78242fdc19c8 100644 --- a/rules/php71/src/Rector/FuncCall/CountOnNullRector.php +++ b/rules/php71/src/Rector/FuncCall/CountOnNullRector.php @@ -82,7 +82,7 @@ public function refactor(Node $node): ?Node $conditionNode = new FuncCall(new Name('is_countable'), [new Arg($countedNode)]); } else { $conditionNode = new BooleanOr( - $this->createFunction('is_array', [new Arg($countedNode)]), + $this->createFuncCall('is_array', [new Arg($countedNode)]), new Instanceof_($countedNode, new FullyQualified('Countable')) ); } diff --git a/rules/php72/src/Rector/Each/ListEachRector.php b/rules/php72/src/Rector/Each/ListEachRector.php index 0eb90fa8202a..4d8ff51cd5aa 100644 --- a/rules/php72/src/Rector/Each/ListEachRector.php +++ b/rules/php72/src/Rector/Each/ListEachRector.php @@ -75,16 +75,16 @@ public function refactor(Node $node): ?Node // only key: list($key, ) = each($values); if ($listNode->items[0] && $listNode->items[1] === null) { - $keyFuncCall = $this->createFunction('key', $eachFuncCall->args); + $keyFuncCall = $this->createFuncCall('key', $eachFuncCall->args); return new Assign($listNode->items[0]->value, $keyFuncCall); } // only value: list(, $value) = each($values); if ($listNode->items[1] && $listNode->items[0] === null) { - $nextFuncCall = $this->createFunction('next', $eachFuncCall->args); + $nextFuncCall = $this->createFuncCall('next', $eachFuncCall->args); $this->addNodeAfterNode($nextFuncCall, $node); - $currentFuncCall = $this->createFunction('current', $eachFuncCall->args); + $currentFuncCall = $this->createFuncCall('current', $eachFuncCall->args); return new Assign($listNode->items[1]->value, $currentFuncCall); } @@ -93,16 +93,16 @@ public function refactor(Node $node): ?Node // $key = key($values); // $value = current($values); // next($values); - only inside a loop - $currentFuncCall = $this->createFunction('current', $eachFuncCall->args); + $currentFuncCall = $this->createFuncCall('current', $eachFuncCall->args); $assignCurrentNode = new Assign($listNode->items[1]->value, $currentFuncCall); $this->addNodeAfterNode($assignCurrentNode, $node); if ($this->isInsideDoWhile($node)) { - $nextFuncCall = $this->createFunction('next', $eachFuncCall->args); + $nextFuncCall = $this->createFuncCall('next', $eachFuncCall->args); $this->addNodeAfterNode($nextFuncCall, $node); } - $keyFuncCall = $this->createFunction('key', $eachFuncCall->args); + $keyFuncCall = $this->createFuncCall('key', $eachFuncCall->args); return new Assign($listNode->items[0]->value, $keyFuncCall); } diff --git a/rules/php72/src/Rector/Each/WhileEachToForeachRector.php b/rules/php72/src/Rector/Each/WhileEachToForeachRector.php index 825f126e4e6d..4fc0dca3d5d8 100644 --- a/rules/php72/src/Rector/Each/WhileEachToForeachRector.php +++ b/rules/php72/src/Rector/Each/WhileEachToForeachRector.php @@ -95,7 +95,7 @@ public function refactor(Node $node): ?Node /** @var List_ $listNode */ $listNode = $assignNode->var; - $foreachedExpr = count($listNode->items) === 1 ? $this->createFunction( + $foreachedExpr = count($listNode->items) === 1 ? $this->createFuncCall( 'array_keys', [$eachFuncCall->args[0]] ) : $eachFuncCall->args[0]->value; diff --git a/rules/php72/src/Rector/Unset_/UnsetCastRector.php b/rules/php72/src/Rector/Unset_/UnsetCastRector.php index 950a5b24188a..9afbc3548ea9 100644 --- a/rules/php72/src/Rector/Unset_/UnsetCastRector.php +++ b/rules/php72/src/Rector/Unset_/UnsetCastRector.php @@ -55,7 +55,7 @@ public function refactor(Node $node): ?Node $unset = $node->expr; if ($this->areNodesEqual($node->var, $unset->expr)) { - return $this->createFunction('unset', [$node->var]); + return $this->createFuncCall('unset', [$node->var]); } } diff --git a/rules/php80/config/config.yaml b/rules/php80/config/config.yaml new file mode 100644 index 000000000000..c653ef55db07 --- /dev/null +++ b/rules/php80/config/config.yaml @@ -0,0 +1,10 @@ +services: + _defaults: + public: true + autowire: true + + Rector\Php80\: + resource: '../src' + exclude: + - '../src/Rector/**/*Rector.php' + - '../src/ValueObject/*' diff --git a/rules/php80/src/MatchAndRefactor/AbstractMatchAndRefactor.php b/rules/php80/src/MatchAndRefactor/AbstractMatchAndRefactor.php new file mode 100644 index 000000000000..682078a5f2ae --- /dev/null +++ b/rules/php80/src/MatchAndRefactor/AbstractMatchAndRefactor.php @@ -0,0 +1,51 @@ +nodeNameResolver = $nodeNameResolver; + $this->valueResolver = $valueResolver; + $this->betterStandardPrinter = $betterStandardPrinter; + } + + protected function isFuncCallName(Node $node, string $name): bool + { + if (! $node instanceof FuncCall) { + return false; + } + + return $this->nodeNameResolver->isName($node, $name); + } +} diff --git a/rules/php80/src/MatchAndRefactor/StrncmpMatchAndRefactor.php b/rules/php80/src/MatchAndRefactor/StrncmpMatchAndRefactor.php new file mode 100644 index 000000000000..0a08b5131253 --- /dev/null +++ b/rules/php80/src/MatchAndRefactor/StrncmpMatchAndRefactor.php @@ -0,0 +1,80 @@ +isFuncCallName($binaryOp->left, self::FUNCTION_NAME)) { + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->left; + return new StrncmpFuncCallToHaystack($funcCall, $isPositive); + } + + if ($this->isFuncCallName($binaryOp->right, self::FUNCTION_NAME)) { + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->right; + return new StrncmpFuncCallToHaystack($funcCall, $isPositive); + } + + return null; + } + + public function refactor(StrncmpFuncCallToHaystack $strncmpFuncCallToHaystack): ?Node + { + $strncmpFuncCall = $strncmpFuncCallToHaystack->getStrncmpFuncCall(); + + $needleExpr = $strncmpFuncCall->args[1]->value; + + if (! $this->isFuncCallName($strncmpFuncCall->args[2]->value, 'strlen')) { + return null; + } + + /** @var FuncCall $strlenFuncCall */ + $strlenFuncCall = $strncmpFuncCall->args[2]->value; + $strlenArgumentValue = $strlenFuncCall->args[0]->value; + + if (! $this->betterStandardPrinter->areNodesEqual($needleExpr, $strlenArgumentValue)) { + return null; + } + + $strStartsWith = $this->createStrStartsWith($strncmpFuncCall, $needleExpr); + if ($strncmpFuncCallToHaystack->isPositive()) { + return $strStartsWith; + } + + return new BooleanNot($strStartsWith); + } + + private function createStrStartsWith(FuncCall $strncmpFuncCall, Expr $needleExpr): FuncCall + { + $haystackExpr = $strncmpFuncCall->args[0]->value; + $args = [new Arg($haystackExpr), new Arg($needleExpr)]; + + return new FuncCall(new Name('str_starts_with'), $args); + } +} diff --git a/rules/php80/src/MatchAndRefactor/StrposMatchAndRefactor.php b/rules/php80/src/MatchAndRefactor/StrposMatchAndRefactor.php new file mode 100644 index 000000000000..d5ac654d1feb --- /dev/null +++ b/rules/php80/src/MatchAndRefactor/StrposMatchAndRefactor.php @@ -0,0 +1,52 @@ +isFuncCallName($binaryOp->left, 'strpos')) { + if (! $this->valueResolver->isValue($binaryOp->right, 0)) { + return null; + } + + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->left; + return new StrposFuncCallToZero($funcCall, $isPositive); + } + + if ($this->isFuncCallName($binaryOp->right, 'strpos')) { + if (! $this->valueResolver->isValue($binaryOp->left, 0)) { + return null; + } + + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->right; + return new StrposFuncCallToZero($funcCall, $isPositive); + } + + return null; + } + + public function refactor(StrposFuncCallToZero $strposFuncCallToZero): FuncCall + { + $strposFuncCall = $strposFuncCallToZero->getStrposFuncCall(); + $strposFuncCall->name = new Name('str_starts_with'); + return $strposFuncCall; + } +} diff --git a/rules/php80/src/MatchAndRefactor/SubstrMatchAndRefactor.php b/rules/php80/src/MatchAndRefactor/SubstrMatchAndRefactor.php new file mode 100644 index 000000000000..de1d425f3052 --- /dev/null +++ b/rules/php80/src/MatchAndRefactor/SubstrMatchAndRefactor.php @@ -0,0 +1,78 @@ +isFuncCallName($binaryOp->left, 'substr')) { + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->left; + return new SubstrFuncCallToHaystack($funcCall, $binaryOp->right, $isPositive); + } + + if ($this->isFuncCallName($binaryOp->right, 'substr')) { + /** @var FuncCall $funcCall */ + $funcCall = $binaryOp->right; + return new SubstrFuncCallToHaystack($funcCall, $binaryOp->left, $isPositive); + } + + return null; + } + + public function refactor(SubstrFuncCallToHaystack $substrFuncCallToHaystack): ?Node + { + $substrFuncCall = $substrFuncCallToHaystack->getSubstrFuncCall(); + if (! $this->valueResolver->isValue($substrFuncCall->args[1]->value, 0)) { + return null; + } + + if (! $this->isFuncCallName($substrFuncCall->args[2]->value, 'strlen')) { + return null; + } + + /** @var FuncCall $strlenFuncCall */ + $strlenFuncCall = $substrFuncCall->args[2]->value; + $needleExpr = $strlenFuncCall->args[0]->value; + + $comparedExpr = $substrFuncCallToHaystack->getHaystackExpr(); + if (! $this->betterStandardPrinter->areNodesEqual($needleExpr, $comparedExpr)) { + return null; + } + + $strStartsWith = $this->createStrStartsWith($substrFuncCall, $needleExpr); + if ($substrFuncCallToHaystack->isPositive()) { + return $strStartsWith; + } + + return new BooleanNot($strStartsWith); + } + + private function createStrStartsWith(FuncCall $substrFuncCall, Expr $needleExpr): FuncCall + { + $haystackExpr = $substrFuncCall->args[0]->value; + + $args = [new Arg($haystackExpr), new Arg($needleExpr)]; + + return new FuncCall(new Name('str_starts_with'), $args); + } +} diff --git a/rules/php80/src/Rector/Identical/StrStartsWithRector.php b/rules/php80/src/Rector/Identical/StrStartsWithRector.php new file mode 100644 index 000000000000..88366654b08c --- /dev/null +++ b/rules/php80/src/Rector/Identical/StrStartsWithRector.php @@ -0,0 +1,117 @@ +strncmpMatchAndRefactor = $strncmpMatchAndRefactor; + $this->strposMatchAndRefactor = $strposMatchAndRefactor; + $this->substrMatchAndRefactor = $substrMatchAndRefactor; + } + + public function getDefinition(): RectorDefinition + { + return new RectorDefinition('Change helper functions to str_starts_with()', [ + new CodeSample( + <<<'PHP' +class SomeClass +{ + public function run() + { + $isMatch = substr($haystack, 0, strlen($needle)) === $needle; + + $isNotMatch = substr($haystack, 0, strlen($needle)) !== $needle; + } +} +PHP +, + <<<'PHP' +class SomeClass +{ + public function run() + { + $isMatch = str_starts_with($haystack, $needle); + + $isMatch = ! str_starts_with($haystack, $needle); + } +} +PHP + + ), + ]); + } + + /** + * @return string[] + */ + public function getNodeTypes(): array + { + return [Identical::class, NotIdentical::class]; + } + + /** + * @param Identical|NotIdentical $node + */ + public function refactor(Node $node): ?Node + { + // 1. substr + $substrFuncCallToHaystack = $this->substrMatchAndRefactor->match($node); + if ($substrFuncCallToHaystack !== null) { + return $this->substrMatchAndRefactor->refactor($substrFuncCallToHaystack); + } + + // 2. strpos + $strposFuncCallToZero = $this->strposMatchAndRefactor->match($node); + if ($strposFuncCallToZero !== null) { + return $this->strposMatchAndRefactor->refactor($strposFuncCallToZero); + } + + // 3. strcmp + $strcmpFuncCallToHaystack = $this->strncmpMatchAndRefactor->match($node); + if ($strcmpFuncCallToHaystack !== null) { + return $this->strncmpMatchAndRefactor->refactor($strcmpFuncCallToHaystack); + } + + return $node; + } +} diff --git a/rules/php80/src/Rector/NotIdentical/StrContainsRector.php b/rules/php80/src/Rector/NotIdentical/StrContainsRector.php index cebb61bf2615..c685c4fd394c 100644 --- a/rules/php80/src/Rector/NotIdentical/StrContainsRector.php +++ b/rules/php80/src/Rector/NotIdentical/StrContainsRector.php @@ -5,6 +5,7 @@ namespace Rector\Php80\Rector\NotIdentical; use PhpParser\Node; +use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotIdentical; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Name; @@ -58,7 +59,7 @@ public function run() */ public function getNodeTypes(): array { - return [NotIdentical::class]; + return [NotIdentical::class, Identical::class]; } /** diff --git a/rules/php80/src/ValueObject/StrncmpFuncCallToHaystack.php b/rules/php80/src/ValueObject/StrncmpFuncCallToHaystack.php new file mode 100644 index 000000000000..d5ab8329dc2a --- /dev/null +++ b/rules/php80/src/ValueObject/StrncmpFuncCallToHaystack.php @@ -0,0 +1,36 @@ +strncmpFuncCall = $strncmpFuncCall; + $this->isPositive = $isPositive; + } + + public function getStrncmpFuncCall(): FuncCall + { + return $this->strncmpFuncCall; + } + + public function isPositive(): bool + { + return $this->isPositive; + } +} diff --git a/rules/php80/src/ValueObject/StrposFuncCallToZero.php b/rules/php80/src/ValueObject/StrposFuncCallToZero.php new file mode 100644 index 000000000000..960c28c6086e --- /dev/null +++ b/rules/php80/src/ValueObject/StrposFuncCallToZero.php @@ -0,0 +1,36 @@ +strposFuncCall = $strposFuncCall; + $this->isPositive = $isPositive; + } + + public function getStrposFuncCall(): FuncCall + { + return $this->strposFuncCall; + } + + public function isPositive(): bool + { + return $this->isPositive; + } +} diff --git a/rules/php80/src/ValueObject/SubstrFuncCallToHaystack.php b/rules/php80/src/ValueObject/SubstrFuncCallToHaystack.php new file mode 100644 index 000000000000..de786a5f1077 --- /dev/null +++ b/rules/php80/src/ValueObject/SubstrFuncCallToHaystack.php @@ -0,0 +1,48 @@ +substrFuncCall = $substrFuncCall; + $this->haystackExpr = $haystackExpr; + $this->isPositive = $isPositive; + } + + public function getSubstrFuncCall(): FuncCall + { + return $this->substrFuncCall; + } + + public function getHaystackExpr(): Expr + { + return $this->haystackExpr; + } + + public function isPositive(): bool + { + return $this->isPositive; + } +} diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/fixture.php.inc b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/fixture.php.inc new file mode 100644 index 000000000000..6f8aaa4ede05 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/fixture.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/not_identical_substr.php.inc b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/not_identical_substr.php.inc new file mode 100644 index 000000000000..17faacebaee1 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/not_identical_substr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/other_side.php.inc b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/other_side.php.inc new file mode 100644 index 000000000000..2d4e19ee6b69 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/other_side.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/skip_non_zero.php.inc b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/skip_non_zero.php.inc new file mode 100644 index 000000000000..c3e8a37843b6 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/skip_non_zero.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/strpos.php.inc b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/strpos.php.inc new file mode 100644 index 000000000000..ee4b979b28e8 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/strpos.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/strpos_other_side.php.inc b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/strpos_other_side.php.inc new file mode 100644 index 000000000000..4700a579c705 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/Fixture/strpos_other_side.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules/php80/tests/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php b/rules/php80/tests/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php new file mode 100644 index 000000000000..4e21647ec576 --- /dev/null +++ b/rules/php80/tests/Rector/Identical/StrStartsWithRector/StrStartsWithRectorTest.php @@ -0,0 +1,30 @@ +doTestFile($file); + } + + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + protected function getRectorClass(): string + { + return StrStartsWithRector::class; + } +} diff --git a/rules/php80/tests/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc b/rules/php80/tests/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc new file mode 100644 index 000000000000..20f5fe327807 --- /dev/null +++ b/rules/php80/tests/Rector/NotIdentical/StrContainsRector/Fixture/the_other_way.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules/strict-code-quality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php b/rules/strict-code-quality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php index 544f240ed91e..ccb6411b9af0 100644 --- a/rules/strict-code-quality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php +++ b/rules/strict-code-quality/src/Rector/Stmt/VarInlineAnnotationToAssertRector.php @@ -184,27 +184,27 @@ private function createFuncCallBasedOnType(Type $type, Variable $variable): ?Fun { if ($type instanceof ObjectType) { $instanceOf = new Instanceof_($variable, new FullyQualified($type->getClassName())); - return $this->createFunction('assert', [$instanceOf]); + return $this->createFuncCall('assert', [$instanceOf]); } if ($type instanceof IntegerType) { - $isInt = $this->createFunction('is_int', [$variable]); - return $this->createFunction('assert', [$isInt]); + $isInt = $this->createFuncCall('is_int', [$variable]); + return $this->createFuncCall('assert', [$isInt]); } if ($type instanceof FloatType) { - $isFloat = $this->createFunction('is_float', [$variable]); - return $this->createFunction('assert', [$isFloat]); + $isFloat = $this->createFuncCall('is_float', [$variable]); + return $this->createFuncCall('assert', [$isFloat]); } if ($type instanceof StringType) { - $isString = $this->createFunction('is_string', [$variable]); - return $this->createFunction('assert', [$isString]); + $isString = $this->createFuncCall('is_string', [$variable]); + return $this->createFuncCall('assert', [$isString]); } if ($type instanceof BooleanType) { - $isInt = $this->createFunction('is_bool', [$variable]); - return $this->createFunction('assert', [$isInt]); + $isInt = $this->createFuncCall('is_bool', [$variable]); + return $this->createFuncCall('assert', [$isInt]); } return null; diff --git a/rules/symfony/src/Rector/Yaml/ParseFileRector.php b/rules/symfony/src/Rector/Yaml/ParseFileRector.php index a462e953070d..8d0a7a9c3483 100644 --- a/rules/symfony/src/Rector/Yaml/ParseFileRector.php +++ b/rules/symfony/src/Rector/Yaml/ParseFileRector.php @@ -54,7 +54,7 @@ public function refactor(Node $node): ?Node return null; } - $fileGetContentsFunCallNode = $this->createFunction('file_get_contents', [$node->args[0]]); + $fileGetContentsFunCallNode = $this->createFuncCall('file_get_contents', [$node->args[0]]); $node->args[0] = new Arg($fileGetContentsFunCallNode); return $node; diff --git a/src/Rector/AbstractRector/NodeFactoryTrait.php b/src/Rector/AbstractRector/NodeFactoryTrait.php index 609741bf03eb..c241b769f597 100644 --- a/src/Rector/AbstractRector/NodeFactoryTrait.php +++ b/src/Rector/AbstractRector/NodeFactoryTrait.php @@ -99,7 +99,7 @@ protected function createArray(array $nodes): Array_ /** * @param mixed[] $arguments */ - protected function createFunction(string $name, array $arguments = []): FuncCall + protected function createFuncCall(string $name, array $arguments = []): FuncCall { $arguments = $this->createArgs($arguments); diff --git a/src/Testing/PHPUnit/AbstractRectorTestCase.php b/src/Testing/PHPUnit/AbstractRectorTestCase.php index 87faf43dc82c..a228b917f88b 100644 --- a/src/Testing/PHPUnit/AbstractRectorTestCase.php +++ b/src/Testing/PHPUnit/AbstractRectorTestCase.php @@ -124,9 +124,9 @@ protected function doTestFileWithoutAutoload(string $file): void $this->autoloadTestFixture = true; } - protected function doTestFile(string $file): void + protected function doTestFile(string $fixtureFile): void { - $smartFileInfo = new SmartFileInfo($file); + $smartFileInfo = new SmartFileInfo($fixtureFile); [$originalFile, $changedFile] = $this->fixtureSplitter->splitContentToOriginalFileAndExpectedFile( $smartFileInfo, $this->autoloadTestFixture @@ -134,7 +134,7 @@ protected function doTestFile(string $file): void $this->nodeScopeResolver->setAnalysedFiles([$originalFile]); - $this->doTestFileMatchesExpectedContent($originalFile, $changedFile, $originalFile); + $this->doTestFileMatchesExpectedContent($originalFile, $changedFile, $fixtureFile); } protected function getTempPath(): string @@ -258,9 +258,6 @@ private function doTestFileMatchesExpectedContent( private function createCausedByFixtureMessage(string $fixtureFile): string { - $fixtureSmartFileInfo = new SmartFileInfo($fixtureFile); - $relativeFixtureFilePath = $fixtureSmartFileInfo->getRelativeFilePathFromCwd(); - - return 'Caused by ' . $relativeFixtureFilePath; + return (new SmartFileInfo($fixtureFile))->getRelativeFilePathFromCwd(); } } diff --git a/utils/doctrine-annotation-parser-syncer/src/Command/SyncAnnotationParserCommand.php b/utils/doctrine-annotation-parser-syncer/src/Command/SyncAnnotationParserCommand.php index 0977555d012a..76f4c9e33220 100644 --- a/utils/doctrine-annotation-parser-syncer/src/Command/SyncAnnotationParserCommand.php +++ b/utils/doctrine-annotation-parser-syncer/src/Command/SyncAnnotationParserCommand.php @@ -13,6 +13,7 @@ use Symfony\Component\Console\Style\SymfonyStyle; use Symplify\PackageBuilder\Console\Command\CommandNaming; use Symplify\PackageBuilder\Console\ShellCode; +use Symplify\PackageBuilder\Parameter\ParameterProvider; use Symplify\SmartFileSystem\SmartFileInfo; final class SyncAnnotationParserCommand extends Command @@ -30,12 +31,15 @@ final class SyncAnnotationParserCommand extends Command /** * @param ClassSyncerInterface[] $classSyncers */ - public function __construct(array $classSyncers, SymfonyStyle $symfonyStyle) + public function __construct(array $classSyncers, SymfonyStyle $symfonyStyle, ParameterProvider $parameterProvider) { parent::__construct(); $this->symfonyStyle = $symfonyStyle; $this->classSyncers = $classSyncers; + + // disable imports + $parameterProvider->changeParameter(Option::AUTO_IMPORT_NAMES, false); } protected function configure(): void