Skip to content

Commit fd8aa33

Browse files
committed
Cache nodes in FileNodesFetcher
1 parent 38ea1ab commit fd8aa33

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

foo.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
function doFoo(A $a) {
4+
5+
}

src/Reflection/BetterReflection/SourceLocator/FileNodesFetcher.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace PHPStan\Reflection\BetterReflection\SourceLocator;
44

55
use PhpParser\NodeTraverser;
6+
use PHPStan\Cache\Cache;
67
use PHPStan\DependencyInjection\AutowiredParameter;
78
use PHPStan\DependencyInjection\AutowiredService;
89
use PHPStan\File\FileReader;
@@ -13,16 +14,44 @@
1314
final class FileNodesFetcher
1415
{
1516

17+
private const CACHE_ROOT_KEY = 'vendor-reflections';
18+
private const CACHE_VARIABLE_KEY = 'v2';
19+
20+
private array $data = [];
21+
1622
public function __construct(
1723
private CachingVisitor $cachingVisitor,
1824
#[AutowiredParameter(ref: '@defaultAnalysisParser')]
1925
private Parser $parser,
26+
private Cache $cache,
2027
)
2128
{
2229
}
2330

31+
private function persist(): void
32+
{
33+
$this->cache->save(self::CACHE_ROOT_KEY, self::CACHE_VARIABLE_KEY, $this->data);
34+
}
35+
36+
private function loadCache(): void
37+
{
38+
$cached = $this->cache->load(self::CACHE_ROOT_KEY, self::CACHE_VARIABLE_KEY);
39+
if ($cached !== null) {
40+
$this->data = $cached;
41+
}
42+
}
43+
44+
2445
public function fetchNodes(string $fileName): FetchedNodesResult
2546
{
47+
if ($this->data === []) {
48+
$this->loadCache();
49+
}
50+
51+
if (isset($this->data[$fileName])) {
52+
return unserialize($this->data[$fileName]);
53+
}
54+
2655
$nodeTraverser = new NodeTraverser();
2756
$nodeTraverser->addVisitor($this->cachingVisitor);
2857

@@ -44,6 +73,9 @@ public function fetchNodes(string $fileName): FetchedNodesResult
4473

4574
$this->cachingVisitor->reset($fileName, $contents);
4675

76+
$this->data[$fileName] = serialize($result);
77+
$this->persist();
78+
4779
return $result;
4880
}
4981

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\ReflectionProvider;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Cache\Cache;
8+
use PHPStan\Reflection\ClassReflection;
9+
use PHPStan\Reflection\ConstantReflection;
10+
use PHPStan\Reflection\FunctionReflection;
11+
use PHPStan\Reflection\NamespaceAnswerer;
12+
use PHPStan\Reflection\ReflectionProvider;
13+
use function strtolower;
14+
15+
final class CachedVendorReflectionProvider implements ReflectionProvider
16+
{
17+
18+
/** @var array<string, bool> */
19+
private array $hasClasses = [];
20+
21+
/** @var array<string, ClassReflection> */
22+
private array $classes = [];
23+
24+
/** @var array<string, string> */
25+
private array $classNames = [];
26+
27+
private const CACHE_ROOT_KEY = 'vendor-reflections';
28+
private const CACHE_VARIABLE_KEY = 'v1';
29+
30+
private bool $needsUpdate = false;
31+
32+
public function __construct(
33+
private ReflectionProvider $provider,
34+
private Cache $cache,
35+
)
36+
{
37+
register_shutdown_function(function() {
38+
if ($this->needsUpdate) {
39+
$this->persist();
40+
}
41+
});
42+
43+
$this->loadCache();
44+
}
45+
46+
private function persist(): void
47+
{
48+
$this->cache->save(self::CACHE_ROOT_KEY, self::CACHE_VARIABLE_KEY, [
49+
$this->hasClasses,
50+
$this->classNames
51+
]);
52+
}
53+
54+
private function loadCache(): void
55+
{
56+
$cached = $this->cache->load(self::CACHE_ROOT_KEY, self::CACHE_VARIABLE_KEY);
57+
if ($cached !== null) {
58+
[
59+
$this->hasClasses,
60+
$this->classNames
61+
] = $cached;
62+
}
63+
}
64+
65+
public function hasClass(string $className): bool
66+
{
67+
if (isset($this->hasClasses[$className])) {
68+
return $this->hasClasses[$className];
69+
}
70+
$this->needsUpdate = true;
71+
72+
return $this->hasClasses[$className] = $this->provider->hasClass($className);
73+
}
74+
75+
public function getClass(string $className): ClassReflection
76+
{
77+
$lowerClassName = strtolower($className);
78+
if (isset($this->classes[$lowerClassName])) {
79+
return $this->classes[$lowerClassName];
80+
}
81+
82+
return $this->classes[$lowerClassName] = $this->provider->getClass($className);
83+
}
84+
85+
public function getClassName(string $className): string
86+
{
87+
$lowerClassName = strtolower($className);
88+
if (isset($this->classNames[$lowerClassName])) {
89+
return $this->classNames[$lowerClassName];
90+
}
91+
$this->needsUpdate = true;
92+
93+
return $this->classNames[$lowerClassName] = $this->provider->getClassName($className);
94+
}
95+
96+
public function getAnonymousClassReflection(Node\Stmt\Class_ $classNode, Scope $scope): ClassReflection
97+
{
98+
return $this->provider->getAnonymousClassReflection($classNode, $scope);
99+
}
100+
101+
public function getUniversalObjectCratesClasses(): array
102+
{
103+
return $this->provider->getUniversalObjectCratesClasses();
104+
}
105+
106+
public function hasFunction(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): bool
107+
{
108+
return $this->provider->hasFunction($nameNode, $namespaceAnswerer);
109+
}
110+
111+
public function getFunction(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): FunctionReflection
112+
{
113+
return $this->provider->getFunction($nameNode, $namespaceAnswerer);
114+
}
115+
116+
public function resolveFunctionName(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): ?string
117+
{
118+
return $this->provider->resolveFunctionName($nameNode, $namespaceAnswerer);
119+
}
120+
121+
public function hasConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): bool
122+
{
123+
return $this->provider->hasConstant($nameNode, $namespaceAnswerer);
124+
}
125+
126+
public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): ConstantReflection
127+
{
128+
return $this->provider->getConstant($nameNode, $namespaceAnswerer);
129+
}
130+
131+
public function resolveConstantName(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): ?string
132+
{
133+
return $this->provider->resolveConstantName($nameNode, $namespaceAnswerer);
134+
}
135+
136+
}

0 commit comments

Comments
 (0)