remove complicated examples, updated README with real use-cases

This commit is contained in:
Tomas Votruba 2019-03-09 13:23:11 +00:00
parent a7073c2935
commit a4b40c60c4
5 changed files with 4 additions and 448 deletions

View File

@ -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.

View File

@ -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",

View File

@ -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:

View File

@ -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];
}
}

View File

@ -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;
}
}