mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 12:52:36 +00:00
Fix too deep method chain calls performance on analysis
This commit is contained in:
parent
9962c25e55
commit
396f076b71
|
@ -179,7 +179,7 @@ CODE_SAMPLE
|
|||
|
||||
return $this->createFunction('in_array', $arguments);
|
||||
}
|
||||
|
||||
|
||||
private function combineCommentsToNode(Node $originalNode, Node $newNode): void
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable([$originalNode], function (Node $node): void {
|
||||
|
|
|
@ -5,10 +5,12 @@ namespace Rector\NodeTypeResolver\PHPStan\Scope;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PHPStan\Analyser\NodeScopeResolver as PHPStanNodeScopeResolver;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Broker\Broker;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor\RemoveDeepChainMethodCallNodeVisitor;
|
||||
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
|
||||
|
||||
/**
|
||||
|
@ -32,14 +34,21 @@ final class NodeScopeResolver
|
|||
*/
|
||||
private $broker;
|
||||
|
||||
/**
|
||||
* @var RemoveDeepChainMethodCallNodeVisitor
|
||||
*/
|
||||
private $removeDeepChainMethodCallNodeVisitor;
|
||||
|
||||
public function __construct(
|
||||
ScopeFactory $scopeFactory,
|
||||
PHPStanNodeScopeResolver $phpStanNodeScopeResolver,
|
||||
Broker $broker
|
||||
Broker $broker,
|
||||
RemoveDeepChainMethodCallNodeVisitor $removeDeepChainMethodCallNodeVisitor
|
||||
) {
|
||||
$this->scopeFactory = $scopeFactory;
|
||||
$this->phpStanNodeScopeResolver = $phpStanNodeScopeResolver;
|
||||
$this->broker = $broker;
|
||||
$this->removeDeepChainMethodCallNodeVisitor = $removeDeepChainMethodCallNodeVisitor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,8 +57,11 @@ final class NodeScopeResolver
|
|||
*/
|
||||
public function processNodes(array $nodes, string $filePath): array
|
||||
{
|
||||
$this->removeDeepChainMethodCallNodes($nodes);
|
||||
|
||||
$this->phpStanNodeScopeResolver->setAnalysedFiles([$filePath]);
|
||||
|
||||
// skip chain method calls, performance issue: https://github.com/phpstan/phpstan/issues/254
|
||||
$this->phpStanNodeScopeResolver->processNodes(
|
||||
$nodes,
|
||||
$this->scopeFactory->createFromFile($filePath),
|
||||
|
@ -88,4 +100,14 @@ final class NodeScopeResolver
|
|||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
private function removeDeepChainMethodCallNodes(array $nodes): void
|
||||
{
|
||||
$nodeTraverser = new NodeTraverser();
|
||||
$nodeTraverser->addVisitor($this->removeDeepChainMethodCallNodeVisitor);
|
||||
$nodeTraverser->traverse($nodes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\NodeTypeResolver\PHPStan\Scope\NodeVisitor;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use Rector\Utils\BetterNodeFinder;
|
||||
|
||||
/**
|
||||
* Skips performance trap in PHPStan: https://github.com/phpstan/phpstan/issues/254
|
||||
*/
|
||||
final class RemoveDeepChainMethodCallNodeVisitor extends NodeVisitorAbstract
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private const NESTED_CHAIN_METHOD_CALL_LIMIT = 10;
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
/**
|
||||
* @var Expression|null
|
||||
*/
|
||||
private $nodeToRemove;
|
||||
|
||||
public function __construct(BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|Node|null
|
||||
*/
|
||||
public function enterNode(Node $node)
|
||||
{
|
||||
if (! $node instanceof Expression) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node->expr instanceof MethodCall && $node->expr->var instanceof MethodCall) {
|
||||
$nestedChainMethodCalls = $this->betterNodeFinder->findInstanceOf([$node->expr], MethodCall::class);
|
||||
if (count($nestedChainMethodCalls) > self::NESTED_CHAIN_METHOD_CALL_LIMIT) {
|
||||
$this->nodeToRemove = $node;
|
||||
|
||||
return NodeTraverser::DONT_TRAVERSE_CHILDREN;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|Node|Node[]|null
|
||||
*/
|
||||
public function leaveNode(Node $node)
|
||||
{
|
||||
if ($node === $this->nodeToRemove) {
|
||||
return NodeTraverser::REMOVE_NODE;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user