Skip to content

Commit

Permalink
Performance: Improve AstResolver caching (#3485)
Browse files Browse the repository at this point in the history
* Performance: Improve AstResolver caching

* Use findFirst in AstResolver
  • Loading branch information
keulinho committed Mar 20, 2023
1 parent 5371408 commit 7dcd5f6
Showing 1 changed file with 39 additions and 71 deletions.
110 changes: 39 additions & 71 deletions src/PhpParser/AstResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,11 @@ final class AstResolver
{
/**
* Parsing files is very heavy performance, so this will help to leverage it
* The value can be also null, as the method might not exist in the class.
* The value can be also null, when no statements could be parsed from the file.
*
* @var array<class-string, array<string, ClassMethod|null>>
* @var array<string, Stmt[]|null>
*/
private array $classMethodsByClassAndMethod = [];

/**
* Parsing files is very heavy performance, so this will help to leverage it
* The value can be also null, as the method might not exist in the class.
*
* @var array<string, Function_|null>>
*/
private array $functionsByName = [];
private array $parsedFileNodes = [];

public function __construct(
private readonly SmartPhpParser $smartPhpParser,
Expand Down Expand Up @@ -87,16 +79,6 @@ public function resolveClassMethodFromMethodReflection(MethodReflection $methodR
$classLikeName = $classReflection->getName();
$methodName = $methodReflection->getName();

if (isset($this->classMethodsByClassAndMethod[$classLikeName][$methodName])) {
return $this->classMethodsByClassAndMethod[$classLikeName][$methodName];
}

// saved as null data
if (array_key_exists($classLikeName, $this->classMethodsByClassAndMethod)
&& array_key_exists($methodName, $this->classMethodsByClassAndMethod[$classLikeName])) {
return null;
}

$fileName = $classReflection->getFileName();

// probably native PHP method → un-parseable
Expand All @@ -109,25 +91,19 @@ public function resolveClassMethodFromMethodReflection(MethodReflection $methodR
return null;
}

/** @var ClassLike[] $classLikes */
$classLikes = $this->betterNodeFinder->findInstanceOf($nodes, ClassLike::class);

foreach ($classLikes as $classLike) {
if (! $this->nodeNameResolver->isName($classLike, $classLikeName)) {
continue;
}

$classMethod = $classLike->getMethod($methodName);
if (! $classMethod instanceof ClassMethod) {
continue;
}
/** @var ClassLike|null $classLike */
$classLike = $this->betterNodeFinder->findFirst(
$nodes,
fn (Node $node): bool => $node instanceof ClassLike && $this->nodeNameResolver->isName(
$node,
$classLikeName
) && $node->getMethod($methodName) instanceof ClassMethod
);

$this->classMethodsByClassAndMethod[$classLikeName][$methodName] = $classMethod;
return $classMethod;
if ($classLike instanceof ClassLike && ($method = $classLike->getMethod($methodName)) instanceof ClassMethod) {
return $method;
}

// avoids looking for a class in a file where is not present
$this->classMethodsByClassAndMethod[$classLikeName][$methodName] = null;
return null;
}

Expand All @@ -146,15 +122,6 @@ public function resolveFunctionFromFunctionReflection(FunctionReflection $functi
{
$functionName = $functionReflection->getName();

if (isset($this->functionsByName[$functionName])) {
return $this->functionsByName[$functionName];
}

// saved as null data
if (array_key_exists($functionName, $this->functionsByName)) {
return null;
}

$fileName = $functionReflection->getFileName();
if ($fileName === null) {
return null;
Expand All @@ -165,23 +132,16 @@ public function resolveFunctionFromFunctionReflection(FunctionReflection $functi
return null;
}

/** @var Function_[] $functions */
$functions = $this->betterNodeFinder->findInstanceOf($nodes, Function_::class);
foreach ($functions as $function) {
if (! $this->nodeNameResolver->isName($function, $functionName)) {
continue;
}

// to avoid parsing missing function again
$this->functionsByName[$functionName] = $function;

return $function;
}

// to avoid parsing missing function again
$this->functionsByName[$functionName] = null;
/** @var Function_|null $function */
$function = $this->betterNodeFinder->findFirst(
$nodes,
fn (Node $node): bool => $node instanceof Function_ && $this->nodeNameResolver->isName(
$node,
$functionName
)
);

return null;
return $function;
}

/**
Expand Down Expand Up @@ -285,12 +245,17 @@ public function resolvePropertyFromPropertyReflection(
$nativeReflectionProperty = $phpPropertyReflection->getNativeReflection();
$desiredPropertyName = $nativeReflectionProperty->getName();

/** @var Property[] $properties */
$properties = $this->betterNodeFinder->findInstanceOf($nodes, Property::class);
foreach ($properties as $property) {
if ($this->nodeNameResolver->isName($property, $desiredPropertyName)) {
return $property;
}
/** @var Property|null $property */
$property = $this->betterNodeFinder->findFirst(
$nodes,
fn (Node $node): bool => $node instanceof Property && $this->nodeNameResolver->isName(
$node,
$desiredPropertyName
)
);

if ($property instanceof Property) {
return $property;
}

// promoted property
Expand All @@ -308,7 +273,6 @@ private function locateClassMethodInTrait(string $methodName, MethodReflection $
fn (Node $node): bool => $node instanceof ClassMethod && $this->nodeNameResolver->isName($node, $methodName)
);

$this->classMethodsByClassAndMethod[$classReflection->getName()][$methodName] = $classMethod;
if ($classMethod instanceof ClassMethod) {
return $classMethod;
}
Expand All @@ -321,13 +285,17 @@ private function locateClassMethodInTrait(string $methodName, MethodReflection $
*/
private function parseFileNameToDecoratedNodes(string $fileName): ?array
{
if (isset($this->parsedFileNodes[$fileName])) {
return $this->parsedFileNodes[$fileName];
}

$stmts = $this->smartPhpParser->parseFile($fileName);
if ($stmts === []) {
return null;
return $this->parsedFileNodes[$fileName] = null;
}

$file = new File($fileName, FileSystem::read($fileName));
return $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $stmts);
return $this->parsedFileNodes[$fileName] = $this->nodeScopeAndMetadataDecorator->decorateNodesFromFile($file, $stmts);
}

/**
Expand Down

0 comments on commit 7dcd5f6

Please sign in to comment.