mirror of
https://github.com/rectorphp/rector.git
synced 2024-07-30 21:30:23 +00:00
[PHP] Add BarewordStringRector
This commit is contained in:
parent
ee61b7ed28
commit
4faa1425c8
@ -2,3 +2,4 @@ services:
|
||||
Rector\Php\Rector\While_\WhileEachToForeachRector: ~
|
||||
Rector\Php\Rector\Each\ListEachRector: ~
|
||||
Rector\Php\Rector\Unset_\UnsetCastRector: ~
|
||||
Rector\Php\Rector\String_\BarewordStringRector: ~
|
||||
|
1
ecs.yml
1
ecs.yml
@ -106,6 +106,7 @@ parameters:
|
||||
- '*NodeVisitor.php'
|
||||
- '*CompilerPass.php'
|
||||
- 'packages/Php/src/Rector/Unset_/UnsetCastRector.php'
|
||||
- 'packages/Php/src/Rector/String_/BarewordStringRector.php'
|
||||
# array type check
|
||||
- 'src/RectorDefinition/RectorDefinition.php'
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\FileSystem;
|
||||
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
|
||||
final class CurrentFileInfoProvider
|
||||
{
|
||||
/**
|
||||
* @var SmartFileInfo|null
|
||||
*/
|
||||
private $smartFileInfo;
|
||||
|
||||
public function setCurrentFileInfo(SmartFileInfo $smartFileInfo): void
|
||||
{
|
||||
$this->smartFileInfo = $smartFileInfo;
|
||||
}
|
||||
|
||||
public function getSmartFileInfo(): ?SmartFileInfo
|
||||
{
|
||||
return $this->smartFileInfo;
|
||||
}
|
||||
}
|
@ -89,4 +89,9 @@ final class Attribute
|
||||
* @var string
|
||||
*/
|
||||
public const CURRENT_EXPRESSION = 'currentExpression';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const FILE_INFO = 'fileInfo';
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use PhpParser\NodeVisitor\CloningVisitor;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\ClassAndMethodNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\ExpressionNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\FileInfoNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\NamespaceNodeVisitor;
|
||||
use Rector\NodeTypeResolver\NodeVisitor\ParentAndNextNodeVisitor;
|
||||
use Rector\NodeTypeResolver\PHPStan\Scope\NodeScopeResolver;
|
||||
@ -44,13 +45,19 @@ final class NodeScopeAndMetadataDecorator
|
||||
*/
|
||||
private $expressionNodeVisitor;
|
||||
|
||||
/**
|
||||
* @var FileInfoNodeVisitor
|
||||
*/
|
||||
private $fileInfoNodeVisitor;
|
||||
|
||||
public function __construct(
|
||||
NodeScopeResolver $nodeScopeResolver,
|
||||
ParentAndNextNodeVisitor $parentAndNextNodeVisitor,
|
||||
CloningVisitor $cloningVisitor,
|
||||
ClassAndMethodNodeVisitor $classAndMethodNodeVisitor,
|
||||
NamespaceNodeVisitor $namespaceNodeVisitor,
|
||||
ExpressionNodeVisitor $expressionNodeVisitor
|
||||
ExpressionNodeVisitor $expressionNodeVisitor,
|
||||
FileInfoNodeVisitor $fileInfoNodeVisitor
|
||||
) {
|
||||
$this->nodeScopeResolver = $nodeScopeResolver;
|
||||
$this->parentAndNextNodeVisitor = $parentAndNextNodeVisitor;
|
||||
@ -58,6 +65,7 @@ final class NodeScopeAndMetadataDecorator
|
||||
$this->classAndMethodNodeVisitor = $classAndMethodNodeVisitor;
|
||||
$this->namespaceNodeVisitor = $namespaceNodeVisitor;
|
||||
$this->expressionNodeVisitor = $expressionNodeVisitor;
|
||||
$this->fileInfoNodeVisitor = $fileInfoNodeVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,6 +94,7 @@ final class NodeScopeAndMetadataDecorator
|
||||
$nodeTraverser->addVisitor($this->classAndMethodNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->namespaceNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->expressionNodeVisitor);
|
||||
$nodeTraverser->addVisitor($this->fileInfoNodeVisitor);
|
||||
|
||||
return $nodeTraverser->traverse($nodes);
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
|
||||
final class FileInfoNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var CurrentFileInfoProvider
|
||||
*/
|
||||
private $currentFileInfoProvider;
|
||||
|
||||
public function __construct(CurrentFileInfoProvider $currentFileInfoProvider)
|
||||
{
|
||||
$this->currentFileInfoProvider = $currentFileInfoProvider;
|
||||
}
|
||||
|
||||
public function enterNode(Node $node): ?Node
|
||||
{
|
||||
$node->setAttribute(Attribute::FILE_INFO, $this->currentFileInfoProvider->getSmartFileInfo());
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
76
packages/Php/src/Rector/String_/BarewordStringRector.php
Normal file
76
packages/Php/src/Rector/String_/BarewordStringRector.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php\Rector\String_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Symplify\PackageBuilder\FileSystem\SmartFileInfo;
|
||||
use function Safe\sprintf;
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/deprecate-bareword-strings
|
||||
* @see https://3v4l.org/56ZAu
|
||||
*/
|
||||
final class BarewordStringRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $undefinedConstants = [];
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Changes unquoted non-existing constants to strings', [
|
||||
new CodeSample('var_dump(VAR);', 'var("VAR");'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ConstFetch::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConstFetch $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$constantName = (string) $node->name;
|
||||
if (defined($constantName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// load the file!
|
||||
/** @var SmartFileInfo $fileInfo */
|
||||
$fileInfo = $node->getAttribute(Attribute::FILE_INFO);
|
||||
|
||||
$this->undefinedConstants = [];
|
||||
$previousErrorHandler = set_error_handler(function ($severity, $message, $file, $line): void {
|
||||
$match = Strings::match($message, '#Use of undefined constant (?<constant>\w+)#');
|
||||
if ($match) {
|
||||
$this->undefinedConstants[] = $match['constant'];
|
||||
}
|
||||
});
|
||||
|
||||
include $fileInfo->getRealPath();
|
||||
|
||||
// restore
|
||||
set_error_handler($previousErrorHandler);
|
||||
|
||||
if (! in_array($constantName, $this->undefinedConstants, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// wrap to explicit string
|
||||
return new String_(sprintf('%s', $constantName));
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Php\Tests\Rector\String_\BarewordStringRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
/**
|
||||
* @covers \Rector\Php\Rector\String_\BarewordStringRector
|
||||
*/
|
||||
final class BarewordStringRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideWrongToFixedFiles()
|
||||
*/
|
||||
public function test(string $wrong, string $fixed): void
|
||||
{
|
||||
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
|
||||
}
|
||||
|
||||
public function provideWrongToFixedFiles(): Iterator
|
||||
{
|
||||
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
|
||||
}
|
||||
|
||||
protected function provideConfig(): string
|
||||
{
|
||||
return __DIR__ . '/config.yml';
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
var_dump('SOME_STRING');
|
||||
|
||||
var_dump(true);
|
||||
var_dump(false);
|
||||
var_dump(NULL);
|
||||
var_dump(__DIR__);
|
||||
|
||||
const THIS_EXISTS = 'yes';
|
||||
var_dump(THIS_EXISTS);
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
var_dump(SOME_STRING);
|
||||
|
||||
var_dump(true);
|
||||
var_dump(false);
|
||||
var_dump(NULL);
|
||||
var_dump(__DIR__);
|
||||
|
||||
const THIS_EXISTS = 'yes';
|
||||
var_dump(THIS_EXISTS);
|
@ -0,0 +1,2 @@
|
||||
services:
|
||||
Rector\Php\Rector\String_\BarewordStringRector: ~
|
@ -62,6 +62,8 @@ parameters:
|
||||
# false positive, has annotation type above
|
||||
- '#Access to an undefined property PhpParser\\Node::\$name#' # 11
|
||||
- '#Access to an undefined property PhpParser\\Node\\Expr::\$expr#'
|
||||
# false positive
|
||||
- '#Call to function in_array\(\) with arguments string, array\(\) and true will always evaluate to false#'
|
||||
|
||||
# subtype
|
||||
- '#Property PhpParser\\Node\\Param::\$type \(PhpParser\\Node\\Name|PhpParser\\Node\\NullableType\|string\|null\) does not accept PhpParser\\Node\\Identifier|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType#' # 3
|
||||
|
@ -5,6 +5,7 @@ namespace Rector\Application;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Node;
|
||||
use Rector\NodeTraverser\RectorNodeTraverser;
|
||||
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
|
||||
use Rector\NodeTypeResolver\NodeScopeAndMetadataDecorator;
|
||||
use Rector\Parser\Parser;
|
||||
use Rector\Printer\FormatPerservingPrinter;
|
||||
@ -37,22 +38,31 @@ final class FileProcessor
|
||||
*/
|
||||
private $nodeScopeAndMetadataDecorator;
|
||||
|
||||
/**
|
||||
* @var CurrentFileInfoProvider
|
||||
*/
|
||||
private $currentFileInfoProvider;
|
||||
|
||||
public function __construct(
|
||||
FormatPerservingPrinter $formatPerservingPrinter,
|
||||
Parser $parser,
|
||||
Lexer $lexer,
|
||||
RectorNodeTraverser $rectorNodeTraverser,
|
||||
NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator
|
||||
NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator,
|
||||
CurrentFileInfoProvider $currentFileInfoProvider
|
||||
) {
|
||||
$this->formatPerservingPrinter = $formatPerservingPrinter;
|
||||
$this->parser = $parser;
|
||||
$this->lexer = $lexer;
|
||||
$this->rectorNodeTraverser = $rectorNodeTraverser;
|
||||
$this->nodeScopeAndMetadataDecorator = $nodeScopeAndMetadataDecorator;
|
||||
$this->currentFileInfoProvider = $currentFileInfoProvider;
|
||||
}
|
||||
|
||||
public function processFile(SmartFileInfo $smartFileInfo): string
|
||||
{
|
||||
$this->currentFileInfoProvider->setCurrentFileInfo($smartFileInfo);
|
||||
|
||||
[$newStmts, $oldStmts, $oldTokens] = $this->parseAndTraverseFileInfoToNodes($smartFileInfo);
|
||||
|
||||
return $this->formatPerservingPrinter->printToFile($smartFileInfo, $newStmts, $oldStmts, $oldTokens);
|
||||
@ -63,6 +73,8 @@ final class FileProcessor
|
||||
*/
|
||||
public function processFileToString(SmartFileInfo $smartFileInfo): string
|
||||
{
|
||||
$this->currentFileInfoProvider->setCurrentFileInfo($smartFileInfo);
|
||||
|
||||
[$newStmts, $oldStmts, $oldTokens] = $this->parseAndTraverseFileInfoToNodes($smartFileInfo);
|
||||
|
||||
return $this->formatPerservingPrinter->printToString($newStmts, $oldStmts, $oldTokens);
|
||||
|
Loading…
Reference in New Issue
Block a user