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