From afa493f5c0350f6372a44c501e5aa58d332700a7 Mon Sep 17 00:00:00 2001 From: Guillaume Sainthillier Date: Mon, 12 Oct 2026 12:05:24 +0200 Subject: [PATCH 1/3] [Php80] Skip loose comparisons that differ from strict on StrStartsWithRector and StrEndsWithRector --- .../Fixture/fixture_equal.php.inc | 31 ------------------- .../Fixture/skip_loose_numeric_string.php.inc | 14 +++++++++ .../Fixture/skip_loose_substr.php.inc | 15 +++++++++ .../skip_loose_substr_not_prefix.php.inc | 15 +++++++++ .../Fixture/substr_not_equal_prefix.php.inc | 31 ------------------- .../Fixture/fixture_equal.php.inc | 27 ---------------- .../Fixture/not_equal_substr.php.inc | 27 ---------------- .../Fixture/other_side_equal.php.inc | 31 ------------------- .../skip_loose_not_equal_substr.php.inc | 13 ++++++++ .../Fixture/skip_loose_numeric_string.php.inc | 14 +++++++++ .../Fixture/skip_loose_strpos.php.inc | 13 ++++++++ .../skip_loose_strpos_hardcoded.php.inc | 13 ++++++++ .../skip_loose_strpos_not_prefix.php.inc | 13 ++++++++ .../skip_loose_strpos_other_side.php.inc | 13 ++++++++ .../Fixture/skip_loose_substr.php.inc | 13 ++++++++ .../skip_loose_substr_other_side.php.inc | 15 +++++++++ .../Fixture/strpos_equal.php.inc | 27 ---------------- .../Fixture/strpos_equal_not_prefix.php.inc | 27 ---------------- .../Fixture/strpos_equal_other_side.php.inc | 27 ---------------- .../Fixture/strpos_hardcoded_equal.php.inc | 27 ---------------- .../StrposMatchAndRefactor.php | 7 ++++- .../SubstrMatchAndRefactor.php | 23 ++++++++++++++ .../Rector/Identical/StrEndsWithRector.php | 16 ++++++++++ 23 files changed, 196 insertions(+), 256 deletions(-) delete mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_numeric_string.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_numeric_string.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_hardcoded.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_not_prefix.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_other_side.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc deleted file mode 100644 index 8c7b7b41348..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/fixture_equal.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_numeric_string.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_numeric_string.php.inc new file mode 100644 index 00000000000..be2ba58390b --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_numeric_string.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc new file mode 100644 index 00000000000..083936b940a --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc new file mode 100644 index 00000000000..b39ce0b5d33 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc deleted file mode 100644 index 5453a79542c..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/substr_not_equal_prefix.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc deleted file mode 100644 index 5f87efcd522..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/fixture_equal.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc deleted file mode 100644 index f48e15217a1..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/not_equal_substr.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc deleted file mode 100644 index 70ca84ee897..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/other_side_equal.php.inc +++ /dev/null @@ -1,31 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc new file mode 100644 index 00000000000..8b8a9ddf1f2 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_numeric_string.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_numeric_string.php.inc new file mode 100644 index 00000000000..641996d5f30 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_numeric_string.php.inc @@ -0,0 +1,14 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos.php.inc new file mode 100644 index 00000000000..1b91f7a0681 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_hardcoded.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_hardcoded.php.inc new file mode 100644 index 00000000000..66108c96325 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_hardcoded.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_not_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_not_prefix.php.inc new file mode 100644 index 00000000000..044ad29bbae --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_not_prefix.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_other_side.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_other_side.php.inc new file mode 100644 index 00000000000..76cd1b6f3ee --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_strpos_other_side.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc new file mode 100644 index 00000000000..e9f11181bd3 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc @@ -0,0 +1,13 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc new file mode 100644 index 00000000000..8bda0422d86 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc @@ -0,0 +1,15 @@ + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc deleted file mode 100644 index cd671374ff5..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc deleted file mode 100644 index 36f6112dbee..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_not_prefix.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc deleted file mode 100644 index 1e959bb197d..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_equal_other_side.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc deleted file mode 100644 index a19bf55f32e..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/strpos_hardcoded_equal.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php index 55912c58310..a38f3d902d5 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/StrposMatchAndRefactor.php @@ -30,7 +30,12 @@ public function __construct( public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStartsWith { - $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; + // strpos() returns false on no match, and false == 0 + if ($binaryOp instanceof Equal || $binaryOp instanceof NotEqual) { + return null; + } + + $isPositive = $binaryOp instanceof Identical; if ($binaryOp->left instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->left, 'strpos')) { return $this->processBinaryOpLeft($binaryOp, $isPositive); diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php index 344f24d4268..272b7e7d58c 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php @@ -5,6 +5,7 @@ namespace Rector\Php80\MatchAndRefactor\StrStartsWithMatchAndRefactor; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\BinaryOp\Equal; use PhpParser\Node\Expr\BinaryOp\Identical; use PhpParser\Node\Expr\BinaryOp\NotEqual; @@ -34,6 +35,10 @@ public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStar $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; if ($binaryOp->left instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->left, 'substr')) { + if (! $this->isStrictOrNonNumericStringComparison($binaryOp, $binaryOp->right)) { + return null; + } + $funcCall = $binaryOp->left; $haystack = $funcCall->getArgs()[0] @@ -43,6 +48,10 @@ public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStar } if ($binaryOp->right instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->right, 'substr')) { + if (! $this->isStrictOrNonNumericStringComparison($binaryOp, $binaryOp->left)) { + return null; + } + $funcCall = $binaryOp->right; $haystack = $funcCall->getArgs()[0] @@ -127,4 +136,18 @@ private function isHardcodedStringWithLNumberLength(StrStartsWith $strStartsWith return $lNumberLength->value === strlen($expr->value); } + + /** + * Loose comparison of two numeric strings is numeric, e.g. '+1' == '01' + */ + private function isStrictOrNonNumericStringComparison( + Identical|NotIdentical|Equal|NotEqual $binaryOp, + Expr $comparedExpr + ): bool { + if (! $binaryOp instanceof Equal && ! $binaryOp instanceof NotEqual) { + return true; + } + + return $comparedExpr instanceof String_ && ! is_numeric($comparedExpr->value); + } } diff --git a/rules/Php80/Rector/Identical/StrEndsWithRector.php b/rules/Php80/Rector/Identical/StrEndsWithRector.php index eac7487378d..dc8338b2323 100644 --- a/rules/Php80/Rector/Identical/StrEndsWithRector.php +++ b/rules/Php80/Rector/Identical/StrEndsWithRector.php @@ -142,6 +142,10 @@ private function refactorSubstr(BinaryOp $binaryOp): FuncCall|BooleanNot|null return null; } + if (! $this->isStrictOrNonNumericStringComparison($binaryOp, $comparedNeedleExpr)) { + return null; + } + if (count($substrFuncCall->getArgs()) < 2) { return null; } @@ -252,6 +256,18 @@ private function isHardCodedLNumberAndString(Expr $substrOffset, Expr $needle): return $lNumber->value === strlen($needle->value); } + /** + * Loose comparison of two numeric strings is numeric, e.g. '+1' == '01' + */ + private function isStrictOrNonNumericStringComparison(BinaryOp $binaryOp, Expr $comparedExpr): bool + { + if (! $binaryOp instanceof Equal && ! $binaryOp instanceof NotEqual) { + return true; + } + + return $comparedExpr instanceof String_ && ! is_numeric($comparedExpr->value); + } + private function buildReturnNode(Expr $haystack, Expr $needle, bool $isPositive): FuncCall|BooleanNot { $funcCall = $this->nodeFactory->createFuncCall('str_ends_with', [$haystack, $needle]); From 1e523d729628f739757bedabc2d36da05d2eab4c Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 12 Oct 2026 13:28:18 +0200 Subject: [PATCH 2/3] Keep loose substr comparison with variable needle, skip only numeric string literal --- .../Fixture/loose_substr.php.inc | 31 +++++++++++++++++++ .../Fixture/loose_substr_not_prefix.php.inc | 31 +++++++++++++++++++ .../Fixture/skip_loose_substr.php.inc | 15 --------- .../skip_loose_substr_not_prefix.php.inc | 15 --------- .../Fixture/loose_not_equal_substr.php.inc | 27 ++++++++++++++++ .../Fixture/loose_substr.php.inc | 27 ++++++++++++++++ .../Fixture/loose_substr_other_side.php.inc | 31 +++++++++++++++++++ .../skip_loose_not_equal_substr.php.inc | 13 -------- .../Fixture/skip_loose_substr.php.inc | 13 -------- .../skip_loose_substr_other_side.php.inc | 15 --------- .../SubstrMatchAndRefactor.php | 11 ++++--- .../Rector/Identical/StrEndsWithRector.php | 9 +++--- 12 files changed, 158 insertions(+), 80 deletions(-) create mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr_not_prefix.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_not_equal_substr.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr.php.inc create mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr_other_side.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc delete mode 100644 rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr.php.inc new file mode 100644 index 00000000000..8975acc6dc6 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr_not_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr_not_prefix.php.inc new file mode 100644 index 00000000000..870389c18c1 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/loose_substr_not_prefix.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc deleted file mode 100644 index 083936b940a..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc b/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc deleted file mode 100644 index b39ce0b5d33..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrEndsWithRector/Fixture/skip_loose_substr_not_prefix.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_not_equal_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_not_equal_substr.php.inc new file mode 100644 index 00000000000..9061e790ada --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_not_equal_substr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr.php.inc new file mode 100644 index 00000000000..ca62c06e012 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr_other_side.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr_other_side.php.inc new file mode 100644 index 00000000000..254589cfa36 --- /dev/null +++ b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/loose_substr_other_side.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc deleted file mode 100644 index 8b8a9ddf1f2..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_not_equal_substr.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc deleted file mode 100644 index e9f11181bd3..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - diff --git a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc b/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc deleted file mode 100644 index 8bda0422d86..00000000000 --- a/rules-tests/Php80/Rector/Identical/StrStartsWithRector/Fixture/skip_loose_substr_other_side.php.inc +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php index 272b7e7d58c..3cfaf62dcaf 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php @@ -35,7 +35,7 @@ public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStar $isPositive = $binaryOp instanceof Identical || $binaryOp instanceof Equal; if ($binaryOp->left instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->left, 'substr')) { - if (! $this->isStrictOrNonNumericStringComparison($binaryOp, $binaryOp->right)) { + if (! $this->isRefactorableLooseComparison($binaryOp, $binaryOp->right)) { return null; } @@ -48,7 +48,7 @@ public function match(Identical|NotIdentical|Equal|NotEqual $binaryOp): ?StrStar } if ($binaryOp->right instanceof FuncCall && $this->nodeNameResolver->isName($binaryOp->right, 'substr')) { - if (! $this->isStrictOrNonNumericStringComparison($binaryOp, $binaryOp->left)) { + if (! $this->isRefactorableLooseComparison($binaryOp, $binaryOp->left)) { return null; } @@ -138,9 +138,10 @@ private function isHardcodedStringWithLNumberLength(StrStartsWith $strStartsWith } /** - * Loose comparison of two numeric strings is numeric, e.g. '+1' == '01' + * Loose comparison of two numeric string literals is numeric, e.g. '+1' == '01'; + * skip only that case, a variable needle is kept */ - private function isStrictOrNonNumericStringComparison( + private function isRefactorableLooseComparison( Identical|NotIdentical|Equal|NotEqual $binaryOp, Expr $comparedExpr ): bool { @@ -148,6 +149,6 @@ private function isStrictOrNonNumericStringComparison( return true; } - return $comparedExpr instanceof String_ && ! is_numeric($comparedExpr->value); + return ! ($comparedExpr instanceof String_ && is_numeric($comparedExpr->value)); } } diff --git a/rules/Php80/Rector/Identical/StrEndsWithRector.php b/rules/Php80/Rector/Identical/StrEndsWithRector.php index dc8338b2323..7333ad92871 100644 --- a/rules/Php80/Rector/Identical/StrEndsWithRector.php +++ b/rules/Php80/Rector/Identical/StrEndsWithRector.php @@ -142,7 +142,7 @@ private function refactorSubstr(BinaryOp $binaryOp): FuncCall|BooleanNot|null return null; } - if (! $this->isStrictOrNonNumericStringComparison($binaryOp, $comparedNeedleExpr)) { + if (! $this->isRefactorableLooseComparison($binaryOp, $comparedNeedleExpr)) { return null; } @@ -257,15 +257,16 @@ private function isHardCodedLNumberAndString(Expr $substrOffset, Expr $needle): } /** - * Loose comparison of two numeric strings is numeric, e.g. '+1' == '01' + * Loose comparison of two numeric string literals is numeric, e.g. '+1' == '01'; + * skip only that case, a variable needle is kept */ - private function isStrictOrNonNumericStringComparison(BinaryOp $binaryOp, Expr $comparedExpr): bool + private function isRefactorableLooseComparison(BinaryOp $binaryOp, Expr $comparedExpr): bool { if (! $binaryOp instanceof Equal && ! $binaryOp instanceof NotEqual) { return true; } - return $comparedExpr instanceof String_ && ! is_numeric($comparedExpr->value); + return ! ($comparedExpr instanceof String_ && is_numeric($comparedExpr->value)); } private function buildReturnNode(Expr $haystack, Expr $needle, bool $isPositive): FuncCall|BooleanNot From b38a8844506bac999201a0359d8a38dddd8c29f6 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 12 Oct 2026 11:29:43 +0000 Subject: [PATCH 3/3] [ci-review] Rector Rectify --- .../StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php | 2 +- rules/Php80/Rector/Identical/StrEndsWithRector.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php index 3cfaf62dcaf..4b9f199877f 100644 --- a/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php +++ b/rules/Php80/MatchAndRefactor/StrStartsWithMatchAndRefactor/SubstrMatchAndRefactor.php @@ -149,6 +149,6 @@ private function isRefactorableLooseComparison( return true; } - return ! ($comparedExpr instanceof String_ && is_numeric($comparedExpr->value)); + return !$comparedExpr instanceof String_ || !is_numeric($comparedExpr->value); } } diff --git a/rules/Php80/Rector/Identical/StrEndsWithRector.php b/rules/Php80/Rector/Identical/StrEndsWithRector.php index 7333ad92871..e7b0dce4889 100644 --- a/rules/Php80/Rector/Identical/StrEndsWithRector.php +++ b/rules/Php80/Rector/Identical/StrEndsWithRector.php @@ -266,7 +266,7 @@ private function isRefactorableLooseComparison(BinaryOp $binaryOp, Expr $compare return true; } - return ! ($comparedExpr instanceof String_ && is_numeric($comparedExpr->value)); + return !$comparedExpr instanceof String_ || !is_numeric($comparedExpr->value); } private function buildReturnNode(Expr $haystack, Expr $needle, bool $isPositive): FuncCall|BooleanNot