Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove inefficient caching from PhpMethodReflection #3534

Open
wants to merge 6 commits into
base: 2.0.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,16 @@ services:
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Parser\VariadicMethodsVisitor
tags:
- phpstan.parser.richParserNodeVisitor
staabm marked this conversation as resolved.
Show resolved Hide resolved

-
class: PHPStan\Parser\VariadicFunctionsVisitor
tags:
- phpstan.parser.richParserNodeVisitor

-
class: PHPStan\Node\Printer\ExprPrinter

Expand Down Expand Up @@ -634,9 +644,6 @@ services:
tags:
- phpstan.diagnoseExtension

-
class: PHPStan\Parser\FunctionCallStatementFinder

-
class: PHPStan\Process\CpuCoreCounter

Expand Down
47 changes: 0 additions & 47 deletions src/Parser/FunctionCallStatementFinder.php

This file was deleted.

4 changes: 4 additions & 0 deletions src/Parser/SimpleParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ final class SimpleParser implements Parser
public function __construct(
private \PhpParser\Parser $parser,
private NameResolver $nameResolver,
private VariadicMethodsVisitor $variadicMethodsVisitor,
private VariadicFunctionsVisitor $variadicFunctionsVisitor,
)
{
}
Expand Down Expand Up @@ -48,6 +50,8 @@ public function parseString(string $sourceCode): array

$nodeTraverser = new NodeTraverser();
$nodeTraverser->addVisitor($this->nameResolver);
$nodeTraverser->addVisitor($this->variadicMethodsVisitor);
$nodeTraverser->addVisitor($this->variadicFunctionsVisitor);

/** @var array<Node\Stmt> */
return $nodeTraverser->traverse($nodes);
Expand Down
102 changes: 102 additions & 0 deletions src/Parser/VariadicFunctionsVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\TrinaryLogic;
use function array_key_exists;
use function in_array;

final class VariadicFunctionsVisitor extends NodeVisitorAbstract
{

private ?Node $topNode = null;

private ?string $inNamespace = null;

private ?string $inFunction = null;

/** @var array<string, TrinaryLogic> */
private array $variadicFunctions = [];

public const ATTRIBUTE_NAME = 'variadicFunctions';

public function beforeTraverse(array $nodes): ?array
{
$this->topNode = null;
$this->variadicFunctions = [];
$this->inNamespace = null;
$this->inFunction = null;

return null;
}

public function enterNode(Node $node): ?Node
{
if ($this->topNode === null) {
$this->topNode = $node;
}

if ($node instanceof Node\Stmt\Namespace_ && $node->name !== null) {
$this->inNamespace = $node->name->toString();
}

if ($node instanceof Node\Stmt\Function_) {
$this->inFunction = $this->inNamespace !== null ? $this->inNamespace . '\\' . $node->name->name : $node->name->name;

foreach ($node->params as $parameter) {
if (!$parameter->variadic) {
continue;
}

if (!array_key_exists($this->inFunction, $this->variadicFunctions)) {
$this->variadicFunctions[$this->inFunction] = TrinaryLogic::createYes();
} else {
$this->variadicFunctions[$this->inFunction]->and(TrinaryLogic::createYes());
}
}
}

if (
$this->inFunction !== null
&& $node instanceof Node\Expr\FuncCall
&& $node->name instanceof Name
&& in_array((string) $node->name, ParametersAcceptor::VARIADIC_FUNCTIONS, true)
) {
if (!array_key_exists($this->inFunction, $this->variadicFunctions)) {
$this->variadicFunctions[$this->inFunction] = TrinaryLogic::createYes();
} else {
$this->variadicFunctions[$this->inFunction]->and(TrinaryLogic::createYes());
}
}

return null;
}

public function leaveNode(Node $node): ?Node
{
if ($node instanceof Node\Stmt\Namespace_ && $node->name !== null) {
$this->inNamespace = null;
}

if ($node instanceof Node\Stmt\Function_ && $this->inFunction !== null) {
$this->variadicFunctions[$this->inFunction] ??= TrinaryLogic::createNo();
$this->inFunction = null;
}

return null;
}

public function afterTraverse(array $nodes): ?array
{
if ($this->topNode !== null && $this->variadicFunctions !== []) {
$this->topNode->setAttribute(self::ATTRIBUTE_NAME, $this->variadicFunctions);
}

return null;
}

}
133 changes: 133 additions & 0 deletions src/Parser/VariadicMethodsVisitor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\NodeVisitorAbstract;
use PHPStan\Reflection\ParametersAcceptor;
use PHPStan\TrinaryLogic;
use function array_key_exists;
use function array_pop;
use function implode;
use function in_array;

