[PHP] Add StringClassNameToClassConstantRector

This commit is contained in:
Tomas Votruba 2019-05-23 19:08:44 +02:00
parent 90ffe43c7a
commit f51b3d3a71
7 changed files with 190 additions and 0 deletions

View File

@ -14,3 +14,6 @@ services:
Rector\CodingStyle\Rector\ClassConst\SplitGroupedConstantsAndPropertiesRector: ~
Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector: ~
Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector: ~
# 'ClassName' → ClassName::class
Rector\Php\Rector\String_\StringClassNameToClassConstantRector: ~

View File

@ -0,0 +1,2 @@
services:
Rector\Php\Rector\String_\StringClassNameToClassConstantRector: ~

View File

@ -0,0 +1,104 @@
<?php declare(strict_types=1);
namespace Rector\Php\Rector\String_;
use PhpParser\Node;
use PhpParser\Node\Scalar\String_;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
use Rector\Util\RectorStrings;
/**
* @see https://wiki.php.net/rfc/class_name_scalars
*/
final class StringClassNameToClassConstantRector extends AbstractRector
{
/**
* @var string[]
*/
private $classesToSkip = [];
/**
* @param string[] $classesToSkip
*/
public function __construct(array $classesToSkip = [
'Error', // can be string
])
{
$this->classesToSkip = $classesToSkip;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Replace string class names by <class>::class constant', [
new CodeSample(
<<<'CODE_SAMPLE'
class AnotherClass
{
}
class SomeClass
{
public function run()
{
return 'AnotherClass';
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class AnotherClass
{
}
class SomeClass
{
public function run()
{
return \AnotherClass::class;
}
}
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [String_::class];
}
/**
* @param String_ $node
*/
public function refactor(Node $node): ?Node
{
$classLikeName = $node->value;
if (! $this->classLikeExists($classLikeName)) {
return null;
}
if (RectorStrings::isInArrayInsensitive($classLikeName, $this->classesToSkip)) {
return null;
}
return new Node\Expr\ClassConstFetch(new Node\Name\FullyQualified($classLikeName), 'class');
}
private function classLikeExists(string $classLikeName): bool
{
if (class_exists($classLikeName)) {
return true;
}
if (interface_exists($classLikeName)) {
return true;
}
return trait_exists($classLikeName);
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Rector\Php\Tests\Rector\String_\StringClassNameToClassConstantRector\Fixture;
class AnotherClass
{
}
class SomeClass
{
public function run()
{
return 'Rector\Php\Tests\Rector\String_\StringClassNameToClassConstantRector\Fixture\AnotherClass';
}
}
?>
-----
<?php
namespace Rector\Php\Tests\Rector\String_\StringClassNameToClassConstantRector\Fixture;
class AnotherClass
{
}
class SomeClass
{
public function run()
{
return \Rector\Php\Tests\Rector\String_\StringClassNameToClassConstantRector\Fixture\AnotherClass::class;
}
}
?>

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\Php\Tests\Rector\String_\StringClassNameToClassConstantRector\Fixture;
class SkipError
{
public function run()
{
return 'Error';
return 'ERROR';
}
}

View File

@ -0,0 +1,19 @@
<?php declare(strict_types=1);
namespace Rector\Php\Tests\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class StringClassNameToClassConstantRectorTest extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFiles([__DIR__ . '/Fixture/fixture.php.inc', __DIR__ . '/Fixture/skip_error.php.inc']);
}
protected function getRectorClass(): string
{
return StringClassNameToClassConstantRector::class;
}
}

View File

@ -18,6 +18,20 @@ final class RectorStrings
return $privatesCaller->callPrivateMethod(new StringInput(''), 'tokenize', $command);
}
/**
* @param string[] $array
*/
public static function isInArrayInsensitive(string $checkedItem, array $array): bool
{
foreach ($array as $item) {
if (Strings::lower($item) === Strings::lower($checkedItem)) {
return true;
}
}
return false;
}
public static function camelCaseToDashes(string $input): string
{
return self::camelCaseToGlue($input, '-');