Skip to content

Commit 557b732

Browse files
ArshidArshid
authored andcommitted
/DowngradeMbStrContainsRector
1 parent 6d54350 commit 557b732

File tree

6 files changed

+224
-0
lines changed

6 files changed

+224
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector;
6+
7+
use Iterator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
10+
11+
final class DowngradeMbStrContainsRectorTest extends AbstractRectorTestCase
12+
{
13+
#[DataProvider('provideData')]
14+
public function test(string $filePath): void
15+
{
16+
$this->doTestFile($filePath);
17+
}
18+
19+
public static function provideData(): Iterator
20+
{
21+
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
22+
}
23+
24+
public function provideConfigFilePath(): string
25+
{
26+
return __DIR__ . '/config/configured_rule.php';
27+
}
28+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture;
4+
5+
final class NoStrContains
6+
{
7+
public function run()
8+
{
9+
return ! str_contains('abc', 'b');
10+
}
11+
}
12+
13+
?>
14+
-----
15+
<?php
16+
17+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture;
18+
19+
final class NoStrContains
20+
{
21+
public function run()
22+
{
23+
return mb_strpos('abc', 'b') === false;
24+
}
25+
}
26+
27+
?>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture;
4+
5+
final class StrContains
6+
{
7+
public function run()
8+
{
9+
str_contains('abc', 'b');
10+
}
11+
}
12+
13+
?>
14+
-----
15+
<?php
16+
17+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture;
18+
19+
final class StrContains
20+
{
21+
public function run()
22+
{
23+
mb_strpos('abc', 'b') !== false;
24+
}
25+
}
26+
27+
?>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture;
4+
5+
final class StrContainsPhpWithMixed
6+
{
7+
public function run($haystack, $needle)
8+
{
9+
str_contains($haystack, 'ab');
10+
str_contains('abc', $needle);
11+
}
12+
}
13+
14+
?>
15+
-----
16+
<?php
17+
18+
namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture;
19+
20+
final class StrContainsPhpWithMixed
21+
{
22+
public function run($haystack, $needle)
23+
{
24+
mb_strpos($haystack, 'ab') !== false;
25+
mb_strpos('abc', $needle) !== false;
26+
}
27+
}
28+
29+
?>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
use Rector\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector;
7+
8+
return static function (RectorConfig $rectorConfig): void {
9+
$rectorConfig->rule(DowngradeMbStrContainsRector::class);
10+
};
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rector\DowngradePhp80\Rector\FuncCall;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\BinaryOp\Identical;
9+
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
10+
use PhpParser\Node\Expr\BooleanNot;
11+
use PhpParser\Node\Expr\FuncCall;
12+
use Rector\Rector\AbstractRector;
13+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
14+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
15+
16+
/**
17+
* @changelog https://wiki.php.net/rfc/str_contains
18+
*
19+
* @see \Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\DowngradeMbStrContainsRectorTest
20+
*/
21+
final class DowngradeMbStrContainsRector extends AbstractRector
22+
{
23+
public function getRuleDefinition(): RuleDefinition
24+
{
25+
return new RuleDefinition(
26+
'Replace str_contains() with mb_strpos() !== false',
27+
[
28+
new CodeSample(
29+
<<<'CODE_SAMPLE'
30+
class SomeClass
31+
{
32+
public function run()
33+
{
34+
return str_contains('abc', 'a');
35+
}
36+
}
37+
CODE_SAMPLE
38+
,
39+
<<<'CODE_SAMPLE'
40+
class SomeClass
41+
{
42+
public function run()
43+
{
44+
return mb_strpos('abc', 'a') !== false;
45+
}
46+
}
47+
CODE_SAMPLE
48+
),
49+
]
50+
);
51+
}
52+
53+
/**
54+
* @return array<class-string<Node>>
55+
*/
56+
public function getNodeTypes(): array
57+
{
58+
return [FuncCall::class, BooleanNot::class];
59+
}
60+
61+
/**
62+
* @param FuncCall|BooleanNot $node
63+
* @return Identical|NotIdentical|null The refactored node.
64+
*/
65+
public function refactor(Node $node): Identical | NotIdentical | null
66+
{
67+
$funcCall = $this->matchStrContainsOrNotStrContains($node);
68+
69+
if (! $funcCall instanceof FuncCall) {
70+
return null;
71+
}
72+
73+
$args = $funcCall->getArgs();
74+
if (count($args) < 2) {
75+
return null;
76+
}
77+
78+
$haystack = $args[0]->value;
79+
$needle = $args[1]->value;
80+
81+
$funcCall = $this->nodeFactory->createFuncCall('mb_strpos', [$haystack, $needle]);
82+
83+
if ($node instanceof BooleanNot) {
84+
return new Identical($funcCall, $this->nodeFactory->createFalse());
85+
}
86+
87+
return new NotIdentical($funcCall, $this->nodeFactory->createFalse());
88+
}
89+
90+
private function matchStrContainsOrNotStrContains(FuncCall | BooleanNot $expr): ?FuncCall
91+
{
92+
$expr = ($expr instanceof BooleanNot) ? $expr->expr : $expr;
93+
if (! $expr instanceof FuncCall) {
94+
return null;
95+
}
96+
97+
if (! $this->isName($expr, 'str_contains')) {
98+
return null;
99+
}
100+
101+
return $expr;
102+
}
103+
}

0 commit comments

Comments
 (0)