rector/rules/Defluent/NodeFactory/NonFluentChainMethodCallFactory.php

124 lines
5.7 KiB
PHP
Raw Normal View History

2020-06-30 11:01:20 +02:00
<?php
declare (strict_types=1);
namespace Rector\Defluent\NodeFactory;
2020-06-30 11:01:20 +02:00
use PhpParser\Node;
2020-06-30 11:01:20 +02:00
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Cast;
2020-06-30 11:01:20 +02:00
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Defluent\NodeAnalyzer\FluentChainMethodCallNodeAnalyzer;
use Rector\Defluent\NodeResolver\FirstMethodCallVarResolver;
use Rector\Defluent\ValueObject\AssignAndRootExpr;
use Rector\Defluent\ValueObject\FluentCallsKind;
use Rector\Naming\Naming\VariableNaming;
final class NonFluentChainMethodCallFactory
2020-06-30 11:01:20 +02:00
{
/**
* @var \Rector\Defluent\NodeAnalyzer\FluentChainMethodCallNodeAnalyzer
*/
private $fluentChainMethodCallNodeAnalyzer;
/**
* @var \Rector\Naming\Naming\VariableNaming
*/
private $variableNaming;
/**
* @var \Rector\Defluent\NodeResolver\FirstMethodCallVarResolver
*/
private $firstMethodCallVarResolver;
public function __construct(\Rector\Defluent\NodeAnalyzer\FluentChainMethodCallNodeAnalyzer $fluentChainMethodCallNodeAnalyzer, \Rector\Naming\Naming\VariableNaming $variableNaming, \Rector\Defluent\NodeResolver\FirstMethodCallVarResolver $firstMethodCallVarResolver)
{
$this->fluentChainMethodCallNodeAnalyzer = $fluentChainMethodCallNodeAnalyzer;
$this->variableNaming = $variableNaming;
$this->firstMethodCallVarResolver = $firstMethodCallVarResolver;
}
/**
* @return Expression[]
*/
public function createFromNewAndRootMethodCall(\PhpParser\Node\Expr\New_ $new, \PhpParser\Node\Expr\MethodCall $rootMethodCall) : array
{
$variableName = $this->variableNaming->resolveFromNode($new);
if ($variableName === null) {
throw new \Rector\Core\Exception\ShouldNotHappenException();
}
$newVariable = new \PhpParser\Node\Expr\Variable($variableName);
$newStmts = [];
$newStmts[] = $this->createAssignExpression($newVariable, $new);
// resolve chain calls
$chainMethodCalls = $this->fluentChainMethodCallNodeAnalyzer->collectAllMethodCallsInChainWithoutRootOne($rootMethodCall);
$chainMethodCalls = \array_reverse($chainMethodCalls);
foreach ($chainMethodCalls as $chainMethodCall) {
$methodCall = new \PhpParser\Node\Expr\MethodCall($newVariable, $chainMethodCall->name, $chainMethodCall->args);
$newStmts[] = new \PhpParser\Node\Stmt\Expression($methodCall);
}
return $newStmts;
}
2020-06-30 11:01:20 +02:00
/**
* @param MethodCall[] $chainMethodCalls
* @return Assign[]|Cast[]|MethodCall[]|Return_[]
2020-06-30 11:01:20 +02:00
*/
public function createFromAssignObjectAndMethodCalls(\Rector\Defluent\ValueObject\AssignAndRootExpr $assignAndRootExpr, array $chainMethodCalls, string $kind, ?\PhpParser\Node $node = null) : array
{
2020-06-30 11:01:20 +02:00
$nodesToAdd = [];
2020-08-12 20:24:52 +02:00
$isNewNodeNeeded = $this->isNewNodeNeeded($assignAndRootExpr);
if ($isNewNodeNeeded) {
$nodesToAdd[] = $assignAndRootExpr->createFirstAssign();
2020-06-30 11:01:20 +02:00
}
$decoupledMethodCalls = $this->createNonFluentMethodCalls($chainMethodCalls, $assignAndRootExpr, $isNewNodeNeeded);
$nodesToAdd = \array_merge($nodesToAdd, $decoupledMethodCalls);
if ($assignAndRootExpr->getSilentVariable() !== null && $kind !== \Rector\Defluent\ValueObject\FluentCallsKind::IN_ARGS) {
2020-06-30 11:01:20 +02:00
$nodesToAdd[] = $assignAndRootExpr->getReturnSilentVariable();
}
if ($node instanceof \PhpParser\Node\Expr\Cast) {
\end($nodesToAdd);
$lastNodeToAdd = $nodesToAdd[\key($nodesToAdd)];
$cast = \get_class($node);
\end($nodesToAdd);
$nodesToAdd[\key($nodesToAdd)] = new $cast($lastNodeToAdd);
}
2020-06-30 11:01:20 +02:00
return $nodesToAdd;
}
private function createAssignExpression(\PhpParser\Node\Expr\Variable $newVariable, \PhpParser\Node\Expr\New_ $new) : \PhpParser\Node\Stmt\Expression
{
$assign = new \PhpParser\Node\Expr\Assign($newVariable, $new);
return new \PhpParser\Node\Stmt\Expression($assign);
}
private function isNewNodeNeeded(\Rector\Defluent\ValueObject\AssignAndRootExpr $assignAndRootExpr) : bool
{
2020-08-12 20:24:52 +02:00
if ($assignAndRootExpr->isFirstCallFactory()) {
return \true;
2020-08-12 20:24:52 +02:00
}
if ($assignAndRootExpr->getRootExpr() === $assignAndRootExpr->getAssignExpr()) {
return \false;
}
return $assignAndRootExpr->getRootExpr() instanceof \PhpParser\Node\Expr\New_;
}
2020-06-30 11:01:20 +02:00
/**
* @param MethodCall[] $chainMethodCalls
2020-08-29 11:03:40 +02:00
* @return Assign[]|MethodCall[]
2020-06-30 11:01:20 +02:00
*/
private function createNonFluentMethodCalls(array $chainMethodCalls, \Rector\Defluent\ValueObject\AssignAndRootExpr $assignAndRootExpr, bool $isNewNodeNeeded) : array
{
2020-06-30 11:01:20 +02:00
$decoupledMethodCalls = [];
\end($chainMethodCalls);
$lastKey = \key($chainMethodCalls);
2020-08-12 20:24:52 +02:00
foreach ($chainMethodCalls as $key => $chainMethodCall) {
// skip first, already handled
if ($key === $lastKey && $assignAndRootExpr->isFirstCallFactory() && $isNewNodeNeeded) {
continue;
}
$chainMethodCall->var = $this->firstMethodCallVarResolver->resolve($assignAndRootExpr, $key);
2020-06-30 11:01:20 +02:00
$decoupledMethodCalls[] = $chainMethodCall;
}
if ($assignAndRootExpr->getRootExpr() instanceof \PhpParser\Node\Expr\New_ && $assignAndRootExpr->getSilentVariable() !== null) {
$decoupledMethodCalls[] = new \PhpParser\Node\Expr\Assign($assignAndRootExpr->getSilentVariable(), $assignAndRootExpr->getRootExpr());
2020-06-30 11:01:20 +02:00
}
return \array_reverse($decoupledMethodCalls);
2020-06-30 11:01:20 +02:00
}
}