-
Notifications
You must be signed in to change notification settings - Fork 26
DowngradeMbStrContainsRector #331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
557b732
2402d39
8b15495
cdbbf49
6656092
081e073
06265a2
257f57a
1e18cd7
1e1806a
7bea52c
cdb3f07
046c1b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector; | ||
|
|
||
| use Iterator; | ||
| use PHPUnit\Framework\Attributes\DataProvider; | ||
| use Rector\Testing\PHPUnit\AbstractRectorTestCase; | ||
|
|
||
| final class DowngradeMbStrContainsRectorTest extends AbstractRectorTestCase | ||
| { | ||
| #[DataProvider('provideData')] | ||
| public function test(string $filePath): void | ||
| { | ||
| $this->doTestFile($filePath); | ||
| } | ||
|
|
||
| public static function provideData(): Iterator | ||
| { | ||
| return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); | ||
| } | ||
|
|
||
| public function provideConfigFilePath(): string | ||
| { | ||
| return __DIR__ . '/config/configured_rule.php'; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture; | ||
|
|
||
| final class NoStrContains | ||
| { | ||
| public function run() | ||
| { | ||
| return ! str_contains('abc', 'b'); | ||
| } | ||
| } | ||
|
|
||
| ?> | ||
| ----- | ||
| <?php | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture; | ||
|
|
||
| final class NoStrContains | ||
| { | ||
| public function run() | ||
| { | ||
| return mb_strpos('abc', 'b') === false; | ||
| } | ||
| } | ||
|
|
||
| ?> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture; | ||
|
|
||
| final class StrContains | ||
| { | ||
| public function run() | ||
| { | ||
| str_contains('abc', 'b'); | ||
| } | ||
| } | ||
|
|
||
| ?> | ||
| ----- | ||
| <?php | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture; | ||
|
|
||
| final class StrContains | ||
| { | ||
| public function run() | ||
| { | ||
| mb_strpos('abc', 'b') !== false; | ||
| } | ||
| } | ||
|
|
||
| ?> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <?php | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture; | ||
|
|
||
| final class StrContainsPhpWithMixed | ||
| { | ||
| public function run($haystack, $needle) | ||
| { | ||
| str_contains($haystack, 'ab'); | ||
| str_contains('abc', $needle); | ||
| } | ||
| } | ||
|
|
||
| ?> | ||
| ----- | ||
| <?php | ||
|
|
||
| namespace Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\Fixture; | ||
|
|
||
| final class StrContainsPhpWithMixed | ||
| { | ||
| public function run($haystack, $needle) | ||
| { | ||
| mb_strpos($haystack, 'ab') !== false; | ||
| mb_strpos('abc', $needle) !== false; | ||
| } | ||
| } | ||
|
|
||
| ?> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types=1); | ||
|
|
||
| use Rector\Config\RectorConfig; | ||
| use Rector\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector; | ||
|
|
||
| return static function (RectorConfig $rectorConfig): void { | ||
| $rectorConfig->rule(DowngradeMbStrContainsRector::class); | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,103 @@ | ||||||
| <?php | ||||||
|
|
||||||
| declare(strict_types=1); | ||||||
|
|
||||||
| namespace Rector\DowngradePhp80\Rector\FuncCall; | ||||||
|
|
||||||
| use PhpParser\Node; | ||||||
| use PhpParser\Node\Expr\BinaryOp\Identical; | ||||||
| use PhpParser\Node\Expr\BinaryOp\NotIdentical; | ||||||
| use PhpParser\Node\Expr\BooleanNot; | ||||||
| use PhpParser\Node\Expr\FuncCall; | ||||||
| use Rector\Rector\AbstractRector; | ||||||
| use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; | ||||||
| use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; | ||||||
|
|
||||||
| /** | ||||||
| * @changelog https://wiki.php.net/rfc/str_contains | ||||||
| * | ||||||
| * @see \Rector\Tests\DowngradePhp80\Rector\FuncCall\DowngradeMbStrContainsRector\DowngradeMbStrContainsRectorTest | ||||||
| */ | ||||||
| final class DowngradeMbStrContainsRector extends AbstractRector | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer to rename to something like:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok |
||||||
| { | ||||||
| public function getRuleDefinition(): RuleDefinition | ||||||
| { | ||||||
| return new RuleDefinition( | ||||||
| 'Replace str_contains() with mb_strpos() !== false', | ||||||
| [ | ||||||
| new CodeSample( | ||||||
| <<<'CODE_SAMPLE' | ||||||
| class SomeClass | ||||||
| { | ||||||
| public function run() | ||||||
| { | ||||||
| return str_contains('abc', 'a'); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only when needle has multibyte value as well:
Suggested change
|
||||||
| } | ||||||
| } | ||||||
| CODE_SAMPLE | ||||||
| , | ||||||
| <<<'CODE_SAMPLE' | ||||||
| class SomeClass | ||||||
| { | ||||||
| public function run() | ||||||
| { | ||||||
| return mb_strpos('abc', 'a') !== false; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only when needle has multibyte value as well:
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok |
||||||
| } | ||||||
| } | ||||||
| CODE_SAMPLE | ||||||
| ), | ||||||
| ] | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @return array<class-string<Node>> | ||||||
| */ | ||||||
| public function getNodeTypes(): array | ||||||
| { | ||||||
| return [FuncCall::class, BooleanNot::class]; | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @param FuncCall|BooleanNot $node | ||||||
| * @return Identical|NotIdentical|null The refactored node. | ||||||
| */ | ||||||
| public function refactor(Node $node): Identical | NotIdentical | null | ||||||
| { | ||||||
| $funcCall = $this->matchStrContainsOrNotStrContains($node); | ||||||
|
|
||||||
| if (! $funcCall instanceof FuncCall) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. early skip first class callable here:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if (!$funcCall->isFirstClassCallable()) {
return null;
}Correct ?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, should be below if (! $funcCall instanceof FuncCall) {
return null;
}
if ($funcCall->isFirstClassCallable()) {
return null;
} |
||||||
| $args = $funcCall->getArgs(); | ||||||
| if (count($args) < 2) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| $haystack = $args[0]->value; | ||||||
| $needle = $args[1]->value; | ||||||
|
|
||||||
| $funcCall = $this->nodeFactory->createFuncCall('mb_strpos', [$haystack, $needle]); | ||||||
|
|
||||||
| if ($node instanceof BooleanNot) { | ||||||
| return new Identical($funcCall, $this->nodeFactory->createFalse()); | ||||||
| } | ||||||
|
|
||||||
| return new NotIdentical($funcCall, $this->nodeFactory->createFalse()); | ||||||
| } | ||||||
|
|
||||||
| private function matchStrContainsOrNotStrContains(FuncCall | BooleanNot $expr): ?FuncCall | ||||||
| { | ||||||
| $expr = ($expr instanceof BooleanNot) ? $expr->expr : $expr; | ||||||
| if (! $expr instanceof FuncCall) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| if (! $this->isName($expr, 'str_contains')) { | ||||||
| return null; | ||||||
| } | ||||||
|
|
||||||
| return $expr; | ||||||
| } | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unregister this, there is no
"mb_str_contains", onlystr_contains, that means that utilizestrposcan be on purpose.let user manually use if needed, dealing with user that may don't have mbstring extension or use strpos on purpose :)
user that use
mb_functions know why they are using themb_functions :)