rector/src/Rector/AbstractRector/ComplexRemovalTrait.php

214 lines
6.2 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Rector\Core\Rector\AbstractRector;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\Manipulator\PropertyManipulator;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
2020-07-30 01:39:41 +02:00
use Rector\Core\ValueObject\MethodName;
use Rector\DeadCode\NodeManipulator\LivingCodeManipulator;
2020-02-10 10:05:15 +01:00
use Rector\NodeCollector\NodeCollector\ParsedNodeCollector;
use Rector\NodeRemoval\AssignRemover;
use Rector\NodeRemoval\ClassMethodRemover;
use Rector\NodeTypeResolver\Node\AttributeKey;
2020-03-31 18:39:08 +02:00
use Rector\PostRector\Collector\NodesToRemoveCollector;
/**
* Located in another trait
2020-03-31 18:39:08 +02:00
* @property NodesToRemoveCollector $nodesToRemoveCollector
*/
trait ComplexRemovalTrait
{
/**
* @var ParsedNodeCollector
*/
protected $parsedNodeCollector;
/**
* @var LivingCodeManipulator
*/
protected $livingCodeManipulator;
/**
* @var BetterStandardPrinter
*/
protected $betterStandardPrinter;
/**
* @var PropertyManipulator
*/
private $propertyManipulator;
/**
* @var ClassMethodRemover
*/
private $classMethodRemover;
/**
* @var AssignRemover
*/
private $assignRemover;
/**
* @required
*/
public function autowireComplexRemovalTrait(
2019-11-09 00:12:44 +01:00
PropertyManipulator $propertyManipulator,
ParsedNodeCollector $parsedNodeCollector,
LivingCodeManipulator $livingCodeManipulator,
BetterStandardPrinter $betterStandardPrinter,
ClassMethodRemover $classMethodRemover,
AssignRemover $assignRemover
2019-11-09 00:12:44 +01:00
): void {
$this->parsedNodeCollector = $parsedNodeCollector;
2019-11-09 00:12:44 +01:00
$this->propertyManipulator = $propertyManipulator;
$this->livingCodeManipulator = $livingCodeManipulator;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->classMethodRemover = $classMethodRemover;
$this->assignRemover = $assignRemover;
}
protected function removeClassMethodAndUsages(ClassMethod $classMethod): void
{
$this->classMethodRemover->removeClassMethodAndUsages($classMethod);
}
/**
* @param string[] $classMethodNamesToSkip
*/
2020-04-29 13:42:11 +02:00
protected function removePropertyAndUsages(Property $property, array $classMethodNamesToSkip = []): void
{
$shouldKeepProperty = false;
$propertyFetches = $this->propertyManipulator->getPrivatePropertyFetches($property);
foreach ($propertyFetches as $propertyFetch) {
if ($this->shouldSkipPropertyForClassMethod($propertyFetch, $classMethodNamesToSkip)) {
$shouldKeepProperty = true;
continue;
}
// remove assigns
$assign = $this->resolveAssign($propertyFetch);
$this->assignRemover->removeAssignNode($assign);
$this->removeConstructorDependency($assign);
}
if ($shouldKeepProperty) {
return;
}
// remove __construct param
/** @var Property $property */
2020-04-29 13:42:11 +02:00
$this->removeNode($property);
foreach ($property->props as $prop) {
2020-03-31 18:39:08 +02:00
if (! $this->nodesToRemoveCollector->isNodeRemoved($prop)) {
// if the property has at least one node left -> return
return;
}
}
$this->removeNode($property);
}
/**
* @param StaticPropertyFetch|PropertyFetch $expr
* @param string[] $classMethodNamesToSkip
*/
private function shouldSkipPropertyForClassMethod(Expr $expr, array $classMethodNamesToSkip): bool
{
$classMethodNode = $expr->getAttribute(AttributeKey::METHOD_NODE);
if (! $classMethodNode instanceof ClassMethod) {
return false;
}
$classMethodName = $this->getName($classMethodNode);
return in_array($classMethodName, $classMethodNamesToSkip, true);
}
/**
* @param PropertyFetch|StaticPropertyFetch $expr
*/
private function resolveAssign(Expr $expr): Assign
{
$assign = $expr->getAttribute(AttributeKey::PARENT_NODE);
while ($assign !== null && ! $assign instanceof Assign) {
$assign = $assign->getAttribute(AttributeKey::PARENT_NODE);
}
if (! $assign instanceof Assign) {
throw new ShouldNotHappenException("Can't handle this situation");
}
return $assign;
}
private function removeConstructorDependency(Assign $assign): void
{
$methodName = $assign->getAttribute(AttributeKey::METHOD_NAME);
2020-07-30 01:39:41 +02:00
if ($methodName !== MethodName::CONSTRUCT) {
return;
}
$class = $assign->getAttribute(AttributeKey::CLASS_NODE);
if (! $class instanceof Class_) {
return;
}
2020-07-30 01:39:41 +02:00
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return;
}
$constructClassMethodStmts = $constructClassMethod->stmts;
foreach ($constructClassMethod->getParams() as $param) {
$variable = $this->betterNodeFinder->findFirst($constructClassMethodStmts, function (Node $node) use (
$param
): bool {
return $this->betterStandardPrinter->areNodesEqual($param->var, $node);
});
if ($variable === null) {
continue;
}
if ($this->isExpressionVariableNotAssign($variable)) {
continue;
}
2020-03-28 16:27:06 +01:00
if (! $this->betterStandardPrinter->areNodesEqual($param->var, $assign->expr)) {
continue;
}
$this->removeNode($param);
}
}
private function isExpressionVariableNotAssign(Node $node): bool
{
if ($node !== null) {
$expressionVariable = $node->getAttribute(AttributeKey::PARENT_NODE);
if (! $expressionVariable instanceof Assign) {
return true;
}
}
return false;
}
}