Merge CoreRectorInterface (#5762)

This commit is contained in:
Tomas Votruba 2021-03-03 22:28:27 +01:00 committed by GitHub
parent bfa5416077
commit 382f146eaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1080 additions and 798 deletions

View File

@ -26,7 +26,7 @@ jobs:
COMPOSER_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# 1. prepare dependencies
- run: sh build-rector-scoped.sh
- run: sh build/build-rector-scoped.sh
# 2. publish it to remote repository
-

View File

@ -27,7 +27,7 @@ jobs:
COMPOSER_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# 1. prepare dependencies
- run: sh build-rector-scoped.sh
- run: sh build/build-rector-scoped.sh
# 2. get tag - see https://github.com/WyriHaximus/github-action-get-previous-tag
-

View File

@ -1,4 +1,4 @@
# 667 Rules Overview
# 672 Rules Overview
<br>
@ -56,8 +56,6 @@
- [MockeryToProphecy](#mockerytoprophecy) (2)
- [MockistaToMockery](#mockistatomockery) (2)
- [MysqlToMysqli](#mysqltomysqli) (4)
- [Naming](#naming) (12)
@ -108,6 +106,8 @@
- [PhpSpecToPHPUnit](#phpspectophpunit) (7)
- [PostRector](#postrector) (7)
- [Privatization](#privatization) (15)
- [RectorGenerator](#rectorgenerator) (1)
@ -7204,54 +7204,6 @@ Changes mockery mock creation to Prophesize
<br>
## MockistaToMockery
### MockeryTearDownRector
Add `Mockery::close()` in `tearDown()` method if not yet
- class: [`Rector\MockistaToMockery\Rector\Class_\MockeryTearDownRector`](/rules/mockista-to-mockery/src/Rector/Class_/MockeryTearDownRector.php)
```diff
use PHPUnit\Framework\TestCase;
class SomeTest extends TestCase
{
+ protected function tearDown(): void
+ {
+ Mockery::close();
+ }
public function test()
{
$mockUser = mock(User::class);
}
}
```
<br>
### MockistaMockToMockeryMockRector
Change functions to static calls, so composer can autoload them
- class: [`Rector\MockistaToMockery\Rector\ClassMethod\MockistaMockToMockeryMockRector`](/rules/mockista-to-mockery/src/Rector/ClassMethod/MockistaMockToMockeryMockRector.php)
```diff
class SomeTest
{
public function run()
{
- $mockUser = mock(User::class);
- $mockUser->getId()->once->andReturn(1);
- $mockUser->freeze();
+ $mockUser = Mockery::mock(User::class);
+ $mockUser->expects()->getId()->once()->andReturn(1);
}
}
```
<br>
## MysqlToMysqli
### MysqlAssignToMysqliRector
@ -7691,14 +7643,16 @@ Use `Nette\Utils\Strings::endsWith()` over bare string-functions
- class: [`Rector\Nette\Rector\Identical\EndsWithFunctionToNetteUtilsStringsRector`](/rules/nette/src/Rector/Identical/EndsWithFunctionToNetteUtilsStringsRector.php)
```diff
+use Nette\Utils\Strings;
+
class SomeClass
{
public function end($needle)
{
$content = 'Hi, my name is Tom';
-
- $yes = substr($content, -strlen($needle)) === $needle;
+ $yes = \Nette\Utils\Strings::endsWith($content, $needle);
+ $yes = Strings::endsWith($content, $needle);
}
}
```
@ -7948,15 +7902,16 @@ Use `Nette\Utils\Strings::startsWith()` over bare string-functions
- class: [`Rector\Nette\Rector\Identical\StartsWithFunctionToNetteUtilsStringsRector`](/rules/nette/src/Rector/Identical/StartsWithFunctionToNetteUtilsStringsRector.php)
```diff
+use Nette\Utils\Strings;
+
class SomeClass
{
public function start($needle)
{
$content = 'Hi, my name is Tom';
- $yes = substr($content, 0, strlen($needle)) === $needle;
+ $yes = \Nette\Utils\Strings::startsWith($content, $needle);
}
public function start($needle)
{
$content = 'Hi, my name is Tom';
- $yes = substr($content, 0, strlen($needle)) === $needle;
+ $yes = Strings::startsWith($content, $needle);
}
}
```
@ -12108,6 +12063,142 @@ Rename "*Spec.php" file to "*Test.php" file
<br>
## PostRector
### ClassRenamingPostRector
Rename references for classes that were renamed during Rector run
- class: [`Rector\PostRector\Rector\ClassRenamingPostRector`](/packages/post-rector/src/Rector/ClassRenamingPostRector.php)
```diff
-function (OriginalClass $someClass)
+function (RenamedClass $someClass)
{
}
```
<br>
### NameImportingPostRector
Imports fully qualified names
- class: [`Rector\PostRector\Rector\NameImportingPostRector`](/packages/post-rector/src/Rector/NameImportingPostRector.php)
```diff
+use App\AnotherClass;
+
class SomeClass
{
- public function run(App\AnotherClass $anotherClass)
+ public function run(AnotherClass $anotherClass)
{
}
}
```
<br>
### NodeAddingPostRector
Add nodes on weird positions
- class: [`Rector\PostRector\Rector\NodeAddingPostRector`](/packages/post-rector/src/Rector/NodeAddingPostRector.php)
```diff
class SomeClass
{
public function run($value)
{
- return 1;
+ if ($value) {
+ return 1;
+ }
}
}
```
<br>
### NodeRemovingPostRector
Remove nodes from weird positions
- class: [`Rector\PostRector\Rector\NodeRemovingPostRector`](/packages/post-rector/src/Rector/NodeRemovingPostRector.php)
```diff
class SomeClass
{
public function run($value)
{
- if ($value) {
- return 1;
- }
+ return 1;
}
}
```
<br>
### NodeToReplacePostRector
Replaces nodes on weird positions
- class: [`Rector\PostRector\Rector\NodeToReplacePostRector`](/packages/post-rector/src/Rector/NodeToReplacePostRector.php)
```diff
class SomeClass
{
public function run($value)
{
- return 1;
+ return $value;
}
}
```
<br>
### PropertyAddingPostRector
Add dependency properties
- class: [`Rector\PostRector\Rector\PropertyAddingPostRector`](/packages/post-rector/src/Rector/PropertyAddingPostRector.php)
```diff
class SomeClass
{
+ private $value;
public function run()
{
return $this->value;
}
}
```
<br>
### UseAddingPostRector
Add unique use imports collected during Rector run
- class: [`Rector\PostRector\Rector\UseAddingPostRector`](/packages/post-rector/src/Rector/UseAddingPostRector.php)
```diff
+use App\AnotherClass;
+
class SomeClass
{
public function run(AnotherClass $anotherClass)
{
}
}
```
<br>
## Privatization
### ChangeGlobalVariablesToPropertiesRector

View File

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Rector\ChangesReporting\ValueObject;
use Rector\Core\Contract\Rector\CoreRectorInterface;
use Rector\Core\Contract\Rector\RectorInterface;
final class RectorWithFileAndLineChange
@ -33,12 +32,8 @@ final class RectorWithFileAndLineChange
public function getRectorDefinitionsDescription(): string
{
if ($this->rector instanceof CoreRectorInterface) {
$ruleDefinition = $this->rector->getRuleDefinition();
return $ruleDefinition->getDescription();
}
return '';
$ruleDefinition = $this->rector->getRuleDefinition();
return $ruleDefinition->getDescription();
}
public function getRectorClass(): string

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Rector\PostRector\Rector;
use PhpParser\NodeVisitorAbstract;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
abstract class AbstractPostRector extends NodeVisitorAbstract implements PostRectorInterface
{
}

View File

@ -5,12 +5,12 @@ declare(strict_types=1);
namespace Rector\PostRector\Rector;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Rector\PSR4\Collector\RenamedClassesCollector;
use Rector\Renaming\NodeManipulator\ClassRenamer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class ClassRenamingPostRector extends NodeVisitorAbstract implements PostRectorInterface
final class ClassRenamingPostRector extends AbstractPostRector
{
/**
* @var RenamedClassesCollector
@ -43,4 +43,23 @@ final class ClassRenamingPostRector extends NodeVisitorAbstract implements PostR
return $this->classRenamer->renameNode($node, $oldToNewClasses);
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Rename references for classes that were renamed during Rector run', [
new CodeSample(
<<<'CODE_SAMPLE'
function (OriginalClass $someClass)
{
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
function (RenamedClass $someClass)
{
}
CODE_SAMPLE
),
]);
}
}

View File

@ -6,7 +6,6 @@ namespace Rector\PostRector\Rector;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Reflection\ReflectionProvider;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper;
@ -14,10 +13,11 @@ use Rector\CodingStyle\Node\NameImporter;
use Rector\Core\Configuration\Option;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockNameImporter;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class NameImportingPostRector extends NodeVisitorAbstract implements PostRectorInterface
final class NameImportingPostRector extends AbstractPostRector
{
/**
* @var ParameterProvider
@ -99,6 +99,33 @@ final class NameImportingPostRector extends NodeVisitorAbstract implements PostR
return 600;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Imports fully qualified names', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run(App\AnotherClass $anotherClass)
{
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use App\AnotherClass;
class SomeClass
{
public function run(AnotherClass $anotherClass)
{
}
}
CODE_SAMPLE
),
]);
}
private function processNodeName(Name $name): ?Node
{
if ($name->isSpecialClassName()) {

View File

@ -5,9 +5,9 @@ declare(strict_types=1);
namespace Rector\PostRector\Rector;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use Rector\PostRector\Collector\NodesToAddCollector;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* This class collects all to-be-added expresssions (= 1 line in code)
@ -20,7 +20,7 @@ use Rector\PostRector\Contract\Rector\PostRectorInterface;
* - $this->someCall();
* - $value = this->someNewCall(); // added expression
*/
final class NodeAddingPostRector extends NodeVisitorAbstract implements PostRectorInterface
final class NodeAddingPostRector extends AbstractPostRector
{
/**
* @var NodesToAddCollector
@ -62,4 +62,33 @@ final class NodeAddingPostRector extends NodeVisitorAbstract implements PostRect
return $newNodes;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add nodes on weird positions', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run($value)
{
return 1;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function run($value)
{
if ($value) {
return 1;
}
}
}
CODE_SAMPLE
), ]
);
}
}

View File

@ -8,14 +8,14 @@ use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PostRector\Collector\NodesToRemoveCollector;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class NodeRemovingPostRector extends NodeVisitorAbstract implements PostRectorInterface
final class NodeRemovingPostRector extends AbstractPostRector
{
/**
* @var NodesToRemoveCollector
@ -107,6 +107,35 @@ final class NodeRemovingPostRector extends NodeVisitorAbstract implements PostRe
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove nodes from weird positions', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run($value)
{
if ($value) {
return 1;
}
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function run($value)
{
return 1;
}
}
CODE_SAMPLE
), ]
);
}
private function isChainMethodCallNodeToBeRemoved(
MethodCall $mainMethodCall,
MethodCall $toBeRemovedMethodCall

View File

@ -5,11 +5,11 @@ declare(strict_types=1);
namespace Rector\PostRector\Rector;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use Rector\PostRector\Collector\NodesToReplaceCollector;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class NodeToReplacePostRector extends NodeVisitorAbstract implements PostRectorInterface
final class NodeToReplacePostRector extends AbstractPostRector
{
/**
* @var NodesToReplaceCollector
@ -36,4 +36,31 @@ final class NodeToReplacePostRector extends NodeVisitorAbstract implements PostR
return null;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replaces nodes on weird positions', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run($value)
{
return 1;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public function run($value)
{
return $value;
}
}
CODE_SAMPLE
), ]
);
}
}

View File

@ -6,17 +6,17 @@ namespace Rector\PostRector\Rector;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\NodeVisitorAbstract;
use Rector\Core\NodeManipulator\ClassDependencyManipulator;
use Rector\Core\NodeManipulator\ClassInsertManipulator;
use Rector\PostRector\Collector\PropertyToAddCollector;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Rector\PostRector\NodeAnalyzer\NetteInjectDetector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* Adds new private properties to class + to constructor
*/
final class PropertyAddingPostRector extends NodeVisitorAbstract implements PostRectorInterface
final class PropertyAddingPostRector extends AbstractPostRector
{
/**
* @var ClassDependencyManipulator
@ -70,6 +70,34 @@ final class PropertyAddingPostRector extends NodeVisitorAbstract implements Post
return $node;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add dependency properties', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
return $this->value;
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
private $value;
public function run()
{
return $this->value;
}
}
CODE_SAMPLE
), ]
);
}
private function addConstants(Class_ $class): void
{
$constants = $this->propertyToAddCollector->getConstantsByClass($class);

View File

@ -8,7 +8,6 @@ use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\NodeVisitorAbstract;
use Rector\CodingStyle\Application\UseImportsAdder;
use Rector\CodingStyle\Application\UseImportsRemover;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
@ -16,11 +15,12 @@ use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PostRector\Collector\UseNodesToAddCollector;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Symplify\SmartFileSystem\SmartFileInfo;
final class UseAddingPostRector extends NodeVisitorAbstract implements PostRectorInterface
final class UseAddingPostRector extends AbstractPostRector
{
/**
* @var UseImportsAdder
@ -122,6 +122,33 @@ final class UseAddingPostRector extends NodeVisitorAbstract implements PostRecto
return 500;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add unique use imports collected during Rector run', [
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public function run(AnotherClass $anotherClass)
{
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
use App\AnotherClass;
class SomeClass
{
public function run(AnotherClass $anotherClass)
{
}
}
CODE_SAMPLE
), ]
);
}
/**
* @param Node[] $nodes
*/

View File

@ -253,7 +253,7 @@ parameters:
# merging comments
- packages/better-php-doc-parser/tests/PhpDocInfo/PhpDocInfo/PhpDocInfoTest.php
- src/PhpParser/NodeTransformer.php
- src/Rector/AbstractTemporaryRector.php
- src/Rector/AbstractRector.php
# playing around with doc block format
- packages/comments/src/CommentRemover.php
- rules/coding-style/src/Rector/Assign/PHPStormVarAnnotationRector.php
@ -370,6 +370,7 @@ parameters:
message: '#Property with protected modifier is not allowed\. Use interface contract method instead#'
paths:
- rules/defluent/src/ValueObject/*
- src/Rector/AbstractRector.php
- '#Parameter \#1 \$keyName of method Rector\\AttributeAwarePhpDoc\\Ast\\Type\\AttributeAwareArrayShapeItemNode\:\:createKeyWithSpacePattern\(\) expects PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode\|PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode\|null, PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode\|PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode\|PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode\|null given#'
- '#AttributeAwarePhpDocNode#'
@ -440,7 +441,7 @@ parameters:
-
message: '#\$this as argument is not allowed\. Refactor method to service composition#'
paths:
- src/Rector/AbstractTemporaryRector.php
- src/Rector/AbstractRector.php
# setter to avoid circular dependency in nested collector
- rules/nette-code-quality/src/NodeResolver/MethodNamesByInputNamesResolver.php
- packages/static-type-mapper/src/StaticTypeMapper.php
@ -613,3 +614,8 @@ parameters:
message: '#Argument and options "debug" got confused#'
paths:
- src/Console/Command/ProcessCommand.php
-
message: '#Class cognitive complexity is 31, keep it under 30#'
paths:
- src/Rector/AbstractRector.php

View File

@ -75,9 +75,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
StringClassNameToClassConstantRector::class,
SplitStringClassConstantToClassConstFetchRector::class,
PrivatizeLocalPropertyToPrivatePropertyRector::class => [
__DIR__ . '/src/Rector/AbstractTemporaryRector.php',
],
PrivatizeLocalPropertyToPrivatePropertyRector::class => [__DIR__ . '/src/Rector/AbstractRector.php'],
// test paths
'*/Fixture/*',

View File

@ -311,4 +311,14 @@ CODE_SAMPLE
return null;
}
private function getNextExpression(Node $node): ?Node
{
$currentExpression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT);
if (! $currentExpression instanceof Expression) {
return null;
}
return $currentExpression->getAttribute(AttributeKey::NEXT_NODE);
}
}

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Composer\Contract\Rector;
use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;
interface CoreComposerRectorInterface extends ComposerRectorInterface, DocumentedRuleInterface
{
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Composer\Rector;
use Rector\Composer\Contract\Rector\CoreComposerRectorInterface;
use Rector\Composer\Contract\Rector\ComposerRectorInterface;
use Rector\Composer\Guard\VersionGuard;
use Rector\Composer\ValueObject\PackageAndVersion;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJson;
@ -14,7 +14,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Composer\Tests\Rector\AddPackageToRequireComposerRector\AddPackageToRequireComposerRectorTest
*/
final class AddPackageToRequireComposerRector implements CoreComposerRectorInterface
final class AddPackageToRequireComposerRector implements ComposerRectorInterface
{
/**
* @var string

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Composer\Rector;
use Rector\Composer\Contract\Rector\CoreComposerRectorInterface;
use Rector\Composer\Contract\Rector\ComposerRectorInterface;
use Rector\Composer\Guard\VersionGuard;
use Rector\Composer\ValueObject\PackageAndVersion;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJson;
@ -14,7 +14,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Composer\Tests\Rector\AddPackageToRequireDevComposerRector\AddPackageToRequireDevComposerRectorTest
*/
final class AddPackageToRequireDevComposerRector implements CoreComposerRectorInterface
final class AddPackageToRequireDevComposerRector implements ComposerRectorInterface
{
/**
* @var string

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Composer\Rector;
use Rector\Composer\Contract\Rector\CoreComposerRectorInterface;
use Rector\Composer\Contract\Rector\ComposerRectorInterface;
use Rector\Composer\Guard\VersionGuard;
use Rector\Composer\ValueObject\PackageAndVersion;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJson;
@ -14,7 +14,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Composer\Tests\Rector\ChangePackageVersionComposerRector\ChangePackageVersionComposerRectorTest
*/
final class ChangePackageVersionComposerRector implements CoreComposerRectorInterface
final class ChangePackageVersionComposerRector implements ComposerRectorInterface
{
/**
* @var string

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Composer\Rector;
use Rector\Composer\Contract\Rector\CoreComposerRectorInterface;
use Rector\Composer\Contract\Rector\ComposerRectorInterface;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJson;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -12,7 +12,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Composer\Tests\Rector\RemovePackageComposerRector\RemovePackageComposerRectorTest
*/
final class RemovePackageComposerRector implements CoreComposerRectorInterface
final class RemovePackageComposerRector implements ComposerRectorInterface
{
/**
* @var string

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\Composer\Rector;
use Rector\Composer\Contract\Rector\CoreComposerRectorInterface;
use Rector\Composer\Contract\Rector\ComposerRectorInterface;
use Rector\Composer\Guard\VersionGuard;
use Rector\Composer\ValueObject\ReplacePackageAndVersion;
use Symplify\ComposerJsonManipulator\ValueObject\ComposerJson;
@ -14,7 +14,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Composer\Tests\Rector\ReplacePackageAndVersionComposerRector\ReplacePackageAndVersionComposerRectorTest
*/
final class ReplacePackageAndVersionComposerRector implements CoreComposerRectorInterface
final class ReplacePackageAndVersionComposerRector implements ComposerRectorInterface
{
/**
* @var string

View File

@ -119,7 +119,7 @@ CODE_SAMPLE
return null;
}
if ($this->isNumberType($node->var)) {
if ($this->nodeTypeResolver->isNumberType($node->var)) {
return $node->var;
}
}
@ -129,7 +129,7 @@ CODE_SAMPLE
if (! $this->valueResolver->isValue($node->expr, 1)) {
return null;
}
if ($this->isNumberType($node->var)) {
if ($this->nodeTypeResolver->isNumberType($node->var)) {
return $node->var;
}
}
@ -159,7 +159,9 @@ CODE_SAMPLE
*/
private function processBinaryPlusAndMinus(BinaryOp $binaryOp): ?Expr
{
if ($this->valueResolver->isValue($binaryOp->left, 0) && $this->isNumberType($binaryOp->right)) {
if ($this->valueResolver->isValue($binaryOp->left, 0) && $this->nodeTypeResolver->isNumberType(
$binaryOp->right
)) {
if ($binaryOp instanceof Minus) {
return new UnaryMinus($binaryOp->right);
}
@ -168,7 +170,7 @@ CODE_SAMPLE
if (! $this->valueResolver->isValue($binaryOp->right, 0)) {
return null;
}
if (! $this->isNumberType($binaryOp->left)) {
if (! $this->nodeTypeResolver->isNumberType($binaryOp->left)) {
return null;
}
return $binaryOp->left;
@ -179,15 +181,16 @@ CODE_SAMPLE
*/
private function processBinaryMulAndDiv(BinaryOp $binaryOp): ?Expr
{
if ($binaryOp instanceof Mul && $this->valueResolver->isValue($binaryOp->left, 1) && $this->isNumberType(
$binaryOp->right
)) {
if ($binaryOp instanceof Mul && $this->valueResolver->isValue(
$binaryOp->left,
1
) && $this->nodeTypeResolver->isNumberType($binaryOp->right)) {
return $binaryOp->right;
}
if (! $this->valueResolver->isValue($binaryOp->right, 1)) {
return null;
}
if (! $this->isNumberType($binaryOp->left)) {
if (! $this->nodeTypeResolver->isNumberType($binaryOp->left)) {
return null;
}
return $binaryOp->left;

View File

@ -198,7 +198,7 @@ CODE_SAMPLE
$isExprFoundInReturn = (bool) $this->betterNodeFinder->findFirst($return->expr, function (Node $node) use (
$expr
): bool {
return $this->areNodesEqual($node, $expr);
return $this->nodeComparator->areNodesEqual($node, $expr);
});
if ($isExprFoundInReturn) {
return true;

View File

@ -145,7 +145,7 @@ CODE_SAMPLE
return null;
}
if (! $this->isNumberType($argExpr)) {
if (! $this->nodeTypeResolver->isNumberType($argExpr)) {
return null;
}

View File

@ -76,13 +76,17 @@ CODE_SAMPLE
if ($node instanceof Coalesce) {
return null;
}
if ($this->isStringOrStaticNonNumbericString($node->left) && $this->isNumberType($node->right)) {
if ($this->isStringOrStaticNonNumbericString($node->left) && $this->nodeTypeResolver->isNumberType(
$node->right
)) {
$node->left = new LNumber(0);
return $node;
}
if ($this->isStringOrStaticNonNumbericString($node->right) && $this->isNumberType($node->left)) {
if ($this->isStringOrStaticNonNumbericString($node->right) && $this->nodeTypeResolver->isNumberType(
$node->left
)) {
$node->right = new LNumber(0);
return $node;

View File

@ -7,9 +7,11 @@ namespace Rector\Php73\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Expression;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -147,4 +149,14 @@ CODE_SAMPLE
return ! $this->reflectionProvider->hasFunction(new Name(self::ARRAY_KEY_LAST), null);
}
private function getNextExpression(FuncCall $funcCall): ?Node
{
$currentExpression = $funcCall->getAttribute(AttributeKey::CURRENT_STATEMENT);
if (! $currentExpression instanceof Expression) {
return null;
}
return $currentExpression->getAttribute(AttributeKey::NEXT_NODE);
}
}

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Contract\Rector;
use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;
interface CorePhpRectorInterface extends PhpRectorInterface, DocumentedRuleInterface
{
}

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Contract\Rector;
use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;
interface CoreRectorInterface extends RectorInterface, DocumentedRuleInterface
{
}

View File

@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Rector\Core\Contract\Rector;
interface RectorInterface
use Symplify\RuleDocGenerator\Contract\DocumentedRuleInterface;
interface RectorInterface extends DocumentedRuleInterface
{
}

View File

@ -4,11 +4,614 @@ declare(strict_types=1);
namespace Rector\Core\Rector;
use Rector\Core\Contract\Rector\CorePhpRectorInterface;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Configuration\Option;
use Rector\Core\Contract\Rector\PhpRectorInterface;
use Rector\Core\Exclusion\ExclusionManager;
use Rector\Core\Logging\CurrentRectorProvider;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\Core\ValueObject\ProjectType;
use Rector\NodeCollector\NodeCollector\NodeRepository;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeRemoval\NodeRemover;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\PostRector\Collector\NodesToAddCollector;
use Rector\PostRector\Collector\NodesToRemoveCollector;
use Rector\PostRector\DependencyInjection\PropertyAdder;
use Rector\Privatization\NodeManipulator\VisibilityManipulator;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
use Symplify\Skipper\Skipper\Skipper;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
* @see \Rector\Testing\PHPUnit\AbstractRectorTestCase
*/
abstract class AbstractRector extends AbstractTemporaryRector implements CorePhpRectorInterface
abstract class AbstractRector extends NodeVisitorAbstract implements PhpRectorInterface
{
/**
* @var string[]
*/
private const ATTRIBUTES_TO_MIRROR = [
AttributeKey::PARENT_NODE,
AttributeKey::CLASS_NODE,
AttributeKey::CLASS_NAME,
AttributeKey::FILE_INFO,
AttributeKey::METHOD_NODE,
AttributeKey::USE_NODES,
AttributeKey::SCOPE,
AttributeKey::METHOD_NAME,
AttributeKey::NAMESPACE_NAME,
AttributeKey::NAMESPACE_NODE,
AttributeKey::RESOLVED_NAME,
];
/**
* @var NodeNameResolver
*/
protected $nodeNameResolver;
/**
* @var NodeTypeResolver
*/
protected $nodeTypeResolver;
/**
* @var BetterStandardPrinter
*/
protected $betterStandardPrinter;
/**
* @var RemovedAndAddedFilesCollector
*/
protected $removedAndAddedFilesCollector;
/**
* @var ParameterProvider
*/
protected $parameterProvider;
/**
* @var PhpVersionProvider
*/
protected $phpVersionProvider;
/**
* @var StaticTypeMapper
*/
protected $staticTypeMapper;
/**
* @var PhpDocInfoFactory
*/
protected $phpDocInfoFactory;
/**
* @var NodeFactory
*/
protected $nodeFactory;
/**
* @var VisibilityManipulator
*/
protected $visibilityManipulator;
/**
* @var ValueResolver
*/
protected $valueResolver;
/**
* @var NodeRepository
*/
protected $nodeRepository;
/**
* @var BetterNodeFinder
*/
protected $betterNodeFinder;
/**
* @var ClassAnalyzer
*/
protected $classNodeAnalyzer;
/**
* @var NodeRemover
*/
protected $nodeRemover;
/**
* @var RectorChangeCollector
*/
protected $rectorChangeCollector;
/**
* @var NodeComparator
*/
protected $nodeComparator;
/**
* @var PropertyAdder
*/
protected $propertyAdder;
/**
* @var SimpleCallableNodeTraverser
*/
private $simpleCallableNodeTraverser;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var ExclusionManager
*/
private $exclusionManager;
/**
* @var CurrentRectorProvider
*/
private $currentRectorProvider;
/**
* @var CurrentNodeProvider
*/
private $currentNodeProvider;
/**
* @var Skipper
*/
private $skipper;
/**
* @var string|null
*/
private $previousAppliedClass;
/**
* @var NodesToRemoveCollector
*/
private $nodesToRemoveCollector;
/**
* @var NodesToAddCollector
*/
private $nodesToAddCollector;
/**
* @required
*/
public function autowireAbstractRector(
NodesToRemoveCollector $nodesToRemoveCollector,
NodesToAddCollector $nodesToAddCollector,
RectorChangeCollector $rectorChangeCollector,
NodeRemover $nodeRemover,
PropertyAdder $propertyAdder,
RemovedAndAddedFilesCollector $removedAndAddedFilesCollector,
BetterStandardPrinter $betterStandardPrinter,
NodeNameResolver $nodeNameResolver,
NodeTypeResolver $nodeTypeResolver,
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
VisibilityManipulator $visibilityManipulator,
NodeFactory $nodeFactory,
PhpDocInfoFactory $phpDocInfoFactory,
SymfonyStyle $symfonyStyle,
PhpVersionProvider $phpVersionProvider,
ExclusionManager $exclusionManager,
StaticTypeMapper $staticTypeMapper,
ParameterProvider $parameterProvider,
CurrentRectorProvider $currentRectorProvider,
ClassAnalyzer $classAnalyzer,
CurrentNodeProvider $currentNodeProvider,
Skipper $skipper,
ValueResolver $valueResolver,
NodeRepository $nodeRepository,
BetterNodeFinder $betterNodeFinder,
NodeComparator $nodeComparator
): void {
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->nodesToAddCollector = $nodesToAddCollector;
$this->rectorChangeCollector = $rectorChangeCollector;
$this->nodeRemover = $nodeRemover;
$this->propertyAdder = $propertyAdder;
$this->removedAndAddedFilesCollector = $removedAndAddedFilesCollector;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->nodeNameResolver = $nodeNameResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->visibilityManipulator = $visibilityManipulator;
$this->nodeFactory = $nodeFactory;
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->symfonyStyle = $symfonyStyle;
$this->phpVersionProvider = $phpVersionProvider;
$this->exclusionManager = $exclusionManager;
$this->staticTypeMapper = $staticTypeMapper;
$this->parameterProvider = $parameterProvider;
$this->currentRectorProvider = $currentRectorProvider;
$this->classNodeAnalyzer = $classAnalyzer;
$this->currentNodeProvider = $currentNodeProvider;
$this->skipper = $skipper;
$this->valueResolver = $valueResolver;
$this->nodeRepository = $nodeRepository;
$this->betterNodeFinder = $betterNodeFinder;
$this->nodeComparator = $nodeComparator;
}
/**
* @return Node[]|null
*/
public function beforeTraverse(array $nodes): ?array
{
$this->previousAppliedClass = null;
return parent::beforeTraverse($nodes);
}
/**
* @return Expression|Node|null
*/
final public function enterNode(Node $node)
{
$nodeClass = get_class($node);
if (! $this->isMatchingNodeType($nodeClass)) {
return null;
}
$this->currentRectorProvider->changeCurrentRector($this);
// for PHP doc info factory and change notifier
$this->currentNodeProvider->setNode($node);
// already removed
if ($this->shouldSkipCurrentNode($node)) {
return null;
}
// show current Rector class on --debug
$this->printDebugApplying();
$originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE) ?? clone $node;
$originalNodeWithAttributes = clone $node;
$node = $this->refactor($node);
// nothing to change → continue
if (! $node instanceof Node) {
return null;
}
// changed!
if ($this->hasNodeChanged($originalNode, $node)) {
$this->mirrorAttributes($originalNodeWithAttributes, $node);
$this->updateAttributes($node);
$this->keepFileInfoAttribute($node, $originalNode);
$this->rectorChangeCollector->notifyNodeFileInfo($node);
}
// if stmt ("$value;") was replaced by expr ("$value"), add the ending ";" (Expression) to prevent breaking the code
if ($originalNode instanceof Stmt && $node instanceof Expr) {
return new Expression($node);
}
return $node;
}
protected function isName(Node $node, string $name): bool
{
return $this->nodeNameResolver->isName($node, $name);
}
protected function areNamesEqual(Node $firstNode, Node $secondNode): bool
{
return $this->nodeNameResolver->areNamesEqual($firstNode, $secondNode);
}
/**
* @param string[] $names
*/
protected function isNames(Node $node, array $names): bool
{
return $this->nodeNameResolver->isNames($node, $names);
}
protected function getName(Node $node): ?string
{
return $this->nodeNameResolver->getName($node);
}
protected function isObjectType(Node $node, ObjectType $objectType): bool
{
return $this->nodeTypeResolver->isObjectType($node, $objectType);
}
protected function getStaticType(Node $node): Type
{
return $this->nodeTypeResolver->getStaticType($node);
}
/**
* @deprecated
* Use getStaticType() instead, as single method to get types
*/
protected function getObjectType(Node $node): Type
{
return $this->nodeTypeResolver->resolve($node);
}
/**
* @param Node|Node[] $nodes
*/
protected function traverseNodesWithCallable($nodes, callable $callable): void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, $callable);
}
/**
* @param Node|Node[]|null $node
*/
protected function print($node): string
{
return $this->betterStandardPrinter->print($node);
}
protected function isAtLeastPhpVersion(int $version): bool
{
return $this->phpVersionProvider->isAtLeastPhpVersion($version);
}
protected function mirrorComments(Node $newNode, Node $oldNode): void
{
$newNode->setAttribute(AttributeKey::PHP_DOC_INFO, $oldNode->getAttribute(AttributeKey::PHP_DOC_INFO));
$newNode->setAttribute(AttributeKey::COMMENTS, $oldNode->getAttribute(AttributeKey::COMMENTS));
}
/**
* @param Stmt[] $stmts
*/
protected function unwrapStmts(array $stmts, Node $node): void
{
// move /* */ doc block from if to first element to keep it
$currentPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
foreach ($stmts as $key => $ifStmt) {
if ($key === 0) {
$ifStmt->setAttribute(AttributeKey::PHP_DOC_INFO, $currentPhpDocInfo);
// move // comments
$ifStmt->setAttribute(AttributeKey::COMMENTS, $node->getComments());
}
$this->addNodeAfterNode($ifStmt, $node);
}
}
protected function isOnClassMethodCall(Node $node, ObjectType $objectType, string $methodName): bool
{
if (! $node instanceof MethodCall) {
return false;
}
if (! $this->isObjectType($node->var, $objectType)) {
return false;
}
return $this->isName($node->name, $methodName);
}
protected function isOpenSourceProjectType(): bool
{
$projectType = $this->parameterProvider->provideStringParameter(Option::PROJECT_TYPE);
return $projectType === ProjectType::OPEN_SOURCE;
}
/**
* @param Arg[] $newArgs
* @param Arg[] $appendingArgs
* @return Arg[]
*/
protected function appendArgs(array $newArgs, array $appendingArgs): array
{
foreach ($appendingArgs as $oldArgument) {
$newArgs[] = new Arg($oldArgument->value);
}
return $newArgs;
}
protected function unwrapExpression(Stmt $stmt): Node
{
if ($stmt instanceof Expression) {
return $stmt->expr;
}
return $stmt;
}
/**
* @param Node[] $newNodes
*/
protected function addNodesAfterNode(array $newNodes, Node $positionNode): void
{
$this->nodesToAddCollector->addNodesAfterNode($newNodes, $positionNode);
}
/**
* @param Node[] $newNodes
*/
protected function addNodesBeforeNode(array $newNodes, Node $positionNode): void
{
$this->nodesToAddCollector->addNodesBeforeNode($newNodes, $positionNode);
}
protected function addNodeAfterNode(Node $newNode, Node $positionNode): void
{
$this->nodesToAddCollector->addNodeAfterNode($newNode, $positionNode);
}
protected function addNodeBeforeNode(Node $newNode, Node $positionNode): void
{
$this->nodesToAddCollector->addNodeBeforeNode($newNode, $positionNode);
}
protected function addConstructorDependencyToClass(
Class_ $class,
Type $propertyType,
string $propertyName,
int $propertyFlags = 0
): void {
$this->propertyAdder->addConstructorDependencyToClass($class, $propertyType, $propertyName, $propertyFlags);
}
protected function removeNode(Node $node): void
{
$this->nodeRemover->removeNode($node);
}
/**
* @param Class_|ClassMethod|Function_ $nodeWithStatements
*/
protected function removeNodeFromStatements(Node $nodeWithStatements, Node $nodeToRemove): void
{
$this->nodeRemover->removeNodeFromStatements($nodeWithStatements, $nodeToRemove);
}
protected function isNodeRemoved(Node $node): bool
{
return $this->nodesToRemoveCollector->isNodeRemoved($node);
}
/**
* @param Node[] $nodes
*/
protected function removeNodes(array $nodes): void
{
$this->nodeRemover->removeNodes($nodes);
}
private function isMatchingNodeType(string $nodeClass): bool
{
foreach ($this->getNodeTypes() as $nodeType) {
if (is_a($nodeClass, $nodeType, true)) {
return true;
}
}
return false;
}
private function shouldSkipCurrentNode(Node $node): bool
{
if ($this->isNodeRemoved($node)) {
return true;
}
if ($this->exclusionManager->isNodeSkippedByRector($node, $this)) {
return true;
}
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if (! $fileInfo instanceof SmartFileInfo) {
return false;
}
return $this->skipper->shouldSkipElementAndFileInfo($this, $fileInfo);
}
private function printDebugApplying(): void
{
if (! $this->symfonyStyle->isDebug()) {
return;
}
if ($this->previousAppliedClass === static::class) {
return;
}
// prevent spamming with the same class over and over
// indented on purpose to improve log nesting under [refactoring]
$this->symfonyStyle->writeln(' [applying] ' . static::class);
$this->previousAppliedClass = static::class;
}
private function hasNodeChanged(Node $originalNode, Node $node): bool
{
if ($this->isNameIdentical($node, $originalNode)) {
return false;
}
return ! $this->nodeComparator->areNodesEqual($originalNode, $node);
}
private function mirrorAttributes(Node $oldNode, Node $newNode): void
{
foreach ($oldNode->getAttributes() as $attributeName => $oldNodeAttributeValue) {
if (! in_array($attributeName, self::ATTRIBUTES_TO_MIRROR, true)) {
continue;
}
$newNode->setAttribute($attributeName, $oldNodeAttributeValue);
}
}
private function updateAttributes(Node $node): void
{
// update Resolved name attribute if name is changed
if ($node instanceof Name) {
$node->setAttribute(AttributeKey::RESOLVED_NAME, $node->toString());
}
}
private function keepFileInfoAttribute(Node $node, Node $originalNode): void
{
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if ($fileInfo instanceof SmartFileInfo) {
return;
}
$fileInfo = $originalNode->getAttribute(AttributeKey::FILE_INFO);
$originalParent = $originalNode->getAttribute(AttributeKey::PARENT_NODE);
if ($fileInfo !== null) {
$node->setAttribute(AttributeKey::FILE_INFO, $originalNode->getAttribute(AttributeKey::FILE_INFO));
} elseif ($originalParent instanceof Node) {
$node->setAttribute(AttributeKey::FILE_INFO, $originalParent->getAttribute(AttributeKey::FILE_INFO));
}
}
private function isNameIdentical(Node $node, Node $originalNode): bool
{
if (! $originalNode instanceof Name) {
return false;
}
// names are the same
return $this->nodeComparator->areNodesEqual($originalNode->getAttribute(AttributeKey::ORIGINAL_NAME), $node);
}
}

View File

@ -1,640 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\Core\Rector;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\Core\Application\FileSystem\RemovedAndAddedFilesCollector;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Configuration\Option;
use Rector\Core\Contract\Rector\PhpRectorInterface;
use Rector\Core\Exclusion\ExclusionManager;
use Rector\Core\Logging\CurrentRectorProvider;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PhpParser\Node\Value\ValueResolver;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\Core\ValueObject\ProjectType;
use Rector\NodeCollector\NodeCollector\NodeRepository;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeRemoval\NodeRemover;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\PostRector\Collector\NodesToAddCollector;
use Rector\PostRector\Collector\NodesToRemoveCollector;
use Rector\PostRector\DependencyInjection\PropertyAdder;
use Rector\Privatization\NodeManipulator\VisibilityManipulator;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
use Symplify\Skipper\Skipper\Skipper;
use Symplify\SmartFileSystem\SmartFileInfo;
abstract class AbstractTemporaryRector extends NodeVisitorAbstract implements PhpRectorInterface
{
/**
* @var string[]
*/
private const ATTRIBUTES_TO_MIRROR = [
AttributeKey::PARENT_NODE,
AttributeKey::CLASS_NODE,
AttributeKey::CLASS_NAME,
AttributeKey::FILE_INFO,
AttributeKey::METHOD_NODE,
AttributeKey::USE_NODES,
AttributeKey::SCOPE,
AttributeKey::METHOD_NAME,
AttributeKey::NAMESPACE_NAME,
AttributeKey::NAMESPACE_NODE,
AttributeKey::RESOLVED_NAME,
];
/**
* @var NodeNameResolver
*/
protected $nodeNameResolver;
/**
* @var NodeTypeResolver
*/
protected $nodeTypeResolver;
/**
* @var BetterStandardPrinter
*/
protected $betterStandardPrinter;
/**
* @var RemovedAndAddedFilesCollector
*/
protected $removedAndAddedFilesCollector;
/**
* @var ParameterProvider
*/
protected $parameterProvider;
/**
* @var PhpVersionProvider
*/
protected $phpVersionProvider;
/**
* @var StaticTypeMapper
*/
protected $staticTypeMapper;
/**
* @var PhpDocInfoFactory
*/
protected $phpDocInfoFactory;
/**
* @var NodeFactory
*/
protected $nodeFactory;
/**
* @var VisibilityManipulator
*/
protected $visibilityManipulator;
/**
* @var ValueResolver
*/
protected $valueResolver;
/**
* @var NodeRepository
*/
protected $nodeRepository;
/**
* @var BetterNodeFinder
*/
protected $betterNodeFinder;
/**
* @var ClassAnalyzer
*/
protected $classNodeAnalyzer;
/**
* @var NodeRemover
*/
protected $nodeRemover;
/**
* @var RectorChangeCollector
*/
protected $rectorChangeCollector;
/**
* @var NodeComparator
*/
protected $nodeComparator;
/**
* @var PropertyAdder
*/
protected $propertyAdder;
/**
* @var SimpleCallableNodeTraverser
*/
private $simpleCallableNodeTraverser;
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var ExclusionManager
*/
private $exclusionManager;
/**
* @var CurrentRectorProvider
*/
private $currentRectorProvider;
/**
* @var CurrentNodeProvider
*/
private $currentNodeProvider;
/**
* @var Skipper
*/
private $skipper;
/**
* @var string|null
*/
private $previousAppliedClass;
/**
* @var NodesToRemoveCollector
*/
private $nodesToRemoveCollector;
/**
* @var NodesToAddCollector
*/
private $nodesToAddCollector;
/**
* @required
*/
public function autowireAbstractTemporaryRector(
NodesToRemoveCollector $nodesToRemoveCollector,
NodesToAddCollector $nodesToAddCollector,
RectorChangeCollector $rectorChangeCollector,
NodeRemover $nodeRemover,
PropertyAdder $propertyAdder,
RemovedAndAddedFilesCollector $removedAndAddedFilesCollector,
BetterStandardPrinter $betterStandardPrinter,
NodeNameResolver $nodeNameResolver,
NodeTypeResolver $nodeTypeResolver,
SimpleCallableNodeTraverser $simpleCallableNodeTraverser,
VisibilityManipulator $visibilityManipulator,
NodeFactory $nodeFactory,
PhpDocInfoFactory $phpDocInfoFactory,
SymfonyStyle $symfonyStyle,
PhpVersionProvider $phpVersionProvider,
ExclusionManager $exclusionManager,
StaticTypeMapper $staticTypeMapper,
ParameterProvider $parameterProvider,
CurrentRectorProvider $currentRectorProvider,
ClassAnalyzer $classAnalyzer,
CurrentNodeProvider $currentNodeProvider,
Skipper $skipper,
ValueResolver $valueResolver,
NodeRepository $nodeRepository,
BetterNodeFinder $betterNodeFinder,
NodeComparator $nodeComparator
): void {
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->nodesToAddCollector = $nodesToAddCollector;
$this->rectorChangeCollector = $rectorChangeCollector;
$this->nodeRemover = $nodeRemover;
$this->propertyAdder = $propertyAdder;
$this->removedAndAddedFilesCollector = $removedAndAddedFilesCollector;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->nodeNameResolver = $nodeNameResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->visibilityManipulator = $visibilityManipulator;
$this->nodeFactory = $nodeFactory;
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->symfonyStyle = $symfonyStyle;
$this->phpVersionProvider = $phpVersionProvider;
$this->exclusionManager = $exclusionManager;
$this->staticTypeMapper = $staticTypeMapper;
$this->parameterProvider = $parameterProvider;
$this->currentRectorProvider = $currentRectorProvider;
$this->classNodeAnalyzer = $classAnalyzer;
$this->currentNodeProvider = $currentNodeProvider;
$this->skipper = $skipper;
$this->valueResolver = $valueResolver;
$this->nodeRepository = $nodeRepository;
$this->betterNodeFinder = $betterNodeFinder;
$this->nodeComparator = $nodeComparator;
}
/**
* @return Node[]|null
*/
public function beforeTraverse(array $nodes): ?array
{
$this->previousAppliedClass = null;
return parent::beforeTraverse($nodes);
}
/**
* @return Expression|Node|null
*/
final public function enterNode(Node $node)
{
$nodeClass = get_class($node);
if (! $this->isMatchingNodeType($nodeClass)) {
return null;
}
$this->currentRectorProvider->changeCurrentRector($this);
// for PHP doc info factory and change notifier
$this->currentNodeProvider->setNode($node);
// already removed
if ($this->shouldSkipCurrentNode($node)) {
return null;
}
// show current Rector class on --debug
$this->printDebugApplying();
$originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE) ?? clone $node;
$originalNodeWithAttributes = clone $node;
$node = $this->refactor($node);
// nothing to change → continue
if (! $node instanceof Node) {
return null;
}
// changed!
if ($this->hasNodeChanged($originalNode, $node)) {
$this->mirrorAttributes($originalNodeWithAttributes, $node);
$this->updateAttributes($node);
$this->keepFileInfoAttribute($node, $originalNode);
$this->rectorChangeCollector->notifyNodeFileInfo($node);
}
// if stmt ("$value;") was replaced by expr ("$value"), add the ending ";" (Expression) to prevent breaking the code
if ($originalNode instanceof Stmt && $node instanceof Expr) {
return new Expression($node);
}
return $node;
}
protected function isName(Node $node, string $name): bool
{
return $this->nodeNameResolver->isName($node, $name);
}
protected function areNamesEqual(Node $firstNode, Node $secondNode): bool
{
return $this->nodeNameResolver->areNamesEqual($firstNode, $secondNode);
}
/**
* @param string[] $names
*/
protected function isNames(Node $node, array $names): bool
{
return $this->nodeNameResolver->isNames($node, $names);
}
protected function getName(Node $node): ?string
{
return $this->nodeNameResolver->getName($node);
}
protected function isObjectType(Node $node, ObjectType $objectType): bool
{
return $this->nodeTypeResolver->isObjectType($node, $objectType);
}
protected function isNumberType(Node $node): bool
{
return $this->nodeTypeResolver->isNumberType($node);
}
protected function getStaticType(Node $node): Type
{
return $this->nodeTypeResolver->getStaticType($node);
}
/**
* @deprecated
* Use getStaticType() instead, as single method to get types
*/
protected function getObjectType(Node $node): Type
{
return $this->nodeTypeResolver->resolve($node);
}
/**
* @param Node|Node[] $nodes
*/
protected function traverseNodesWithCallable($nodes, callable $callable): void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($nodes, $callable);
}
/**
* @param Node|Node[]|null $node
*/
protected function print($node): string
{
return $this->betterStandardPrinter->print($node);
}
/**
* Removes all comments from both nodes
*
* @param Node|Node[]|null $firstNode
* @param Node|Node[]|null $secondNode
*/
protected function areNodesEqual($firstNode, $secondNode): bool
{
return $this->nodeComparator->areNodesEqual($firstNode, $secondNode);
}
protected function getNextExpression(Node $node): ?Node
{
$currentExpression = $node->getAttribute(AttributeKey::CURRENT_STATEMENT);
if (! $currentExpression instanceof Expression) {
return null;
}
return $currentExpression->getAttribute(AttributeKey::NEXT_NODE);
}
protected function isAtLeastPhpVersion(int $version): bool
{
return $this->phpVersionProvider->isAtLeastPhpVersion($version);
}
protected function mirrorComments(Node $newNode, Node $oldNode): void
{
$newNode->setAttribute(AttributeKey::PHP_DOC_INFO, $oldNode->getAttribute(AttributeKey::PHP_DOC_INFO));
$newNode->setAttribute(AttributeKey::COMMENTS, $oldNode->getAttribute(AttributeKey::COMMENTS));
}
/**
* @param Stmt[] $stmts
*/
protected function unwrapStmts(array $stmts, Node $node): void
{
// move /* */ doc block from if to first element to keep it
$currentPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
foreach ($stmts as $key => $ifStmt) {
if ($key === 0) {
$ifStmt->setAttribute(AttributeKey::PHP_DOC_INFO, $currentPhpDocInfo);
// move // comments
$ifStmt->setAttribute(AttributeKey::COMMENTS, $node->getComments());
}
$this->addNodeAfterNode($ifStmt, $node);
}
}
protected function isOnClassMethodCall(Node $node, ObjectType $objectType, string $methodName): bool
{
if (! $node instanceof MethodCall) {
return false;
}
if (! $this->isObjectType($node->var, $objectType)) {
return false;
}
return $this->isName($node->name, $methodName);
}
protected function isOpenSourceProjectType(): bool
{
$projectType = $this->parameterProvider->provideStringParameter(Option::PROJECT_TYPE);
return $projectType === ProjectType::OPEN_SOURCE;
}
/**
* @param Arg[] $newArgs
* @param Arg[] $appendingArgs
* @return Arg[]
*/
protected function appendArgs(array $newArgs, array $appendingArgs): array
{
foreach ($appendingArgs as $oldArgument) {
$newArgs[] = new Arg($oldArgument->value);
}
return $newArgs;
}
protected function unwrapExpression(Stmt $stmt): Node
{
if ($stmt instanceof Expression) {
return $stmt->expr;
}
return $stmt;
}
/**
* @param Node[] $newNodes
*/
protected function addNodesAfterNode(array $newNodes, Node $positionNode): void
{
$this->nodesToAddCollector->addNodesAfterNode($newNodes, $positionNode);
}
/**
* @param Node[] $newNodes
*/
protected function addNodesBeforeNode(array $newNodes, Node $positionNode): void
{
$this->nodesToAddCollector->addNodesBeforeNode($newNodes, $positionNode);
}
protected function addNodeAfterNode(Node $newNode, Node $positionNode): void
{
$this->nodesToAddCollector->addNodeAfterNode($newNode, $positionNode);
}
protected function addNodeBeforeNode(Node $newNode, Node $positionNode): void
{
$this->nodesToAddCollector->addNodeBeforeNode($newNode, $positionNode);
}
protected function addConstructorDependencyToClass(
Class_ $class,
Type $propertyType,
string $propertyName,
int $propertyFlags = 0
): void {
$this->propertyAdder->addConstructorDependencyToClass($class, $propertyType, $propertyName, $propertyFlags);
}
protected function removeNode(Node $node): void
{
$this->nodeRemover->removeNode($node);
}
/**
* @param Class_|ClassMethod|Function_ $nodeWithStatements
*/
protected function removeNodeFromStatements(Node $nodeWithStatements, Node $nodeToRemove): void
{
$this->nodeRemover->removeNodeFromStatements($nodeWithStatements, $nodeToRemove);
}
protected function isNodeRemoved(Node $node): bool
{
return $this->nodesToRemoveCollector->isNodeRemoved($node);
}
/**
* @param Node[] $nodes
*/
protected function removeNodes(array $nodes): void
{
$this->nodeRemover->removeNodes($nodes);
}
private function isMatchingNodeType(string $nodeClass): bool
{
foreach ($this->getNodeTypes() as $nodeType) {
if (is_a($nodeClass, $nodeType, true)) {
return true;
}
}
return false;
}
private function shouldSkipCurrentNode(Node $node): bool
{
if ($this->isNodeRemoved($node)) {
return true;
}
if ($this->exclusionManager->isNodeSkippedByRector($node, $this)) {
return true;
}
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if (! $fileInfo instanceof SmartFileInfo) {
return false;
}
return $this->skipper->shouldSkipElementAndFileInfo($this, $fileInfo);
}
private function printDebugApplying(): void
{
if (! $this->symfonyStyle->isDebug()) {
return;
}
if ($this->previousAppliedClass === static::class) {
return;
}
// prevent spamming with the same class over and over
// indented on purpose to improve log nesting under [refactoring]
$this->symfonyStyle->writeln(' [applying] ' . static::class);
$this->previousAppliedClass = static::class;
}
private function hasNodeChanged(Node $originalNode, Node $node): bool
{
if ($this->isNameIdentical($node, $originalNode)) {
return false;
}
return ! $this->nodeComparator->areNodesEqual($originalNode, $node);
}
private function mirrorAttributes(Node $oldNode, Node $newNode): void
{
foreach ($oldNode->getAttributes() as $attributeName => $oldNodeAttributeValue) {
if (! in_array($attributeName, self::ATTRIBUTES_TO_MIRROR, true)) {
continue;
}
$newNode->setAttribute($attributeName, $oldNodeAttributeValue);
}
}
private function updateAttributes(Node $node): void
{
// update Resolved name attribute if name is changed
if ($node instanceof Name) {
$node->setAttribute(AttributeKey::RESOLVED_NAME, $node->toString());
}
}
private function keepFileInfoAttribute(Node $node, Node $originalNode): void
{
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if ($fileInfo instanceof SmartFileInfo) {
return;
}
$fileInfo = $originalNode->getAttribute(AttributeKey::FILE_INFO);
$originalParent = $originalNode->getAttribute(AttributeKey::PARENT_NODE);
if ($fileInfo !== null) {
$node->setAttribute(AttributeKey::FILE_INFO, $originalNode->getAttribute(AttributeKey::FILE_INFO));
} elseif ($originalParent instanceof Node) {
$node->setAttribute(AttributeKey::FILE_INFO, $originalParent->getAttribute(AttributeKey::FILE_INFO));
}
}
private function isNameIdentical(Node $node, Node $originalNode): bool
{
if (! $originalNode instanceof Name) {
return false;
}
// names are the same
return $this->nodeComparator->areNodesEqual($originalNode->getAttribute(AttributeKey::ORIGINAL_NAME), $node);
}
}

View File

@ -4,15 +4,42 @@ declare(strict_types=1);
namespace Rector\Utils\DoctrineAnnotationParserSyncer\Rector\StaticCall;
use Doctrine\Common\Annotations\AnnotationRegistry;
use PhpParser\Node;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractTemporaryRector;
use Rector\Core\Rector\AbstractRector;
use Rector\Utils\DoctrineAnnotationParserSyncer\Contract\Rector\ClassSyncerRectorInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class RemoveAnnotationRegistryRegisterFileRector extends AbstractTemporaryRector implements ClassSyncerRectorInterface
final class RemoveAnnotationRegistryRegisterFileRector extends AbstractRector implements ClassSyncerRectorInterface
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove registerFile() static calls from AnnotationParser', [
new CodeSample(
<<<'CODE_SAMPLE'
class AnnotationParser
{
public function run()
{
Doctrine\Common\Annotations\AnnotationRegistry::registerFile('...');
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class AnnotationParser
{
public function run()
{
}
}
CODE_SAMPLE
),
]);
}
/**
* @return array<class-string<\PhpParser\Node>>
*/
@ -35,7 +62,11 @@ final class RemoveAnnotationRegistryRegisterFileRector extends AbstractTemporary
return null;
}
if (! $this->nodeNameResolver->isStaticCallNamed($node, AnnotationRegistry::class, 'registerFile')) {
if (! $this->nodeNameResolver->isStaticCallNamed(
$node,
'Doctrine\Common\Annotations\AnnotationRegistry',
'registerFile'
)) {
return null;
}

View File

@ -11,6 +11,7 @@ services:
- PHPStan\Type\StaticType
- Rector\BetterPhpDocParser\PhpDocNodeFactory\AbstractPhpDocNodeFactory
- PHPStan\PhpDocParser\Parser\PhpDocParser
- Rector\PostRector\Rector\AbstractPostRector
# value objects
- Rector\Defluent\ValueObject\AbstractRootExpr
- Symplify\SetConfigResolver\Provider\AbstractSetProvider

View File

@ -10,6 +10,7 @@ use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\DynamicMethodReturnTypeExtension;
use PHPStan\Type\Type;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Rector\AbstractTemporaryRector;
use Rector\PHPStanExtensions\TypeResolver\MethodCallTypeResolver;
@ -32,7 +33,7 @@ final class NameResolverReturnTypeAbstractRectorExtension implements DynamicMeth
public function getClass(): string
{
return AbstractTemporaryRector::class;
return AbstractRector::class;
}
public function isMethodSupported(MethodReflection $methodReflection): bool