final class VariadicMethodsVisitor extends NodeVisitorAbstract
{

private ?Node $topNode = null;

private ?string $inNamespace = null;

private ?string $inClassLike = null;

/** @var array<string> */
private array $classStack = [];

private ?string $inMethod = null;

/** @var array<string, array<string, TrinaryLogic>> */
private array $variadicMethods = [];

public const ATTRIBUTE_NAME = 'variadicMethods';

public function beforeTraverse(array $nodes): ?array
{
$this->topNode = null;
$this->variadicMethods = [];
$this->inNamespace = null;
$this->classStack = [];
$this->inClassLike = null;
$this->inMethod = null;

return null;
}

public function enterNode(Node $node): ?Node
{
if ($this->topNode === null) {
$this->topNode = $node;
}

if ($node instanceof Node\Stmt\Namespace_ && $node->name !== null) {
$this->inNamespace = $node->name->toString();
}

if (
$node instanceof Node\Stmt\Class_
|| $node instanceof Node\Stmt\ClassLike
) {
if (!$node->name instanceof Node\Identifier) {
$className = 'class@anonymous:' . $node->getStartLine();
} else {
$className = $node->name->name;
}

$this->classStack[] = $className;
$this->inClassLike = $this->inNamespace !== null ? $this->inNamespace . '\\' . implode('\\', $this->classStack) : implode('\\', $this->classStack);
$this->variadicMethods[$this->inClassLike] ??= [];
}

if ($this->inClassLike !== null && $node instanceof ClassMethod) {
$this->inMethod = $node->name->name;
}

if (
$this->inClassLike !== null
&& $this->inMethod !== null
&& $node instanceof Node\Expr\FuncCall
&& $node->name instanceof Name
&& in_array((string) $node->name, ParametersAcceptor::VARIADIC_FUNCTIONS, true)
) {
if (!array_key_exists($this->inMethod, $this->variadicMethods[$this->inClassLike])) {
$this->variadicMethods[$this->inClassLike][$this->inMethod] = TrinaryLogic::createYes();
} else {
$this->variadicMethods[$this->inClassLike][$this->inMethod]->and(TrinaryLogic::createYes());
}

}

return null;
}

public function leaveNode(Node $node): ?Node
{
if (
$node instanceof ClassMethod
&& $this->inClassLike !== null
) {
$this->variadicMethods[$this->inClassLike][$node->name->name] ??= TrinaryLogic::createNo();
$this->inMethod = null;
}

if (
$node instanceof Node\Stmt\Class_
|| $node instanceof Node\Stmt\ClassLike
) {
array_pop($this->classStack);

if ($this->classStack !== []) {
$this->inClassLike = $this->inNamespace !== null ? $this->inNamespace . '\\' . implode('\\', $this->classStack) : implode('\\', $this->classStack);
} else {
$this->inClassLike = null;
}
}

if ($node instanceof Node\Stmt\Namespace_ && $node->name !== null) {
$this->inNamespace = null;
}

return null;
}

public function afterTraverse(array $nodes): ?array
{
if ($this->topNode !== null && $this->variadicMethods !== []) {
$this->topNode->setAttribute(self::ATTRIBUTE_NAME, $this->variadicMethods);
}

return null;
}

}
Loading
Loading