rector/packages/Defluent/NodeAnalyzer/FluentChainMethodCallNodeAnalyzer.php
2022-06-07 08:22:29 +00:00

89 lines
2.8 KiB
PHP

<?php
declare (strict_types=1);
namespace Rector\Defluent\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* Utils for chain of MethodCall Node:
* "$this->methodCall()->chainedMethodCall()"
*/
final class FluentChainMethodCallNodeAnalyzer
{
/**
* @readonly
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
public function __construct(NodeNameResolver $nodeNameResolver)
{
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @return string[]
*/
public function collectMethodCallNamesInChain(MethodCall $desiredMethodCall) : array
{
$methodCalls = $this->collectAllMethodCallsInChain($desiredMethodCall);
$methodNames = [];
foreach ($methodCalls as $methodCall) {
$methodName = $this->nodeNameResolver->getName($methodCall->name);
if ($methodName === null) {
continue;
}
$methodNames[] = $methodName;
}
return $methodNames;
}
/**
* @return MethodCall[]
*/
public function collectAllMethodCallsInChain(MethodCall $methodCall) : array
{
$chainMethodCalls = [$methodCall];
// traverse up
$currentNode = $methodCall->var;
while ($currentNode instanceof MethodCall) {
$chainMethodCalls[] = $currentNode;
$currentNode = $currentNode->var;
}
// traverse down
if (\count($chainMethodCalls) === 1) {
$currentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE);
while ($currentNode instanceof MethodCall) {
$chainMethodCalls[] = $currentNode;
$currentNode = $currentNode->getAttribute(AttributeKey::PARENT_NODE);
}
}
return $chainMethodCalls;
}
/**
* @return \PhpParser\Node\Expr|\PhpParser\Node\Name
*/
public function resolveRootExpr(MethodCall $methodCall)
{
$callerNode = $methodCall->var;
while ($callerNode instanceof MethodCall || $callerNode instanceof StaticCall) {
$callerNode = $callerNode instanceof StaticCall ? $callerNode->class : $callerNode->var;
}
return $callerNode;
}
public function resolveRootMethodCall(MethodCall $methodCall) : ?MethodCall
{
$callerNode = $methodCall->var;
while ($callerNode instanceof MethodCall && $callerNode->var instanceof MethodCall) {
$callerNode = $callerNode->var;
}
if ($callerNode instanceof MethodCall) {
return $callerNode;
}
return null;
}
}