mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-17 05:18:18 +01:00
remove complicated examples, updated README with real use-cases
This commit is contained in:
parent
a7073c2935
commit
a4b40c60c4
@ -36,12 +36,12 @@ Rector **instantly upgrades and instantly refactors PHP code of your application
|
||||
|
||||
**Rector can**:
|
||||
|
||||
- Rename classes, methods and properties
|
||||
- Rename partial namespace
|
||||
- Rename pseudo-namespace to namespace
|
||||
- Rename classes, methods, properties, namespaces, constants... anything :)
|
||||
- Add, replace or remove arguments
|
||||
- Add arguments or return typehint
|
||||
- Add [parameter or return type declarations](https://www.tomasvotruba.cz/blog/2019/01/03/how-to-complete-type-declarations-without-docblocks-with-rector/) without docblocks - just with static analysis
|
||||
- Change visibility of constant, property or method
|
||||
- Upgrade from PHP 5.3 to PHP 7.4
|
||||
- [Complete PHP 7.4 property type declarations](https://www.tomasvotruba.cz/blog/2018/11/15/how-to-get-php-74-typed-properties-to-your-code-in-few-seconds/)
|
||||
- And much more...
|
||||
|
||||
...**look at overview of [all available Rectors](/docs/AllRectorsOverview.md)** with before/after diffs and configuration examples. You can use them to build your own sets.
|
||||
|
@ -89,7 +89,6 @@
|
||||
"Rector\\PHPStanExtensions\\": "utils/PHPStanExtensions/src"
|
||||
},
|
||||
"classmap": [
|
||||
"examples",
|
||||
"packages/Symfony/tests/Rector/FrameworkBundle/AbstractToConstructorInjectionRectorSource",
|
||||
"packages/Symfony/tests/Rector/FrameworkBundle/ContainerGetToConstructorInjectionRector/Source",
|
||||
"packages/NodeTypeResolver/tests/PerNodeTypeResolver/ParamTypeResolver/Source",
|
||||
|
1
ecs.yml
1
ecs.yml
@ -109,7 +109,6 @@ parameters:
|
||||
- 'packages/Php/src/EregToPcreTransformer.php'
|
||||
# dev
|
||||
- 'packages/Php/src/Rector/FunctionLike/*TypeDeclarationRector.php'
|
||||
- 'examples/*'
|
||||
- 'packages/Php/src/Rector/If_/IfToSpaceshipRector.php'
|
||||
|
||||
Symplify\CodingStandard\Sniffs\ControlStructure\SprintfOverContactSniff:
|
||||
|
@ -1,132 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Examples;
|
||||
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use Rector\PhpParser\Node\Manipulator\ClassManipulator;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
final class ConfiguredProvideConfigRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var ClassManipulator
|
||||
*/
|
||||
private $classManipulator;
|
||||
|
||||
/**
|
||||
* @var BuilderFactory
|
||||
*/
|
||||
private $builderFactory;
|
||||
|
||||
public function __construct(ClassManipulator $classManipulator, BuilderFactory $builderFactory)
|
||||
{
|
||||
$this->classManipulator = $classManipulator;
|
||||
$this->builderFactory = $builderFactory;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Simplify tests', [new CodeSample('', '')]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isType($node, AbstractRectorTestCase::class) || $node->isAbstract()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$classMethodsByName = $this->classManipulator->getMethodsByName($node);
|
||||
if (! isset($classMethodsByName['provideConfig'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// already changed
|
||||
if (isset($classMethodsByName['getRectorConfiguration'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$provideConfigMethod = $classMethodsByName['provideConfig'];
|
||||
|
||||
if ($provideConfigMethod->stmts[0] instanceof Return_) {
|
||||
/** @var Return_ $returnNode */
|
||||
$returnNode = $provideConfigMethod->stmts[0];
|
||||
|
||||
$configPath = $this->getValue($returnNode->expr);
|
||||
$yaml = Yaml::parseFile($configPath);
|
||||
|
||||
$resolved = $this->resolveClassToConfigurationFromConfig($yaml);
|
||||
if ($resolved === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$class, $configuration] = $resolved;
|
||||
|
||||
$returnNode = new Return_($this->createClassConstant($class, 'class'));
|
||||
$node->stmts[] = $this->builderFactory->method('getRectorClass')
|
||||
->makeProtected()
|
||||
->setReturnType('string')
|
||||
->addStmt($returnNode)
|
||||
->getNode();
|
||||
|
||||
$returnNode = new Return_(BuilderHelpers::normalizeValue($configuration));
|
||||
$node->stmts[] = $this->builderFactory->method('getRectorConfiguration')
|
||||
->makeProtected()
|
||||
->setReturnType('array')
|
||||
->setDocComment("/**\n * @return mixed[]\n */")
|
||||
->addStmt($returnNode)
|
||||
->getNode();
|
||||
|
||||
$this->removeNode($provideConfigMethod);
|
||||
|
||||
// remove config file
|
||||
unlink($configPath);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $yaml
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
private function resolveClassToConfigurationFromConfig(array $yaml): ?array
|
||||
{
|
||||
if (count($yaml) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($yaml['services'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// has only "services" keys
|
||||
if (count($yaml['services']) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$class = key($yaml['services']);
|
||||
// only service
|
||||
$configuration = array_pop($yaml['services']);
|
||||
|
||||
return [$class, $configuration];
|
||||
}
|
||||
}
|
@ -1,310 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* This class was used to automate huge refactoring in https://github.com/rectorphp/rector/pull/584
|
||||
*
|
||||
* It helped with 7-step refactoring of 96 files:
|
||||
* - 1. replace "isCandidate()" method by "getNodeType()" method
|
||||
* - 2. rename "return false;" to "return null;" to respect "refactor(Node $node): ?Node" typehint
|
||||
* - 3. rename used variable "$node" to "$specificTypeParam"
|
||||
* - 4. turn last return in "isCondition()" with early return
|
||||
* - 5. return true makes no sense anymore, just continue
|
||||
* - 6. remove first "instanceof", already covered by getNodeType()
|
||||
* - 7. add contents of "isCandidate()" method to start of "refactor()" method
|
||||
*
|
||||
* It took ~2 hours to setup. Saved work of 5-6 hours and much more stress :)
|
||||
*/
|
||||
|
||||
namespace Rector\Examples;
|
||||
|
||||
use PhpParser\BuilderFactory;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\BinaryOp\Identical;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\ConstFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\If_;
|
||||
use PhpParser\Node\Stmt\Nop;
|
||||
use PhpParser\Node\Stmt\Return_;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator;
|
||||
use Rector\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class MergeIsCandidateRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var BuilderFactory
|
||||
*/
|
||||
private $builderFactory;
|
||||
|
||||
/**
|
||||
* @var DocBlockManipulator
|
||||
*/
|
||||
private $docBlockManipulator;
|
||||
|
||||
/**
|
||||
* @var CallableNodeTraverser
|
||||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
public function __construct(
|
||||
BuilderFactory $builderFactory,
|
||||
DocBlockManipulator $docBlockManipulator,
|
||||
CallableNodeTraverser $callableNodeTraverser
|
||||
) {
|
||||
$this->builderFactory = $builderFactory;
|
||||
$this->docBlockManipulator = $docBlockManipulator;
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition(
|
||||
'Finds all "Rector\Rector\AbstractRector" instances, merges "isCandidate()" method to "refactor()" method and creates "getNodeType()" method by @param annotation of "refactor()" method.',
|
||||
[new CodeSample('', '')]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $this->isType($node, AbstractRector::class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $node->isAbstract()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// has "isCandidate()" method?
|
||||
if (! $this->hasClassIsCandidateMethod($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
[$isCandidateClassMethodPosition, $isCandidateClassMethod] = $this->getClassMethodByName($node, 'isCandidate');
|
||||
[$refactorClassMethodPosition, $refactorClassMethod] = $this->getClassMethodByName($node, 'refactor');
|
||||
|
||||
if ($refactorClassMethod === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 1. replace "isCandidate()" method by "getNodeType()" method
|
||||
$node->stmts[$isCandidateClassMethodPosition] = $this->createGetNodeTypeClassMethod($refactorClassMethod);
|
||||
|
||||
// 2. rename "return false;" to "return null;" to respect "refactor(Node $node): ?Node" typehint
|
||||
$this->replaceReturnFalseWithReturnNull($isCandidateClassMethod);
|
||||
|
||||
// 3. rename used variable "$node" to "$specificTypeParam"
|
||||
$this->renameNodeToParamNode($isCandidateClassMethod, $refactorClassMethod->params[0]->var->name);
|
||||
|
||||
// 4. turn last return in "isCondition()" with early return
|
||||
$this->replaceLastReturnWithIf($isCandidateClassMethod);
|
||||
|
||||
// 5. return true makes no sense anymore, just continue
|
||||
$this->removeReturnTrue($isCandidateClassMethod);
|
||||
|
||||
// 6. remove first "instanceof", already covered by getNodeType()
|
||||
$isCandidateClassMethod = $this->removeFirstInstanceOf($isCandidateClassMethod);
|
||||
|
||||
// 7. add contents of "isCandidate()" method to start of "refactor()" method
|
||||
$refactorClassMethod->stmts = array_merge($isCandidateClassMethod->stmts, $refactorClassMethod->stmts);
|
||||
|
||||
$node->stmts[$refactorClassMethodPosition] = $refactorClassMethod;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function hasClassIsCandidateMethod(Class_ $classNode): bool
|
||||
{
|
||||
return (bool) $this->getClassMethodByName($classNode, 'isCandidate');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[]|ClassMethod[]|null
|
||||
*/
|
||||
private function getClassMethodByName(Class_ $classNode, string $name)
|
||||
{
|
||||
foreach ($classNode->stmts as $i => $stmt) {
|
||||
if (! $stmt instanceof ClassMethod) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isName($stmt->name, $name)) {
|
||||
return [$i, $stmt];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveParamTagValueNodeToStrings(ParamTagValueNode $paramTagValueNode): array
|
||||
{
|
||||
$types = [];
|
||||
|
||||
if ($paramTagValueNode->type instanceof UnionTypeNode) {
|
||||
foreach ($paramTagValueNode->type->types as $type) {
|
||||
$types[] = (string) $type;
|
||||
}
|
||||
} elseif ($paramTagValueNode->type instanceof IdentifierTypeNode) {
|
||||
$types[] = $paramTagValueNode->type->name;
|
||||
}
|
||||
// todo: resolve
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
private function createGetNodeTypeClassMethod(ClassMethod $refactorClassMethod): ClassMethod
|
||||
{
|
||||
$paramTypes = $this->resolveSingleParamTypesFromClassMethod($refactorClassMethod);
|
||||
|
||||
$nodeToBeReturned = new Array_();
|
||||
|
||||
if (count($paramTypes) > 1) {
|
||||
foreach ($paramTypes as $paramType) {
|
||||
$classConstFetchNode = $this->createClassConstant($paramType, 'class');
|
||||
$nodeToBeReturned->items[] = new ArrayItem($classConstFetchNode);
|
||||
}
|
||||
} elseif (count($paramTypes) === 1) {
|
||||
$nodeToBeReturned->items[] = $this->createClassConstant($paramTypes[0], 'class');
|
||||
} else { // fallback to basic node
|
||||
$nodeToBeReturned->items[] = $this->createClassConstant(Node::class, 'class');
|
||||
}
|
||||
|
||||
return $this->builderFactory->method('getNodeTypes')
|
||||
->makePublic()
|
||||
->setReturnType('array')
|
||||
->addStmt(new Return_($nodeToBeReturned))
|
||||
->getNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveSingleParamTypesFromClassMethod(ClassMethod $classMethod): array
|
||||
{
|
||||
// add getNodeType() by $refactorClassMethod "@param" doc type
|
||||
if (! $this->docBlockManipulator->hasTag($classMethod, 'param')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$paramNode = $this->docBlockManipulator->getTagByName($classMethod, 'param');
|
||||
|
||||
/** @var ParamTagValueNode $paramTagValueNode */
|
||||
$paramTagValueNode = $paramNode->value;
|
||||
|
||||
return $this->resolveParamTagValueNodeToStrings($paramTagValueNode);
|
||||
}
|
||||
|
||||
private function replaceReturnFalseWithReturnNull(ClassMethod $classMethod): void
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable([$classMethod], function (Node $node): ?Node {
|
||||
if (! $node instanceof Return_ || ! $node->expr instanceof ConstFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->isFalse($node->expr)) {
|
||||
return new Return_(new ConstFetch(new Name('null')));
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private function renameNodeToParamNode(ClassMethod $classMethod, string $nodeName): void
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable([$classMethod], function (Node $node) use (
|
||||
$nodeName
|
||||
): ?Node {
|
||||
if (! $node instanceof Variable || ! $this->isName($node, 'node')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$node->name = $nodeName;
|
||||
|
||||
return $node;
|
||||
});
|
||||
}
|
||||
|
||||
private function replaceLastReturnWithIf(ClassMethod $classMethod): void
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable([$classMethod], function (Node $node): ?Node {
|
||||
if (! $node instanceof Return_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node->expr instanceof ConstFetch) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$identicalCondition = new Identical($node->expr, new ConstFetch(new Name('false')));
|
||||
return new If_($identicalCondition, [
|
||||
'stmts' => [new Return_(new ConstFetch(new Name('null')))],
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
private function removeReturnTrue(ClassMethod $classMethod): void
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable([$classMethod], function (Node $node): ?Node {
|
||||
if (! $node instanceof Return_ || ! $node->expr instanceof ConstFetch || ! $this->isTrue($node->expr)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Nop();
|
||||
});
|
||||
}
|
||||
|
||||
private function removeFirstInstanceOf(ClassMethod $classMethod): ClassMethod
|
||||
{
|
||||
if (! isset($classMethod->stmts[0])) {
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
if (! $classMethod->stmts[0] instanceof If_) {
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
/** @var If_ $ifNode */
|
||||
$ifNode = $classMethod->stmts[0];
|
||||
if (! $ifNode->stmts[0] instanceof Return_) {
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
/** @var Return_ $returnNode */
|
||||
$returnNode = $ifNode->stmts[0];
|
||||
if (! $returnNode->expr instanceof ConstFetch) {
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
$constFetchNode = $returnNode->expr;
|
||||
if ($constFetchNode->name->toString() === null) {
|
||||
unset($classMethod->stmts[0]);
|
||||
}
|
||||
|
||||
return $classMethod;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user