2019-11-05 16:33:38 +01:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2022-06-06 16:43:29 +00:00
|
|
|
namespace RectorPrefix20220606\Rector\Removing\NodeManipulator;
|
2019-11-05 16:33:38 +01:00
|
|
|
|
2022-06-06 16:43:29 +00:00
|
|
|
use RectorPrefix20220606\PhpParser\Node;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Expr;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Expr\ArrayDimFetch;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Expr\Assign;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Expr\PropertyFetch;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Expr\StaticPropertyFetch;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Expr\Variable;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Param;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Stmt\Class_;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Stmt\ClassMethod;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Stmt\Expression;
|
|
|
|
use RectorPrefix20220606\PhpParser\Node\Stmt\Property;
|
|
|
|
use RectorPrefix20220606\Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer;
|
|
|
|
use RectorPrefix20220606\Rector\Core\PhpParser\Node\BetterNodeFinder;
|
|
|
|
use RectorPrefix20220606\Rector\Core\ValueObject\MethodName;
|
|
|
|
use RectorPrefix20220606\Rector\DeadCode\SideEffect\SideEffectNodeDetector;
|
|
|
|
use RectorPrefix20220606\Rector\NodeNameResolver\NodeNameResolver;
|
|
|
|
use RectorPrefix20220606\Rector\NodeRemoval\NodeRemover;
|
2022-06-06 06:00:07 +00:00
|
|
|
use RectorPrefix20220606\Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser;
|
2021-02-08 22:00:45 +01:00
|
|
|
final class ComplexNodeRemover
|
2019-11-05 16:33:38 +01:00
|
|
|
{
|
2020-11-16 17:50:38 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\NodeNameResolver\NodeNameResolver
|
2020-11-16 17:50:38 +00:00
|
|
|
*/
|
2021-02-08 22:00:45 +01:00
|
|
|
private $nodeNameResolver;
|
2020-11-16 17:50:38 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\PhpParser\Node\BetterNodeFinder
|
2020-11-16 17:50:38 +00:00
|
|
|
*/
|
2021-02-08 22:00:45 +01:00
|
|
|
private $betterNodeFinder;
|
2021-01-19 20:11:10 +01:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\NodeRemoval\NodeRemover
|
2021-01-19 20:11:10 +01:00
|
|
|
*/
|
2021-02-08 22:00:45 +01:00
|
|
|
private $nodeRemover;
|
2022-03-20 19:48:16 +00:00
|
|
|
/**
|
|
|
|
* @readonly
|
|
|
|
* @var \Rector\DeadCode\SideEffect\SideEffectNodeDetector
|
|
|
|
*/
|
|
|
|
private $sideEffectNodeDetector;
|
2022-05-04 22:24:14 +00:00
|
|
|
/**
|
|
|
|
* @readonly
|
|
|
|
* @var \Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser
|
|
|
|
*/
|
|
|
|
private $simpleCallableNodeTraverser;
|
2022-05-05 08:58:28 +00:00
|
|
|
/**
|
|
|
|
* @readonly
|
|
|
|
* @var \Rector\Core\NodeAnalyzer\PropertyFetchAnalyzer
|
|
|
|
*/
|
|
|
|
private $propertyFetchAnalyzer;
|
2022-06-06 16:43:29 +00:00
|
|
|
public function __construct(NodeNameResolver $nodeNameResolver, BetterNodeFinder $betterNodeFinder, NodeRemover $nodeRemover, SideEffectNodeDetector $sideEffectNodeDetector, SimpleCallableNodeTraverser $simpleCallableNodeTraverser, PropertyFetchAnalyzer $propertyFetchAnalyzer)
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2021-02-08 22:00:45 +01:00
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
|
|
|
$this->betterNodeFinder = $betterNodeFinder;
|
|
|
|
$this->nodeRemover = $nodeRemover;
|
2022-03-20 19:48:16 +00:00
|
|
|
$this->sideEffectNodeDetector = $sideEffectNodeDetector;
|
2022-05-04 22:24:14 +00:00
|
|
|
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
|
2022-05-05 08:58:28 +00:00
|
|
|
$this->propertyFetchAnalyzer = $propertyFetchAnalyzer;
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|
2022-06-06 16:43:29 +00:00
|
|
|
public function removePropertyAndUsages(Class_ $class, Property $property, bool $removeAssignSideEffect) : void
|
2020-04-29 13:42:11 +02:00
|
|
|
{
|
2022-05-04 22:24:14 +00:00
|
|
|
$propertyName = $this->nodeNameResolver->getName($property);
|
|
|
|
$hasSideEffect = \false;
|
2022-05-12 06:13:58 +00:00
|
|
|
$isPartOfAnotherAssign = \false;
|
2022-06-06 16:43:29 +00:00
|
|
|
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($class->stmts, function (Node $node) use($removeAssignSideEffect, $propertyName, &$hasSideEffect, &$isPartOfAnotherAssign) {
|
2022-05-04 22:24:14 +00:00
|
|
|
// here should be checked all expr like stmts that can hold assign, e.f. if, foreach etc. etc.
|
2022-06-06 16:43:29 +00:00
|
|
|
if (!$node instanceof Expression) {
|
2022-05-05 07:46:45 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$nodeExpr = $node->expr;
|
|
|
|
// remove direct assigns
|
2022-06-06 16:43:29 +00:00
|
|
|
if (!$nodeExpr instanceof Assign) {
|
2022-05-05 07:46:45 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$assign = $nodeExpr;
|
|
|
|
// skip double assigns
|
2022-06-06 16:43:29 +00:00
|
|
|
if ($assign->expr instanceof Assign) {
|
2022-05-12 06:13:58 +00:00
|
|
|
$isPartOfAnotherAssign = \true;
|
2022-05-05 07:46:45 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
$propertyFetches = $this->resolvePropertyFetchFromDimFetch($assign->var);
|
|
|
|
if ($propertyFetches === []) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
foreach ($propertyFetches as $propertyFetch) {
|
|
|
|
if ($this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
|
|
|
|
if (!$removeAssignSideEffect && $this->sideEffectNodeDetector->detect($assign->expr)) {
|
|
|
|
$hasSideEffect = \true;
|
2022-05-04 22:24:14 +00:00
|
|
|
return null;
|
|
|
|
}
|
2022-05-05 07:46:45 +00:00
|
|
|
$this->nodeRemover->removeNode($node);
|
2022-05-04 22:24:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
});
|
|
|
|
// do not remove anyhting in case of side-effect
|
|
|
|
if ($hasSideEffect) {
|
|
|
|
return;
|
|
|
|
}
|
2022-05-12 06:13:58 +00:00
|
|
|
if ($isPartOfAnotherAssign) {
|
2022-05-05 06:11:24 +00:00
|
|
|
return;
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|
2022-05-05 06:11:24 +00:00
|
|
|
$this->removeConstructorDependency($class, $propertyName);
|
2021-02-08 22:00:45 +01:00
|
|
|
$this->nodeRemover->removeNode($property);
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|
2022-01-22 16:02:37 +00:00
|
|
|
/**
|
|
|
|
* @param Param[] $params
|
|
|
|
* @param int[] $paramKeysToBeRemoved
|
|
|
|
* @return int[]
|
|
|
|
*/
|
|
|
|
public function processRemoveParamWithKeys(array $params, array $paramKeysToBeRemoved) : array
|
|
|
|
{
|
|
|
|
$totalKeys = \count($params) - 1;
|
|
|
|
$removedParamKeys = [];
|
|
|
|
foreach ($paramKeysToBeRemoved as $paramKeyToBeRemoved) {
|
|
|
|
$startNextKey = $paramKeyToBeRemoved + 1;
|
|
|
|
for ($nextKey = $startNextKey; $nextKey <= $totalKeys; ++$nextKey) {
|
|
|
|
if (!isset($params[$nextKey])) {
|
|
|
|
// no next param, break the inner loop, remove the param
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (\in_array($nextKey, $paramKeysToBeRemoved, \true)) {
|
|
|
|
// keep searching next key not in $paramKeysToBeRemoved
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
$this->nodeRemover->removeNode($params[$paramKeyToBeRemoved]);
|
|
|
|
$removedParamKeys[] = $paramKeyToBeRemoved;
|
|
|
|
}
|
|
|
|
return $removedParamKeys;
|
|
|
|
}
|
2022-06-06 16:43:29 +00:00
|
|
|
private function removeConstructorDependency(Class_ $class, string $propertyName) : void
|
2021-11-11 15:47:08 +00:00
|
|
|
{
|
2022-06-06 16:43:29 +00:00
|
|
|
$classMethod = $class->getMethod(MethodName::CONSTRUCT);
|
|
|
|
if (!$classMethod instanceof ClassMethod) {
|
2021-04-09 12:45:07 +02:00
|
|
|
return;
|
|
|
|
}
|
2022-05-05 06:11:24 +00:00
|
|
|
$stmts = (array) $classMethod->stmts;
|
2022-05-05 07:46:45 +00:00
|
|
|
$paramKeysToBeRemoved = [];
|
2022-05-05 06:11:24 +00:00
|
|
|
foreach ($stmts as $key => $stmt) {
|
2022-06-06 16:43:29 +00:00
|
|
|
if (!$stmt instanceof Expression) {
|
2021-11-29 06:47:55 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-05-05 06:11:24 +00:00
|
|
|
$stmtExpr = $stmt->expr;
|
2022-06-06 16:43:29 +00:00
|
|
|
if (!$stmtExpr instanceof Assign) {
|
2022-05-05 07:46:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-05-05 08:58:28 +00:00
|
|
|
if (!$this->propertyFetchAnalyzer->isLocalPropertyFetch($stmtExpr->var)) {
|
2022-05-05 07:46:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-05-05 08:58:28 +00:00
|
|
|
/** @var StaticPropertyFetch|PropertyFetch $propertyFetch */
|
2022-05-05 07:46:45 +00:00
|
|
|
$propertyFetch = $stmtExpr->var;
|
|
|
|
if (!$this->nodeNameResolver->isName($propertyFetch, $propertyName)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
unset($classMethod->stmts[$key]);
|
2022-06-06 16:43:29 +00:00
|
|
|
if (!$stmtExpr->expr instanceof Variable) {
|
2022-05-05 07:46:45 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$key = $this->resolveToBeClearedParamFromConstructor($classMethod, $stmtExpr->expr);
|
|
|
|
if (\is_int($key)) {
|
|
|
|
$paramKeysToBeRemoved[] = $key;
|
2021-11-29 06:47:55 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-05 07:46:45 +00:00
|
|
|
if ($paramKeysToBeRemoved === []) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$this->processRemoveParamWithKeys($classMethod->getParams(), $paramKeysToBeRemoved);
|
2020-11-09 01:22:24 +07:00
|
|
|
}
|
2022-05-04 22:24:14 +00:00
|
|
|
/**
|
2022-05-05 08:58:28 +00:00
|
|
|
* @return StaticPropertyFetch[]|PropertyFetch[]
|
2022-05-04 22:24:14 +00:00
|
|
|
*/
|
2022-06-06 16:43:29 +00:00
|
|
|
private function resolvePropertyFetchFromDimFetch(Expr $expr) : array
|
2022-05-04 22:24:14 +00:00
|
|
|
{
|
|
|
|
// unwrap array dim fetch, till we get to parent too caller node
|
2022-05-05 09:37:41 +00:00
|
|
|
/** @var PropertyFetch[]|StaticPropertyFetch[] $propertyFetches */
|
2022-05-04 22:24:14 +00:00
|
|
|
$propertyFetches = [];
|
2022-06-06 16:43:29 +00:00
|
|
|
while ($expr instanceof ArrayDimFetch) {
|
2022-05-05 08:58:28 +00:00
|
|
|
$propertyFetches = $this->collectPropertyFetches($expr->dim, $propertyFetches);
|
2022-05-04 22:24:14 +00:00
|
|
|
$expr = $expr->var;
|
|
|
|
}
|
2022-05-05 09:37:41 +00:00
|
|
|
return $this->collectPropertyFetches($expr, $propertyFetches);
|
2022-05-05 08:58:28 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param StaticPropertyFetch[]|PropertyFetch[] $propertyFetches
|
2022-05-05 09:37:41 +00:00
|
|
|
* @return PropertyFetch[]|StaticPropertyFetch[]
|
2022-05-05 08:58:28 +00:00
|
|
|
*/
|
2022-06-06 16:43:29 +00:00
|
|
|
private function collectPropertyFetches(?Expr $expr, array $propertyFetches) : array
|
2022-05-05 08:58:28 +00:00
|
|
|
{
|
2022-06-06 16:43:29 +00:00
|
|
|
if (!$expr instanceof Expr) {
|
2022-05-05 08:58:28 +00:00
|
|
|
return $propertyFetches;
|
|
|
|
}
|
|
|
|
if ($this->propertyFetchAnalyzer->isLocalPropertyFetch($expr)) {
|
|
|
|
/** @var StaticPropertyFetch|PropertyFetch $expr */
|
|
|
|
return \array_merge($propertyFetches, [$expr]);
|
2022-05-04 22:24:14 +00:00
|
|
|
}
|
|
|
|
return $propertyFetches;
|
|
|
|
}
|
2022-06-06 16:43:29 +00:00
|
|
|
private function resolveToBeClearedParamFromConstructor(ClassMethod $classMethod, Variable $assignedVariable) : ?int
|
2022-05-05 06:11:24 +00:00
|
|
|
{
|
|
|
|
// is variable used somewhere else? skip it
|
2022-06-06 16:43:29 +00:00
|
|
|
$variables = $this->betterNodeFinder->findInstanceOf($classMethod, Variable::class);
|
|
|
|
$paramNamedVariables = \array_filter($variables, function (Variable $variable) use($assignedVariable) : bool {
|
2022-05-05 06:11:24 +00:00
|
|
|
return $this->nodeNameResolver->areNamesEqual($variable, $assignedVariable);
|
|
|
|
});
|
|
|
|
// there is more than 1 use, keep it in the constructor
|
|
|
|
if (\count($paramNamedVariables) > 1) {
|
2022-05-05 07:46:45 +00:00
|
|
|
return null;
|
2022-05-05 06:11:24 +00:00
|
|
|
}
|
|
|
|
$paramName = $this->nodeNameResolver->getName($assignedVariable);
|
|
|
|
if (!\is_string($paramName)) {
|
2022-05-05 07:46:45 +00:00
|
|
|
return null;
|
2022-05-05 06:11:24 +00:00
|
|
|
}
|
|
|
|
foreach ($classMethod->params as $paramKey => $param) {
|
|
|
|
if ($this->nodeNameResolver->isName($param->var, $paramName)) {
|
2022-05-05 07:46:45 +00:00
|
|
|
return $paramKey;
|
2022-05-05 06:11:24 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-05 07:46:45 +00:00
|
|
|
return null;
|
2022-05-05 06:11:24 +00:00
|
|
|
}
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|