Skip to content

Commit 27db610

Browse files
committed
FileTypeMapper caching
1 parent 270e136 commit 27db610

2 files changed

Lines changed: 82 additions & 20 deletions

File tree

src/Analyser/IntermediaryNameScope.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,4 +146,24 @@ public function getClassNameForTypeAlias(): ?string
146146
return $this->typeAliasClassName;
147147
}
148148

149+
/**
150+
* @param array<string, mixed> $properties
151+
*/
152+
public static function __set_state(array $properties): self
153+
{
154+
return new self(
155+
$properties['namespace'],
156+
$properties['uses'],
157+
$properties['className'],
158+
$properties['functionName'],
159+
$properties['templatePhpDocNodes'],
160+
$properties['parent'],
161+
$properties['typeAliasesMap'],
162+
$properties['bypassTypeAliases'],
163+
$properties['constUses'],
164+
$properties['typeAliasClassName'],
165+
$properties['traitData'],
166+
);
167+
}
168+
149169
}

src/Type/FileTypeMapper.php

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Analyser\NameScope;
99
use PHPStan\BetterReflection\Util\GetLastDocComment;
1010
use PHPStan\Broker\AnonymousClassNameHelper;
11+
use PHPStan\Cache\Cache;
1112
use PHPStan\DependencyInjection\AutowiredParameter;
1213
use PHPStan\DependencyInjection\AutowiredService;
1314
use PHPStan\File\FileHelper;
@@ -37,6 +38,7 @@
3738
use function array_reverse;
3839
use function array_slice;
3940
use function count;
41+
use function hash_file;
4042
use function in_array;
4143
use function is_array;
4244
use function is_file;
@@ -54,7 +56,7 @@ final class FileTypeMapper
5456
private const SKIP_NODE = 1;
5557
private const POP_TYPE_MAP_STACK = 2;
5658

57-
/** @var array<string, array{array<string, PhpDocNode>, array<string, IntermediaryNameScope>}> */
59+
/** @var array<string, array{array<string, IntermediaryNameScope>}> */
5860
private array $memoryCache = [];
5961

6062
private int $memoryCacheCount = 0;
@@ -75,6 +77,7 @@ public function __construct(
7577
private PhpDocNodeResolver $phpDocNodeResolver,
7678
private AnonymousClassNameHelper $anonymousClassNameHelper,
7779
private FileHelper $fileHelper,
80+
private Cache $cache,
7881
)
7982
{
8083
}
@@ -197,7 +200,7 @@ public function getNameScope(
197200
throw new NameScopeAlreadyBeingCreatedException();
198201
}
199202

200-
[, $nameScopeMap] = $this->getNameScopeMap($fileName);
203+
[$nameScopeMap] = $this->getNameScopeMap($fileName);
201204
if (!isset($nameScopeMap[$nameScopeKey])) {
202205
throw new NameScopeAlreadyBeingCreatedException();
203206
}
@@ -317,12 +320,25 @@ public function getNameScope(
317320
}
318321

