Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\Php80\Rector\Identical\StrEndsWithRector\Fixture;

class Fixture
class LooseSubstr
{
public function run()
{
Expand All @@ -18,7 +18,7 @@ class Fixture

namespace Rector\Tests\Php80\Rector\Identical\StrEndsWithRector\Fixture;

class Fixture
class LooseSubstr
{
public function run()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\Php80\Rector\Identical\StrEndsWithRector\Fixture;

class SubstrNotPrefix
class LooseSubstrNotPrefix
{
public function run()
{
Expand All @@ -18,7 +18,7 @@ class SubstrNotPrefix

namespace Rector\Tests\Php80\Rector\Identical\StrEndsWithRector\Fixture;

class SubstrNotPrefix
class LooseSubstrNotPrefix
{
public function run()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Rector\Tests\Php80\Rector\Identical\StrEndsWithRector\Fixture;

class SkipLooseNumericString
{
public function run($haystack)
{
// '+1' == '01' is true
$isMatch = substr($haystack, -2) == '01';
}
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class NotIdenticalSubstr
class LooseNotEqualSubstr
{
public function run()
{
Expand All @@ -16,7 +16,7 @@ class NotIdenticalSubstr

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class NotIdenticalSubstr
class LooseNotEqualSubstr
{
public function run()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class Fixture
class LooseSubstr
{
public function run()
{
Expand All @@ -16,7 +16,7 @@ class Fixture

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class Fixture
class LooseSubstr
{
public function run()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class OtherSide
class LooseSubstrOtherSide
{
public function run()
{
Expand All @@ -18,7 +18,7 @@ class OtherSide

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class OtherSide
class LooseSubstrOtherSide
{
public function run()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class SkipLooseNumericString
{
public function run($haystack)
{
// '+1' == '01' is true
$isMatch = substr($haystack, 0, 2) == '01';
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class SkipLooseStrpos
{
public function run()
{
$isMatch = strpos($haystack, $needle) == 0;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class SkipLooseStrposHardcoded
{
public function run()
{
$isMatch = strpos($haystack, 'hardcoded') == 0;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class SkipLooseStrposNotPrefix
{
public function run()
{
$notMatch = strpos($haystack, $needle) != 0;
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Rector\Tests\Php80\Rector\Identical\StrStartsWithRector\Fixture;

class SkipLooseStrposOtherSide
{
public function run()
{
$isMatch = 0 == strpos($haystack, $needle);
}
}

?>

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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->isRefactorableLooseComparison($binaryOp, $binaryOp->right)) {
return null;
}

$funcCall = $binaryOp->left;

$haystack = $funcCall->getArgs()[0]
Expand All @@ -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->isRefactorableLooseComparison($binaryOp, $binaryOp->left)) {
return null;
}

$funcCall = $binaryOp->right;

$haystack = $funcCall->getArgs()[0]
Expand Down Expand Up @@ -127,4 +136,19 @@ private function isHardcodedStringWithLNumberLength(StrStartsWith $strStartsWith

return $lNumberLength->value === strlen($expr->value);
}

/**
* Loose comparison of two numeric string literals is numeric, e.g. '+1' == '01';
* skip only that case, a variable needle is kept
*/
private function isRefactorableLooseComparison(
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);
}
}
17 changes: 17 additions & 0 deletions rules/Php80/Rector/Identical/StrEndsWithRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ private function refactorSubstr(BinaryOp $binaryOp): FuncCall|BooleanNot|null
return null;
}

if (! $this->isRefactorableLooseComparison($binaryOp, $comparedNeedleExpr)) {
return null;
}

if (count($substrFuncCall->getArgs()) < 2) {
return null;
}
Expand Down Expand Up @@ -252,6 +256,19 @@ private function isHardCodedLNumberAndString(Expr $substrOffset, Expr $needle):
return $lNumber->value === strlen($needle->value);
}

/**
* Loose comparison of two numeric string literals is numeric, e.g. '+1' == '01';
* skip only that case, a variable needle is kept
*/
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);
}

private function buildReturnNode(Expr $haystack, Expr $needle, bool $isPositive): FuncCall|BooleanNot
{
$funcCall = $this->nodeFactory->createFuncCall('str_ends_with', [$haystack, $needle]);
Expand Down
Loading