2019-10-13 05:59:52 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
2019-05-23 17:08:44 +00:00
|
|
|
|
2019-09-22 18:57:03 +00:00
|
|
|
namespace Rector\Php55\Rector\String_;
|
2019-05-23 17:08:44 +00:00
|
|
|
|
|
|
|
use PhpParser\Node;
|
2019-05-25 16:36:53 +00:00
|
|
|
use PhpParser\Node\Expr\ClassConstFetch;
|
|
|
|
use PhpParser\Node\Name\FullyQualified;
|
2019-05-23 17:08:44 +00:00
|
|
|
use PhpParser\Node\Scalar\String_;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\Rector\AbstractRector;
|
|
|
|
use Rector\Core\RectorDefinition\CodeSample;
|
|
|
|
use Rector\Core\RectorDefinition\RectorDefinition;
|
2020-05-03 19:30:01 +00:00
|
|
|
use Rector\Core\Util\StaticRectorStrings;
|
2020-02-06 21:48:18 +00:00
|
|
|
use Rector\Core\ValueObject\PhpVersionFeature;
|
2019-09-10 06:21:20 +00:00
|
|
|
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
|
2020-05-13 07:57:21 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2019-06-05 06:40:03 +00:00
|
|
|
use ReflectionClass;
|
2019-05-23 17:08:44 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @see https://wiki.php.net/rfc/class_name_scalars
|
2020-07-12 20:56:51 +00:00
|
|
|
* @see https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md#form
|
2020-05-03 19:30:01 +00:00
|
|
|
*
|
2019-09-23 11:43:13 +00:00
|
|
|
* @see \Rector\Php55\Tests\Rector\String_\StringClassNameToClassConstantRector\StringClassNameToClassConstantRectorTest
|
2019-05-23 17:08:44 +00:00
|
|
|
*/
|
|
|
|
final class StringClassNameToClassConstantRector extends AbstractRector
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
private $classesToSkip = [];
|
|
|
|
|
2019-06-05 06:40:03 +00:00
|
|
|
/**
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
private $sensitiveExistingClasses = [];
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var string[]
|
|
|
|
*/
|
|
|
|
private $sensitiveNonExistingClasses = [];
|
|
|
|
|
2019-05-23 17:08:44 +00:00
|
|
|
/**
|
|
|
|
* @param string[] $classesToSkip
|
|
|
|
*/
|
|
|
|
public function __construct(array $classesToSkip = [
|
2020-05-12 15:20:40 +00:00
|
|
|
// can be string
|
|
|
|
'Error',
|
2020-07-23 10:59:10 +00:00
|
|
|
'Exception',
|
2019-05-23 17:08:44 +00:00
|
|
|
])
|
|
|
|
{
|
|
|
|
$this->classesToSkip = $classesToSkip;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getDefinition(): RectorDefinition
|
|
|
|
{
|
|
|
|
return new RectorDefinition('Replace string class names by <class>::class constant', [
|
|
|
|
new CodeSample(
|
2019-09-18 06:14:35 +00:00
|
|
|
<<<'PHP'
|
2019-05-23 17:08:44 +00:00
|
|
|
class AnotherClass
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
class SomeClass
|
|
|
|
{
|
|
|
|
public function run()
|
|
|
|
{
|
|
|
|
return 'AnotherClass';
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 06:14:35 +00:00
|
|
|
PHP
|
2019-05-23 17:08:44 +00:00
|
|
|
,
|
2019-09-18 06:14:35 +00:00
|
|
|
<<<'PHP'
|
2019-05-23 17:08:44 +00:00
|
|
|
class AnotherClass
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
class SomeClass
|
|
|
|
{
|
|
|
|
public function run()
|
|
|
|
{
|
|
|
|
return \AnotherClass::class;
|
|
|
|
}
|
|
|
|
}
|
2019-09-18 06:14:35 +00:00
|
|
|
PHP
|
2019-05-23 17:08:44 +00:00
|
|
|
),
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string[]
|
|
|
|
*/
|
|
|
|
public function getNodeTypes(): array
|
|
|
|
{
|
|
|
|
return [String_::class];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param String_ $node
|
|
|
|
*/
|
|
|
|
public function refactor(Node $node): ?Node
|
|
|
|
{
|
2019-12-26 19:54:00 +00:00
|
|
|
if (! $this->isAtLeastPhpVersion(PhpVersionFeature::CLASSNAME_CONSTANT)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-05-23 17:08:44 +00:00
|
|
|
$classLikeName = $node->value;
|
2019-06-10 15:49:44 +00:00
|
|
|
|
2019-06-10 15:52:59 +00:00
|
|
|
// remove leading slash
|
|
|
|
$classLikeName = ltrim($classLikeName, '\\');
|
|
|
|
if ($classLikeName === '') {
|
2019-06-10 15:49:44 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2019-06-05 06:40:03 +00:00
|
|
|
if (! $this->classLikeSensitiveExists($classLikeName)) {
|
2019-05-23 17:08:44 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-05-03 19:30:01 +00:00
|
|
|
if (StaticRectorStrings::isInArrayInsensitive($classLikeName, $this->classesToSkip)) {
|
2019-05-23 17:08:44 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-06-29 21:19:37 +00:00
|
|
|
$fullyQualified = new FullyQualified($classLikeName);
|
2020-05-13 07:57:21 +00:00
|
|
|
/** @see \Rector\PostRector\Collector\UseNodesToAddCollector::isShortImported() */
|
2020-06-29 21:19:37 +00:00
|
|
|
$fullyQualified->setAttribute(AttributeKey::FILE_INFO, $node->getAttribute(AttributeKey::FILE_INFO));
|
2020-05-13 07:57:21 +00:00
|
|
|
|
2020-06-29 21:19:37 +00:00
|
|
|
return new ClassConstFetch($fullyQualified, 'class');
|
2019-05-23 17:08:44 +00:00
|
|
|
}
|
|
|
|
|
2019-06-05 06:40:03 +00:00
|
|
|
private function classLikeSensitiveExists(string $classLikeName): bool
|
|
|
|
{
|
2019-09-10 06:21:20 +00:00
|
|
|
if (! ClassExistenceStaticHelper::doesClassLikeExist($classLikeName)) {
|
2019-06-05 06:40:03 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// already known values
|
|
|
|
if (in_array($classLikeName, $this->sensitiveExistingClasses, true)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (in_array($classLikeName, $this->sensitiveNonExistingClasses, true)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-06-29 21:19:37 +00:00
|
|
|
$reflectionClass = new ReflectionClass($classLikeName);
|
2019-06-05 06:40:03 +00:00
|
|
|
|
2020-06-29 21:19:37 +00:00
|
|
|
if ($classLikeName !== $reflectionClass->getName()) {
|
2019-06-05 06:40:03 +00:00
|
|
|
$this->sensitiveNonExistingClasses[] = $classLikeName;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->sensitiveExistingClasses[] = $classLikeName;
|
|
|
|
return true;
|
|
|
|
}
|
2019-05-23 17:08:44 +00:00
|
|
|
}
|