Skip to content

Commit

Permalink
Improve memory consumption
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Oct 4, 2021
1 parent 25cb765 commit d781e34
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 6 deletions.
6 changes: 6 additions & 0 deletions conf/config.neon
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,12 @@ services:
autowired: no

currentPhpVersionSimpleParser:
class: PHPStan\Parser\CleaningParser
arguments:
wrappedParser: @currentPhpVersionSimpleDirectParser
autowired: no

currentPhpVersionSimpleDirectParser:
class: PHPStan\Parser\SimpleParser
arguments:
parser: @currentPhpVersionPhpParser
Expand Down
9 changes: 7 additions & 2 deletions src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -375,11 +375,16 @@ public static function begin(
/** @var FileFinder $fileFinder */
$fileFinder = $container->getService('fileFinderAnalyse');

$pathRoutingParser = $container->getService('pathRoutingParser');

/** @var \Closure(): (array{string[], bool}) $filesCallback */
$filesCallback = static function () use ($fileFinder, $paths): array {
$filesCallback = static function () use ($fileFinder, $pathRoutingParser, $paths): array {
$fileFinderResult = $fileFinder->findFiles($paths);
$files = $fileFinderResult->getFiles();

$pathRoutingParser->setAnalysedFiles($files);

return [$fileFinderResult->getFiles(), $fileFinderResult->isOnlyFiles()];
return [$files, $fileFinderResult->isOnlyFiles()];
};

return new InceptionResult(
Expand Down
42 changes: 42 additions & 0 deletions src/Parser/CleaningParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php declare(strict_types = 1);

namespace PHPStan\Parser;

use PhpParser\Node\Stmt;
use PhpParser\NodeTraverser;

class CleaningParser implements Parser
{

private Parser $wrappedParser;

private NodeTraverser $traverser;

public function __construct(Parser $wrappedParser)
{
$this->wrappedParser = $wrappedParser;
$this->traverser = new NodeTraverser();
$this->traverser->addVisitor(new CleaningVisitor());
}

public function parseFile(string $file): array
{
return $this->clean($this->wrappedParser->parseFile($file));
}

public function parseString(string $sourceCode): array
{
return $this->clean($this->wrappedParser->parseString($sourceCode));
}

/**
* @param Stmt[] $ast
* @return Stmt[]
*/
private function clean(array $ast): array
{
/** @var Stmt[] */
return $this->traverser->traverse($ast);
}

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

namespace PHPStan\Parser;

use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;

class CleaningVisitor extends NodeVisitorAbstract
{

public function enterNode(Node $node): ?Node
{
if ($node instanceof Node\Stmt\Function_) {
$node->stmts = [];
return $node;
}

if ($node instanceof Node\Stmt\ClassMethod) {
$node->stmts = [];
return $node;
}

if ($node instanceof Node\Expr\Closure) {
$node->stmts = [];
return $node;
}

return null;
}

}
15 changes: 15 additions & 0 deletions src/Parser/PathRoutingParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class PathRoutingParser implements Parser

private Parser $php8Parser;

/** @var bool[] filePath(string) => bool(true) */
private array $analysedFiles = [];

public function __construct(
FileHelper $fileHelper,
Parser $currentPhpVersionRichParser,
Expand All @@ -28,13 +31,25 @@ public function __construct(
$this->php8Parser = $php8Parser;
}

/**
* @param string[] $files
*/
public function setAnalysedFiles(array $files): void
{
$this->analysedFiles = array_fill_keys($files, true);
}

public function parseFile(string $file): array
{
$file = $this->fileHelper->normalizePath($file, '/');
if (strpos($file, 'vendor/jetbrains/phpstorm-stubs') !== false) {
return $this->php8Parser->parseFile($file);
}

if (!isset($this->analysedFiles[$file])) {
return $this->currentPhpVersionSimpleParser->parseFile($file);
}

return $this->currentPhpVersionRichParser->parseFile($file);
}

Expand Down
3 changes: 3 additions & 0 deletions src/PhpDoc/StubValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public function validate(array $stubFiles, bool $debug): array
$nodeScopeResolver = $container->getByType(NodeScopeResolver::class);
$nodeScopeResolver->setAnalysedFiles($stubFiles);

$pathRoutingParser = $container->getService('pathRoutingParser');
$pathRoutingParser->setAnalysedFiles($stubFiles);

$analysedFiles = array_fill_keys($stubFiles, true);

$errors = [];
Expand Down
2 changes: 1 addition & 1 deletion src/Reflection/Php/PhpClassReflectionExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ private function inferAndCachePropertyTypes(
}

$methodNode = $this->findConstructorNode($constructor->getName(), $classNode->stmts);
if ($methodNode === null || $methodNode->stmts === null) {
if ($methodNode === null || $methodNode->stmts === null || count($methodNode->stmts) === 0) {
return $this->propertyTypesCache[$declaringClass->getName()] = [];
}

Expand Down
2 changes: 1 addition & 1 deletion src/Reflection/Php/PhpFunctionReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private function isVariadic(): bool
if ($modifiedTime === false) {
$modifiedTime = time();
}
$variableCacheKey = sprintf('%d-v1', $modifiedTime);
$variableCacheKey = sprintf('%d-v2', $modifiedTime);
$key = sprintf('variadic-function-%s-%s', $functionName, $fileName);
$cachedResult = $this->cache->load($key, $variableCacheKey);
if ($cachedResult === null) {
Expand Down
2 changes: 1 addition & 1 deletion src/Reflection/Php/PhpMethodReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ private function isVariadic(): bool
$modifiedTime = time();
}
$key = sprintf('variadic-method-%s-%s-%s', $declaringClass->getName(), $this->reflection->getName(), $filename);
$variableCacheKey = sprintf('%d-v2', $modifiedTime);
$variableCacheKey = sprintf('%d-v3', $modifiedTime);
$cachedResult = $this->cache->load($key, $variableCacheKey);
if ($cachedResult === null || !is_bool($cachedResult)) {
$nodes = $this->parser->parseFile($filename);
Expand Down
2 changes: 2 additions & 0 deletions src/Testing/TestCase.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ services:
cacheStorage:
class: PHPStan\Cache\MemoryCacheStorage
arguments!: []
pathRoutingParser!:
factory: @currentPhpVersionRichParser
10 changes: 9 additions & 1 deletion tests/PHPStan/Parser/CachedParserTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Parser;

use PhpParser\Node\Stmt\Namespace_;
use PHPStan\File\FileHelper;
use PHPStan\File\FileReader;
use PHPStan\Testing\PHPStanTestCase;

Expand Down Expand Up @@ -81,8 +82,15 @@ private function getPhpParserNodeMock(): \PhpParser\Node

public function testParseTheSameFileWithDifferentMethod(): void
{
$parser = new CachedParser(self::getContainer()->getService('pathRoutingParser'), 500);
$pathRoutingParser = new PathRoutingParser(
self::getContainer()->getByType(FileHelper::class),
self::getContainer()->getService('currentPhpVersionRichParser'),
self::getContainer()->getService('currentPhpVersionSimpleParser'),
self::getContainer()->getService('php8Parser')
);
$parser = new CachedParser($pathRoutingParser, 500);
$path = __DIR__ . '/data/test.php';
$pathRoutingParser->setAnalysedFiles([$path]);
$contents = FileReader::read($path);
$stmts = $parser->parseString($contents);
$this->assertInstanceOf(Namespace_::class, $stmts[0]);
Expand Down

0 comments on commit d781e34

Please sign in to comment.