2019-11-05 16:33:38 +01:00
|
|
|
<?php
|
|
|
|
|
2021-05-09 20:15:43 +00:00
|
|
|
declare (strict_types=1);
|
2021-02-08 22:00:45 +01:00
|
|
|
namespace Rector\Removing\NodeManipulator;
|
2019-11-05 16:33:38 +01:00
|
|
|
|
|
|
|
use PhpParser\Node;
|
|
|
|
use PhpParser\Node\Expr\Assign;
|
|
|
|
use PhpParser\Node\Expr\PropertyFetch;
|
|
|
|
use PhpParser\Node\Expr\StaticPropertyFetch;
|
2021-11-29 06:47:55 +00:00
|
|
|
use PhpParser\Node\Expr\Variable;
|
2021-11-11 14:30:13 +00:00
|
|
|
use PhpParser\Node\Param;
|
2020-03-26 22:57:56 +01:00
|
|
|
use PhpParser\Node\Stmt\Class_;
|
2021-11-12 13:45:18 +00:00
|
|
|
use PhpParser\Node\Stmt\ClassLike;
|
2019-11-05 16:33:38 +01:00
|
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
|
|
|
use PhpParser\Node\Stmt\Property;
|
2021-02-19 13:01:23 +01:00
|
|
|
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
2021-02-08 22:00:45 +01:00
|
|
|
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
2021-01-19 20:11:10 +01:00
|
|
|
use Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder;
|
2020-07-30 01:39:41 +02:00
|
|
|
use Rector\Core\ValueObject\MethodName;
|
2022-03-20 19:48:16 +00:00
|
|
|
use Rector\DeadCode\SideEffect\SideEffectNodeDetector;
|
2021-02-08 22:00:45 +01:00
|
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
2020-11-16 17:50:38 +00:00
|
|
|
use Rector\NodeRemoval\AssignRemover;
|
2021-02-08 22:00:45 +01:00
|
|
|
use Rector\NodeRemoval\NodeRemover;
|
2019-11-05 16:33:38 +01:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2021-11-12 13:45:18 +00:00
|
|
|
use Rector\Removing\NodeAnalyzer\ForbiddenPropertyRemovalAnalyzer;
|
2021-02-08 22:00:45 +01:00
|
|
|
final class ComplexNodeRemover
|
2019-11-05 16:33:38 +01:00
|
|
|
{
|
2020-03-26 22:57:56 +01:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\NodeRemoval\AssignRemover
|
2020-03-26 22:57:56 +01:00
|
|
|
*/
|
2021-02-08 22:00:45 +01:00
|
|
|
private $assignRemover;
|
2019-11-05 16:33:38 +01:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder
|
2019-11-05 16:33:38 +01:00
|
|
|
*/
|
2021-02-08 22:00:45 +01:00
|
|
|
private $propertyFetchFinder;
|
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;
|
2021-05-10 23:39:21 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-05-10 23:39:21 +00:00
|
|
|
* @var \Rector\Core\PhpParser\Comparing\NodeComparator
|
|
|
|
*/
|
|
|
|
private $nodeComparator;
|
2021-11-12 13:45:18 +00:00
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-11-12 13:45:18 +00:00
|
|
|
* @var \Rector\Removing\NodeAnalyzer\ForbiddenPropertyRemovalAnalyzer
|
|
|
|
*/
|
|
|
|
private $forbiddenPropertyRemovalAnalyzer;
|
2022-03-20 19:48:16 +00:00
|
|
|
/**
|
|
|
|
* @readonly
|
|
|
|
* @var \Rector\DeadCode\SideEffect\SideEffectNodeDetector
|
|
|
|
*/
|
|
|
|
private $sideEffectNodeDetector;
|
|
|
|
public function __construct(\Rector\NodeRemoval\AssignRemover $assignRemover, \Rector\Core\PhpParser\NodeFinder\PropertyFetchFinder $propertyFetchFinder, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\Core\PhpParser\Node\BetterNodeFinder $betterNodeFinder, \Rector\NodeRemoval\NodeRemover $nodeRemover, \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator, \Rector\Removing\NodeAnalyzer\ForbiddenPropertyRemovalAnalyzer $forbiddenPropertyRemovalAnalyzer, \Rector\DeadCode\SideEffect\SideEffectNodeDetector $sideEffectNodeDetector)
|
2021-05-09 20:15:43 +00:00
|
|
|
{
|
2020-11-16 17:50:38 +00:00
|
|
|
$this->assignRemover = $assignRemover;
|
2021-01-19 20:11:10 +01:00
|
|
|
$this->propertyFetchFinder = $propertyFetchFinder;
|
2021-02-08 22:00:45 +01:00
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
|
|
|
$this->betterNodeFinder = $betterNodeFinder;
|
|
|
|
$this->nodeRemover = $nodeRemover;
|
2021-02-19 13:01:23 +01:00
|
|
|
$this->nodeComparator = $nodeComparator;
|
2021-11-12 13:45:18 +00:00
|
|
|
$this->forbiddenPropertyRemovalAnalyzer = $forbiddenPropertyRemovalAnalyzer;
|
2022-03-20 19:48:16 +00:00
|
|
|
$this->sideEffectNodeDetector = $sideEffectNodeDetector;
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|
2022-03-20 19:48:16 +00:00
|
|
|
public function removePropertyAndUsages(\PhpParser\Node\Stmt\Property $property, bool $removeAssignSideEffect = \true) : void
|
2020-04-29 13:42:11 +02:00
|
|
|
{
|
2021-01-19 20:11:10 +01:00
|
|
|
$propertyFetches = $this->propertyFetchFinder->findPrivatePropertyFetches($property);
|
2021-11-11 15:47:08 +00:00
|
|
|
$assigns = [];
|
2019-11-05 16:33:38 +01:00
|
|
|
foreach ($propertyFetches as $propertyFetch) {
|
2021-05-19 13:48:36 +00:00
|
|
|
$assign = $this->resolveAssign($propertyFetch);
|
2021-05-19 14:34:27 +00:00
|
|
|
if (!$assign instanceof \PhpParser\Node\Expr\Assign) {
|
|
|
|
return;
|
|
|
|
}
|
2022-03-17 07:36:14 +00:00
|
|
|
if ($assign->expr instanceof \PhpParser\Node\Expr\Assign) {
|
|
|
|
return;
|
|
|
|
}
|
2022-03-20 19:48:16 +00:00
|
|
|
if (!$removeAssignSideEffect && $this->sideEffectNodeDetector->detect($assign->expr)) {
|
|
|
|
return;
|
|
|
|
}
|
2021-11-12 13:45:18 +00:00
|
|
|
$assigns[] = $assign;
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|
2021-11-12 13:45:18 +00:00
|
|
|
$this->processRemovePropertyAssigns($assigns);
|
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;
|
|
|
|
}
|
2021-11-11 15:47:08 +00:00
|
|
|
/**
|
2021-11-12 13:45:18 +00:00
|
|
|
* @param Assign[] $assigns
|
2021-11-11 15:47:08 +00:00
|
|
|
*/
|
2021-11-12 13:45:18 +00:00
|
|
|
private function processRemovePropertyAssigns(array $assigns) : void
|
2021-11-11 15:47:08 +00:00
|
|
|
{
|
2021-11-12 13:45:18 +00:00
|
|
|
foreach ($assigns as $assign) {
|
|
|
|
// remove assigns
|
|
|
|
$this->assignRemover->removeAssignNode($assign);
|
|
|
|
$this->removeConstructorDependency($assign);
|
2021-11-11 15:47:08 +00:00
|
|
|
}
|
|
|
|
}
|
2020-02-09 12:31:31 +01:00
|
|
|
/**
|
2021-08-23 00:20:32 +00:00
|
|
|
* @param \PhpParser\Node\Expr\PropertyFetch|\PhpParser\Node\Expr\StaticPropertyFetch $expr
|
2020-02-09 12:31:31 +01:00
|
|
|
*/
|
2021-05-30 10:12:56 +00:00
|
|
|
private function resolveAssign($expr) : ?\PhpParser\Node\Expr\Assign
|
2020-02-09 12:31:31 +01:00
|
|
|
{
|
2021-05-10 22:23:08 +00:00
|
|
|
$assign = $expr->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
2022-02-05 11:57:25 +00:00
|
|
|
while ($assign instanceof \PhpParser\Node && !$assign instanceof \PhpParser\Node\Expr\Assign) {
|
2021-05-10 22:23:08 +00:00
|
|
|
$assign = $assign->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
2020-02-09 12:31:31 +01:00
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$assign instanceof \PhpParser\Node\Expr\Assign) {
|
2021-05-19 14:34:27 +00:00
|
|
|
return null;
|
2020-02-09 12:31:31 +01:00
|
|
|
}
|
2021-11-11 15:47:08 +00:00
|
|
|
$isInExpr = (bool) $this->betterNodeFinder->findFirst($assign->expr, function (\PhpParser\Node $subNode) use($expr) : bool {
|
|
|
|
return $this->nodeComparator->areNodesEqual($subNode, $expr);
|
|
|
|
});
|
|
|
|
if ($isInExpr) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-11-12 13:45:18 +00:00
|
|
|
$classLike = $this->betterNodeFinder->findParentType($expr, \PhpParser\Node\Stmt\ClassLike::class);
|
|
|
|
$propertyName = (string) $this->nodeNameResolver->getName($expr);
|
|
|
|
if ($this->forbiddenPropertyRemovalAnalyzer->isForbiddenInNewCurrentClassNameSelfClone($propertyName, $classLike)) {
|
|
|
|
return null;
|
|
|
|
}
|
2020-02-09 12:31:31 +01:00
|
|
|
return $assign;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
private function removeConstructorDependency(\PhpParser\Node\Expr\Assign $assign) : void
|
2020-03-26 22:57:56 +01:00
|
|
|
{
|
2021-11-06 14:25:01 +00:00
|
|
|
$classMethod = $this->betterNodeFinder->findParentType($assign, \PhpParser\Node\Stmt\ClassMethod::class);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
|
2021-04-09 12:45:07 +02:00
|
|
|
return;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$this->nodeNameResolver->isName($classMethod, \Rector\Core\ValueObject\MethodName::CONSTRUCT)) {
|
2020-03-26 22:57:56 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-11-06 12:10:48 +00:00
|
|
|
$class = $this->betterNodeFinder->findParentType($assign, \PhpParser\Node\Stmt\Class_::class);
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$class instanceof \PhpParser\Node\Stmt\Class_) {
|
2020-03-26 22:57:56 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
$constructClassMethod = $class->getMethod(\Rector\Core\ValueObject\MethodName::CONSTRUCT);
|
|
|
|
if (!$constructClassMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
|
2020-03-26 22:57:56 +01:00
|
|
|
return;
|
|
|
|
}
|
2021-11-11 14:30:13 +00:00
|
|
|
$params = $constructClassMethod->getParams();
|
|
|
|
$paramKeysToBeRemoved = [];
|
2021-11-29 06:47:55 +00:00
|
|
|
/** @var Variable[] $variables */
|
|
|
|
$variables = $this->resolveVariables($constructClassMethod);
|
2021-11-11 14:30:13 +00:00
|
|
|
foreach ($params as $key => $param) {
|
2021-05-10 22:23:08 +00:00
|
|
|
$variable = $this->betterNodeFinder->findFirst((array) $constructClassMethod->stmts, function (\PhpParser\Node $node) use($param) : bool {
|
2021-02-19 13:01:23 +01:00
|
|
|
return $this->nodeComparator->areNodesEqual($param->var, $node);
|
2020-11-09 01:22:24 +07:00
|
|
|
});
|
2021-05-10 22:23:08 +00:00
|
|
|
if (!$variable instanceof \PhpParser\Node) {
|
2020-11-16 17:50:38 +00:00
|
|
|
continue;
|
|
|
|
}
|
2020-11-09 01:22:24 +07:00
|
|
|
if ($this->isExpressionVariableNotAssign($variable)) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-05-09 20:15:43 +00:00
|
|
|
if (!$this->nodeComparator->areNodesEqual($param->var, $assign->expr)) {
|
2020-03-26 22:57:56 +01:00
|
|
|
continue;
|
|
|
|
}
|
2021-11-29 06:47:55 +00:00
|
|
|
if ($this->isInVariables($variables, $assign)) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-11-11 14:30:13 +00:00
|
|
|
$paramKeysToBeRemoved[] = $key;
|
|
|
|
}
|
|
|
|
$this->processRemoveParamWithKeys($params, $paramKeysToBeRemoved);
|
|
|
|
}
|
2021-11-29 06:47:55 +00:00
|
|
|
/**
|
|
|
|
* @return Variable[]
|
|
|
|
*/
|
|
|
|
private function resolveVariables(\PhpParser\Node\Stmt\ClassMethod $classMethod) : array
|
|
|
|
{
|
|
|
|
return $this->betterNodeFinder->find((array) $classMethod->stmts, function (\PhpParser\Node $subNode) : bool {
|
|
|
|
if (!$subNode instanceof \PhpParser\Node\Expr\Variable) {
|
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
return $this->isExpressionVariableNotAssign($subNode);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param Variable[] $variables
|
|
|
|
*/
|
|
|
|
private function isInVariables(array $variables, \PhpParser\Node\Expr\Assign $assign) : bool
|
|
|
|
{
|
|
|
|
foreach ($variables as $variable) {
|
|
|
|
if ($this->nodeComparator->areNodesEqual($assign->expr, $variable)) {
|
|
|
|
return \true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return \false;
|
|
|
|
}
|
2021-05-10 22:23:08 +00:00
|
|
|
private function isExpressionVariableNotAssign(\PhpParser\Node $node) : bool
|
2020-11-09 01:22:24 +07:00
|
|
|
{
|
2021-11-12 13:45:18 +00:00
|
|
|
$expressionVariable = $node->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::PARENT_NODE);
|
|
|
|
return !$expressionVariable instanceof \PhpParser\Node\Expr\Assign;
|
2020-11-09 01:22:24 +07:00
|
|
|
}
|
2019-11-05 16:33:38 +01:00
|
|
|
}
|