add ReplaceVariableByPropertyFetchRector

This commit is contained in:
Tomas Votruba 2018-05-05 13:39:39 +01:00
parent af5cc5beb2
commit ade19f6a7d
4 changed files with 151 additions and 8 deletions

View File

@ -1,2 +1,3 @@
services:
Rector\Rector\Architecture\DependencyInjection\ActionInjectionToConstructorInjectionRector: ~
Rector\Rector\Architecture\DependencyInjection\ReplaceVariableByPropertyFetchRector: ~

View File

@ -0,0 +1,29 @@
<?php declare(strict_types=1);
namespace Rector\Configuration\Rector\Architecture\DependencyInjection;
use Rector\Builder\Class_\VariableInfo;
final class VariablesToPropertyFetchCollection
{
/**
* @var VariableInfo[]
*/
private $variableInfos = [];
/**
* @param string[] $types
*/
public function addVariableInfo(VariableInfo $variableInfo): void
{
$this->variableInfos[] = $variableInfo;
}
/**
* @return VariableInfo[]
*/
public function getVariableInfos(): array
{
return $this->variableInfos;
}
}

View File

@ -7,9 +7,11 @@ use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Builder\Class_\VariableInfo;
use Rector\Builder\Class_\VariableInfoFactory;
use Rector\Builder\ConstructorMethodBuilder;
use Rector\Builder\PropertyBuilder;
use Rector\Configuration\Rector\Architecture\DependencyInjection\VariablesToPropertyFetchCollection;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
@ -30,15 +32,21 @@ final class ActionInjectionToConstructorInjectionRector extends AbstractRector
* @var VariableInfoFactory
*/
private $variableInfoFactory;
/**
* @var VariablesToPropertyFetchCollection
*/
private $variablesToPropertyFetchCollection;
public function __construct(
PropertyBuilder $propertyBuilder,
ConstructorMethodBuilder $constructorMethodBuilder,
VariableInfoFactory $variableInfoFactory
VariableInfoFactory $variableInfoFactory,
VariablesToPropertyFetchCollection $variablesToPropertyFetchCollection
) {
$this->propertyBuilder = $propertyBuilder;
$this->constructorMethodBuilder = $constructorMethodBuilder;
$this->variableInfoFactory = $variableInfoFactory;
$this->variablesToPropertyFetchCollection = $variablesToPropertyFetchCollection;
}
public function isCandidate(Node $node): bool
@ -109,10 +117,14 @@ CODE_SAMPLE
continue;
}
$this->addConstructorDependencyToClassNode($classNode, $paramNode->var->name, [(string) $paramNode->type]);
$variableInfo = $this->variableInfoFactory->createFromNameAndTypes($paramNode->var->name, [(string) $paramNode->type]);
$this->addConstructorDependencyToClassNode($classNode, $variableInfo);
// remove arguments
unset($classMethodNode->params[$key]);
$this->variablesToPropertyFetchCollection->addVariableInfo($variableInfo);
}
}
@ -136,13 +148,8 @@ CODE_SAMPLE
return true;
}
/**
* @param string[] $variablesTypes
*/
private function addConstructorDependencyToClassNode(Class_ $classNode, string $variableName, array $variablesTypes): void
private function addConstructorDependencyToClassNode(Class_ $classNode, VariableInfo $variableInfo): void
{
$variableInfo = $this->variableInfoFactory->createFromNameAndTypes($variableName, $variablesTypes);
// add property
$this->propertyBuilder->addPropertyToClass($classNode, $variableInfo);

View File

@ -0,0 +1,106 @@
<?php declare(strict_types=1);
namespace Rector\Rector\Architecture\DependencyInjection;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Builder\Class_\VariableInfo;
use Rector\Configuration\Rector\Architecture\DependencyInjection\VariablesToPropertyFetchCollection;
use Rector\Node\Attribute;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
final class ReplaceVariableByPropertyFetchRector extends AbstractRector
{
/**
* @var VariablesToPropertyFetchCollection
*/
private $variablesToPropertyFetchCollection;
/**
* @var VariableInfo|null
*/
private $activeVariableInfo;
public function __construct(VariablesToPropertyFetchCollection $variablesToPropertyFetchCollection)
{
$this->variablesToPropertyFetchCollection = $variablesToPropertyFetchCollection;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Turns variable to property fetch, as follow up to action injection variable to property change', [
new CodeSample(
<<<'CODE_SAMPLE'
final class SomeController
{
public function default()
{
$products = $productRepository->fetchAll();
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
final class SomeController
{
public function default()
{
$products = $this->productRepository->fetchAll();
}
}
CODE_SAMPLE
),
]);
}
public function isCandidate(Node $node): bool
{
$this->activeVariableInfo = null;
if (! $node instanceof Variable) {
return false;
}
if (! Strings::endsWith((string) $node->getAttribute(Attribute::CLASS_NAME), 'Controller')) {
return false;
}
/** @var ClassMethod|null $methodNode */
$methodNode = $node->getAttribute(Attribute::METHOD_NODE);
if ($methodNode === null) {
return false;
}
// is probably in controller action
if (! $methodNode->isPublic()) {
return false;
}
foreach ($this->variablesToPropertyFetchCollection->getVariableInfos() as $variableInfo) {
if ($node->name !== $variableInfo->getName()) {
continue;
}
if ($node->getAttribute(Attribute::TYPES) === $variableInfo->getTypes()) {
$this->activeVariableInfo = $variableInfo;
return true;
}
}
return false;
}
/**
* @param Variable $variableNode
*/
public function refactor(Node $variableNode): ?Node
{
// @todo NodeFactory
return new PropertyFetch(new Variable('this'), $this->activeVariableInfo->getName());
}
}