Skip to content

Commit b747262

Browse files
committed
Use separate cache per identifier
1 parent 92f802f commit b747262

File tree

1 file changed

+91
-74
lines changed

1 file changed

+91
-74
lines changed

src/Reflection/BetterReflection/SourceLocator/FileCachedSourceLocator.php

Lines changed: 91 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@
1818
use PHPStan\Reflection\ConstantNameHelper;
1919
use PHPStan\ShouldNotHappenException;
2020
use function array_key_exists;
21+
use function hash;
2122
use function sprintf;
2223
use function strtolower;
2324

2425
final class FileCachedSourceLocator implements SourceLocator
2526
{
2627

27-
/** @var array{classes: array<string, ?Reflection>, functions: array<string, ?Reflection>, constants: array<string, ?Reflection>}|null */
28-
private ?array $cachedSymbols = null;
28+
private const NOT_FOUND = null;
29+
private const NULL_CACHED = true;
30+
31+
/** @var array{classes: array<string, ?Reflection>, functions: array<string, ?Reflection>, constants: array<string, ?Reflection>} */
32+
private array $cachedSymbols = ['classes' => [], 'functions' => [], 'constants' => []];
2933

3034
/**
3135
* @param non-empty-string $cacheKey
@@ -42,32 +46,50 @@ public function __construct(
4246
#[Override]
4347
public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection
4448
{
45-
$this->cachedSymbols ??= $this->loadCache($reflector);
46-
4749
if ($identifier->isClass()) {
4850
$className = strtolower($identifier->getName());
4951

5052
if (!array_key_exists($className, $this->cachedSymbols['classes'])) {
51-
$this->cachedSymbols['classes'][$className] = $this->locator->locateIdentifier($reflector, $identifier);
52-
$this->storeCache();
53+
$result = $this->loadCache($identifier, $reflector);
54+
if ($result === self::NOT_FOUND) {
55+
$result = $this->locator->locateIdentifier($reflector, $identifier);
56+
$this->storeCache($identifier, $result);
57+
} elseif ($result === self::NULL_CACHED) {
58+
$result = null;
59+
}
60+
$this->cachedSymbols['classes'][$className] = $result;
5361
}
5462
return $this->cachedSymbols['classes'][$className];
5563
}
64+
5665
if ($identifier->isFunction()) {
5766
$className = strtolower($identifier->getName());
5867

5968
if (!array_key_exists($className, $this->cachedSymbols['functions'])) {
60-
$this->cachedSymbols['functions'][$className] = $this->locator->locateIdentifier($reflector, $identifier);
61-
$this->storeCache();
69+
$result = $this->loadCache($identifier, $reflector);
70+
if ($result === self::NOT_FOUND) {
71+
$result = $this->locator->locateIdentifier($reflector, $identifier);
72+
$this->storeCache($identifier, $result);
73+
} elseif ($result === self::NULL_CACHED) {
74+
$result = null;
75+
}
76+
$this->cachedSymbols['functions'][$className] = $result;
6277
}
6378
return $this->cachedSymbols['functions'][$className];
6479
}
80+
6581
if ($identifier->isConstant()) {
6682
$constantName = ConstantNameHelper::normalize($identifier->getName());
6783

6884
if (!array_key_exists($constantName, $this->cachedSymbols['constants'])) {
69-
$this->cachedSymbols['constants'][$constantName] = $this->locator->locateIdentifier($reflector, $identifier);
70-
$this->storeCache();
85+
$result = $this->loadCache($identifier, $reflector);
86+
if ($result === self::NOT_FOUND) {
87+
$result = $this->locator->locateIdentifier($reflector, $identifier);
88+
$this->storeCache($identifier, $result);
89+
} elseif ($result === self::NULL_CACHED) {
90+
$result = null;
91+
}
92+
$this->cachedSymbols['constants'][$constantName] = $result;
7193
}
7294
return $this->cachedSymbols['constants'][$constantName];
7395
}
@@ -81,87 +103,82 @@ public function locateIdentifiersByType(Reflector $reflector, IdentifierType $id
81103
return $this->locator->locateIdentifiersByType($reflector, $identifierType);
82104
}
83105

84-
/** @return non-empty-string */
85-
private function getVariableCacheKey(): string
106+
private function loadCache(
107+
Identifier $identifier,
108+
Reflector $reflector,
109+
): ReflectionClass|ReflectionFunction|ReflectionConstant|true|null
86110
{
87-
return sprintf('v2-%s-%s', ComposerHelper::getBetterReflectionVersion(), $this->phpVersion->getVersionString());
88-
}
89-
90-
/** @return array{classes: array<string, ReflectionClass|null>, functions: array<string, ReflectionFunction|null>, constants: array<string, ReflectionConstant|null>} */
91-
private function loadCache(Reflector $reflector): array
92-
{
93-
$variableCacheKey = $this->getVariableCacheKey();
94-
$cached = $this->cache->load($this->cacheKey, $variableCacheKey);
95-
96-
$restored = [
97-
'classes' => [],
98-
'functions' => [],
99-
'constants' => [],
100-
];
101-
if ($cached === null) {
102-
return $restored;
111+
[$cacheKey, $variableCacheKey] = $this->getCacheKeys($identifier);
112+
$cachedReflection = $this->cache->load(
113+
$cacheKey,
114+
$variableCacheKey,
115+
);
116+
117+
if ($cachedReflection === self::NOT_FOUND) {
118+
return self::NOT_FOUND;
119+
}
120+
if ($cachedReflection === self::NULL_CACHED) {
121+
return self::NULL_CACHED;
103122
}
104123

105-
foreach ($cached['classes'] ?? [] as $class => $cachedReflection) {
106-
if ($cachedReflection === null) {
107-
$restored['classes'][$class] = null;
108-
continue;
109-
}
110-
124+
if ($identifier->isClass()) {
111125
if (array_key_exists('backingType', $cachedReflection)) {
112-
$restored['classes'][$class] = ReflectionEnum::importFromCache($reflector, $cachedReflection);
113-
continue;
126+
return ReflectionEnum::importFromCache($reflector, $cachedReflection);
114127
}
115128

116-
$restored['classes'][$class] = ReflectionClass::importFromCache($reflector, $cachedReflection);
129+
return ReflectionClass::importFromCache($reflector, $cachedReflection);
117130
}
118-
foreach ($cached['functions'] ?? [] as $class => $cachedReflection) {
119-
if ($cachedReflection === null) {
120-
$restored['functions'][$class] = null;
121-
continue;
122-
}
123-
$restored['functions'][$class] = ReflectionFunction::importFromCache($reflector, $cachedReflection);
131+
132+
if ($identifier->isFunction()) {
133+
return ReflectionFunction::importFromCache($reflector, $cachedReflection);
124134
}
125-
foreach ($cached['constants'] ?? [] as $constantName => $cachedReflection) {
126-
if ($cachedReflection === null) {
127-
$restored['constants'][$constantName] = null;
128-
continue;
135+
136+
return ReflectionConstant::importFromCache($reflector, $cachedReflection);
137+
}
138+
139+
private function storeCache(
140+
Identifier $identifier,
141+
Reflection|null $reflection,
142+
): void
143+
{
144+
[$cacheKey, $variableCacheKey] = $this->getCacheKeys($identifier);
145+
146+
$exported = self::NULL_CACHED;
147+
if ($reflection !== null) {
148+
if (
149+
!$reflection instanceof ReflectionClass
150+
&& !$reflection instanceof ReflectionFunction
151+
&& !$reflection instanceof ReflectionConstant
152+
) {
153+
throw new ShouldNotHappenException();
129154
}
130155

131-
$restored['constants'][$constantName] = ReflectionConstant::importFromCache($reflector, $cachedReflection);
156+
$exported = $reflection->exportToCache();
132157
}
133-
return $restored;
158+
159+
$this->cache->save(
160+
$cacheKey,
161+
$variableCacheKey,
162+
$exported,
163+
);
134164
}
135165

136-
private function storeCache(): void
166+
/** @return array{non-empty-string, non-empty-string} */
167+
private function getCacheKeys(Identifier $identifier): array
137168
{
138-
$variableCacheKey = $this->getVariableCacheKey();
169+
$suffix = $this->getCacheKeySuffix($identifier);
139170

140-
$exported = [
141-
'classes' => [],
142-
'functions' => [],
143-
'constants' => [],
171+
return [
172+
sprintf('%s-%s', $this->cacheKey, $suffix),
173+
sprintf('v3-%s-%s-%s', ComposerHelper::getBetterReflectionVersion(), $this->phpVersion->getVersionString(), $suffix),
144174
];
145-
foreach ($this->cachedSymbols ?? [] as $type => $data) {
146-
foreach ($data as $name => $reflection) {
147-
if ($reflection === null) {
148-
$exported[$type][$name] = $reflection;
149-
continue;
150-
}
151-
152-
if (
153-
!$reflection instanceof ReflectionClass
154-
&& !$reflection instanceof ReflectionFunction
155-
&& !$reflection instanceof ReflectionConstant
156-
) {
157-
throw new ShouldNotHappenException();
158-
}
159-
160-
$exported[$type][$name] = $reflection->exportToCache();
161-
}
162-
}
175+
}
163176

164-
$this->cache->save($this->cacheKey, $variableCacheKey, $exported);
177+
/** @return non-empty-string */
178+
private function getCacheKeySuffix(Identifier $identifier): string
179+
{
180+
$identifierHash = hash('sha256', $identifier->getName());
181+
return sprintf('%s-%s', $identifier->getType()->getName(), $identifierHash);
165182
}
166183

167184
}

0 commit comments

Comments
 (0)