Add new rector to replace hardcoded class name reference in string with class keyword reference

This commit is contained in:
dobryy 2020-06-29 20:36:58 +02:00
parent 5e908d9810
commit 4a3fdf3705
6 changed files with 187 additions and 2 deletions

View File

@ -1,4 +1,4 @@
# All 512 Rectors Overview
# All 513 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -11,7 +11,7 @@
- [CakePHP](#cakephp) (6)
- [Celebrity](#celebrity) (3)
- [CodeQuality](#codequality) (54)
- [CodingStyle](#codingstyle) (32)
- [CodingStyle](#codingstyle) (33)
- [DeadCode](#deadcode) (40)
- [Decouple](#decouple) (1)
- [Doctrine](#doctrine) (16)
@ -2239,6 +2239,19 @@ Change under_score names to pascalCase
<br><br>
### `UseClassKeywordForClassNameResolutionRector`
- class: [`Rector\CodingStyle\Rector\String_\UseClassKeywordForClassNameResolutionRector`](/../master/rules/coding-style/src/Rector/String_/UseClassKeywordForClassNameResolutionRector.php)
Use `class` keyword for class name resolution in string instead of hardcoded string reference
```diff
-$value = 'App\SomeClass::someMethod()';
+$value = \App\SomeClass . '::someMethod()';
```
<br><br>
### `UseIncrementAssignRector`
- class: [`Rector\CodingStyle\Rector\Assign\UseIncrementAssignRector`](/../master/rules/coding-style/src/Rector/Assign/UseIncrementAssignRector.php)

View File

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Rector\String_;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
/**
* @see \Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\UseClassKeywordForClassNameResolutionTest
*/
final class UseClassKeywordForClassNameResolutionRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Use `class` keyword for class name resolution in string instead of hardcoded string reference',
[
new CodeSample(
<<<'PHP'
$value = 'App\SomeClass::someMethod()';
PHP
,
<<<'PHP'
$value = \App\SomeClass . '::someMethod()';
PHP
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [String_::class];
}
/**
* @param String_ $node
*/
public function refactor(Node $node): ?Node
{
if (substr_count($node->value, '::') !== 1) {
return null;
}
// a possible static call reference
// @see https://regex101.com/r/Vv41Qr/1/
[$before, $possibleClass, $after] = Strings::split($node->value, '#([\\\\a-zA-Z0-9_\\x80-\\xff]*)::#');
if (! $possibleClass || ! class_exists($possibleClass)) {
return null;
}
$classConstFetch = new ClassConstFetch(new FullyQualified(ltrim($possibleClass, '\\')), 'class');
$concat = new Concat($classConstFetch, new String_('::' . $after));
if (! empty($before)) {
$concat = new Concat(new String_($before), $classConstFetch);
$concat = new Concat($concat, new String_('::' . $after));
}
return $concat;
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture;
class EmptyStringBeforeClassName
{
public function run()
{
return 'Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture\EmptyStringBeforeClassName::staticCall()) { ?>';
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture;
class EmptyStringBeforeClassName
{
public function run()
{
return \Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture\EmptyStringBeforeClassName::class . '::staticCall()) { ?>';
}
}
?>

View File

@ -0,0 +1,27 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture;
class SomeClass
{
public function run()
{
return '<?php if (Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture\SomeClass::staticCall()) { ?>';
}
}
?>
-----
<?php
namespace Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture;
class SomeClass
{
public function run()
{
return '<?php if (' . \Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture\SomeClass::class . '::staticCall()) { ?>';
}
}
?>

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture;
class NonExistingClassName
{
public function run()
{
return '<?php if (Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector\Fixture\SomeClassThatDoesntExist::staticCall()) { ?>';
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Tests\Rector\String_\UseClassKeywordForClassNameResolutionRector;
use Iterator;
use Rector\CodingStyle\Rector\String_\UseClassKeywordForClassNameResolutionRector;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class UseClassKeywordForClassNameResolutionTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $file): void
{
$this->doTestFileInfo($file);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return UseClassKeywordForClassNameResolutionRector::class;
}
}