319322
/**
320-
* @return array{array<string, PhpDocNode>, array<string, IntermediaryNameScope>}
323+
* @return array{array<string, IntermediaryNameScope>}
321324
*/
322325
private function getNameScopeMap(string $fileName): array
323326
{
324327
if (!isset($this->memoryCache[$fileName])) {
325-
[$phpDocNodeMap, $nameScopeMap] = $this->createPhpDocNodeMap($fileName, null, null, [], $fileName);
328+
$cacheKey = sprintf('ftm-%s', $fileName);
329+
$variableCacheKey = 'v2';
330+
$cached = $this->loadCachedPhpDocNodeMap($cacheKey, $variableCacheKey);
331+
if ($cached === null) {
332+
[$nameScopeMap, $files] = $this->createPhpDocNodeMap($fileName, null, null, [], $fileName);
333+
$filesWithHashes = [];
334+
foreach ($files as $file) {
335+
$newHash = hash_file('sha256', $file);
336+
$filesWithHashes[$file] = $newHash;
337+
}
338+
$this->cache->save($cacheKey, $variableCacheKey, [$nameScopeMap, $filesWithHashes]);
339+
} else {
340+
[$nameScopeMap, $files] = $cached;
341+
}
326342
if ($this->memoryCacheCount >= 2048) {
327343
$this->memoryCache = array_slice(
328344
$this->memoryCache,
@@ -332,22 +348,53 @@ private function getNameScopeMap(string $fileName): array
332348
$this->memoryCacheCount--;
333349
}
334350

335-
$this->memoryCache[$fileName] = [$phpDocNodeMap, $nameScopeMap];
351+
$this->memoryCache[$fileName] = [$nameScopeMap, $files];
336352
$this->memoryCacheCount++;
337353
}
338354

339355
return $this->memoryCache[$fileName];
340356
}
341357

358+
/**
359+
* @param non-empty-string $cacheKey
360+
* @return array{array<string, IntermediaryNameScope>, list<string>}|null
361+
*/
362+
private function loadCachedPhpDocNodeMap(string $cacheKey, string $variableCacheKey): ?array
363+
{
364+
$cached = $this->cache->load($cacheKey, $variableCacheKey);
365+
if ($cached !== null) {
366+
/**
367+
* @var array<string, string> $filesWithHashes
368+
*/
369+
[$nameScopeMap, $filesWithHashes] = $cached;
370+
$useCache = true;
371+
foreach ($filesWithHashes as $file => $hash) {
372+
if (!is_file($file)) {
373+
$useCache = false;
374+
break;
375+
}
376+
$newHash = hash_file('sha256', $file);
377+
if ($newHash === $hash) {
378+
continue;
379+
}
380+
$useCache = false;
381+
break;
382+
}
383+
384+
if ($useCache) {
385+
return [$nameScopeMap, array_keys($filesWithHashes)];
386+
}
387+
}
388+
389+
return null;
390+
}
391+
342392
/**
343393
* @param array<string, string> $traitMethodAliases
344-
* @return array{array<string, PhpDocNode>, array<string, IntermediaryNameScope>}
394+
* @return array{array<string, IntermediaryNameScope>, list<string>}
345395
*/
346396
private function createPhpDocNodeMap(string $fileName, ?string $lookForTrait, ?string $traitUseClass, array $traitMethodAliases, string $originalClassFileName): array
347397
{
348-
/** @var array<string, PhpDocNode> $phpDocNodeMap */
349-
$phpDocNodeMap = [];
350-
351398
/** @var array<string, IntermediaryNameScope> $nameScopeMap */
352399
$nameScopeMap = [];
353400

@@ -367,13 +414,15 @@ private function createPhpDocNodeMap(string $fileName, ?string $lookForTrait, ?s
367414

368415
$traitFound = false;
369416

417+
$files = [$fileName];
418+
370419
/** @var array<string|null> $functionStack */
371420
$functionStack = [];
372421
$uses = [];
373422
$constUses = [];
374423
$this->processNodes(
375424
$this->phpParser->parseFile($fileName),
376-
function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodAliases, $originalClassFileName, &$phpDocNodeMap, &$nameScopeMap, &$typeMapStack, &$typeAliasStack, &$classStack, &$namespace, &$functionStack, &$uses, &$constUses): ?int {
425+
function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodAliases, $originalClassFileName, &$nameScopeMap, &$typeMapStack, &$typeAliasStack, &$classStack, &$namespace, &$functionStack, &$uses, &$constUses, &$files): ?int {
377426
if ($node instanceof Node\Stmt\ClassLike) {
378427
if ($traitFound && $fileName === $originalClassFileName) {
379428
return self::SKIP_NODE;
@@ -434,13 +483,7 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
434483
) {
435484
$docComment = GetLastDocComment::forNode($node);
436485
if ($docComment !== null) {
437-
$phpDocKey = $this->getPhpDocKey($nameScopeKey, $docComment);
438486
$phpDocNode = $this->phpDocStringResolver->resolve($docComment);
439-
$phpDocNodeMap[$phpDocKey] = $phpDocNode;
440-
$plainDocComment = $node->getDocComment();
441-
if ($plainDocComment !== null && $plainDocComment->getText() !== $docComment) {
442-
$phpDocNodeMap[$this->getPhpDocKey($nameScopeKey, $plainDocComment->getText())] = $phpDocNode;
443-
}
444487
}
445488
}
446489

@@ -576,16 +619,15 @@ function (Node $node) use ($fileName, $lookForTrait, &$traitFound, $traitMethodA
576619
throw new ShouldNotHappenException();
577620
}
578621

579-
[$traitPhpDocNodeMap, $traitNameScopeMap] = $this->createPhpDocNodeMap(
622+
[$traitNameScopeMap, $traitFiles] = $this->createPhpDocNodeMap(
580623
$traitReflection->getFileName(),
581624
$traitName,
582625
$className,
583626
$traitMethodAliases[$traitName] ?? [],
584627
$originalClassFileName,
585628
);
586-
$phpDocNodeMap = array_merge($phpDocNodeMap, $traitPhpDocNodeMap);
587629
$nameScopeMap = array_merge($nameScopeMap, array_map(static fn ($originalNameScope) => $originalNameScope->getTraitData() === null ? $originalNameScope->withTraitData($fileName, $className, $traitName, $lookForTrait, $docComment) : $originalNameScope, $traitNameScopeMap));
588-
630+
$files = array_merge($files, $traitFiles);
589631
}
590632
}
591633

@@ -644,7 +686,7 @@ static function (Node $node, $callbackResult) use (&$namespace, &$functionStack,
644686
throw new ShouldNotHappenException();
645687
}
646688

647-
return [$phpDocNodeMap, $nameScopeMap];
689+
return [$nameScopeMap, $files];
648690
}
649691

650692
/**

0 commit comments

Comments
 (0)