|
3 | 3 | namespace PHPStan\Reflection\BetterReflection\SourceLocator; |
4 | 4 |
|
5 | 5 | use Override; |
6 | | -use PhpParser\Node\Arg; |
7 | | -use PhpParser\Node\Expr\FuncCall; |
8 | | -use PhpParser\Node\Name; |
9 | | -use PhpParser\Node\Scalar\String_; |
10 | | -use PhpParser\Node\Stmt\Const_; |
11 | 6 | use PHPStan\BetterReflection\Identifier\Identifier; |
12 | 7 | use PHPStan\BetterReflection\Identifier\IdentifierType; |
13 | 8 | use PHPStan\BetterReflection\Reflection\Reflection; |
| 9 | +use PHPStan\BetterReflection\Reflection\ReflectionClass; |
14 | 10 | use PHPStan\BetterReflection\Reflection\ReflectionConstant; |
| 11 | +use PHPStan\BetterReflection\Reflection\ReflectionEnum; |
| 12 | +use PHPStan\BetterReflection\Reflection\ReflectionFunction; |
15 | 13 | use PHPStan\BetterReflection\Reflector\Reflector; |
16 | | -use PHPStan\BetterReflection\SourceLocator\Ast\Strategy\NodeToReflection; |
17 | | -use PHPStan\BetterReflection\SourceLocator\Located\LocatedSource; |
18 | 14 | use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator; |
19 | 15 | use PHPStan\Cache\Cache; |
20 | 16 | use PHPStan\Internal\ComposerHelper; |
21 | | -use PHPStan\Node\Expr\TypeExpr; |
22 | 17 | use PHPStan\Php\PhpVersion; |
23 | 18 | use PHPStan\Reflection\ConstantNameHelper; |
24 | 19 | use PHPStan\ShouldNotHappenException; |
25 | | -use PHPStan\Type\ConstantTypeHelper; |
26 | | -use ReflectionClass; |
27 | | -use ReflectionFunction; |
28 | 20 | use function array_key_exists; |
29 | | -use function array_keys; |
30 | | -use function class_exists; |
31 | | -use function constant; |
32 | | -use function count; |
33 | | -use function defined; |
34 | | -use function function_exists; |
35 | | -use function interface_exists; |
36 | | -use function is_file; |
37 | | -use function is_string; |
38 | | -use function opcache_invalidate; |
39 | | -use function restore_error_handler; |
40 | | -use function set_error_handler; |
41 | | -use function spl_autoload_functions; |
| 21 | +use function sprintf; |
42 | 22 | use function strtolower; |
43 | | -use function trait_exists; |
44 | | -use const PHP_VERSION_ID; |
45 | | - |
46 | | -/** |
47 | | - * Use PHP's built in autoloader to locate a class, without actually loading. |
48 | | - * |
49 | | - * There are some prerequisites... |
50 | | - * - we expect the autoloader to load classes from a file (i.e. using require/include) |
51 | | - * |
52 | | - * Modified code from Roave/BetterReflection, Copyright (c) 2017 Roave, LLC. |
53 | | - */ |
| 23 | + |
54 | 24 | final class FileCachedSourceLocator implements SourceLocator |
55 | 25 | { |
56 | | - /** @var array<string, mixed> */ |
57 | | - private array $cached; |
58 | 26 |
|
| 27 | + /** @var array{classes: array<string, ?Reflection>, functions: array<string, ?Reflection>, constants: array<string, ?Reflection>}|null */ |
| 28 | + private ?array $cachedSymbols = null; |
| 29 | + |
| 30 | + /** |
| 31 | + * @param non-empty-string $cacheKey |
| 32 | + */ |
59 | 33 | public function __construct( |
60 | 34 | private SourceLocator $locator, |
61 | 35 | private Cache $cache, |
62 | 36 | private PhpVersion $phpVersion, |
63 | 37 | private string $cacheKey, |
64 | 38 | ) |
65 | 39 | { |
66 | | - $variableCacheKey = $this->getVariableCacheKey(); |
67 | | - $this->cached = $this->cache->load($this->cacheKey, $variableCacheKey) ?? []; |
68 | 40 | } |
69 | 41 |
|
70 | | - |
71 | | - public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?\PHPStan\BetterReflection\Reflection\Reflection |
| 42 | + #[Override] |
| 43 | + public function locateIdentifier(Reflector $reflector, Identifier $identifier): ?Reflection |
72 | 44 | { |
73 | | - $key = $identifier->getName(); |
| 45 | + $this->cachedSymbols ??= $this->loadCache($reflector); |
| 46 | + |
| 47 | + if ($identifier->isClass()) { |
| 48 | + $className = strtolower($identifier->getName()); |
| 49 | + |
| 50 | + if (!array_key_exists($className, $this->cachedSymbols['classes'])) { |
| 51 | + $this->cachedSymbols['classes'][$className] = $this->locator->locateIdentifier($reflector, $identifier); |
| 52 | + $this->storeCache(); |
| 53 | + } |
| 54 | + return $this->cachedSymbols['classes'][$className]; |
| 55 | + } |
| 56 | + if ($identifier->isFunction()) { |
| 57 | + $className = strtolower($identifier->getName()); |
| 58 | + |
| 59 | + if (!array_key_exists($className, $this->cachedSymbols['functions'])) { |
| 60 | + $this->cachedSymbols['functions'][$className] = $this->locator->locateIdentifier($reflector, $identifier); |
| 61 | + $this->storeCache(); |
| 62 | + } |
| 63 | + return $this->cachedSymbols['functions'][$className]; |
| 64 | + } |
| 65 | + if ($identifier->isConstant()) { |
| 66 | + $constantName = ConstantNameHelper::normalize($identifier->getName()); |
74 | 67 |
|
75 | | - $this->cached['identifier'] ??= []; |
76 | | - if (!array_key_exists($key, $this->cached['identifier'])) { |
77 | | - $this->cached['identifier'][$key] = $this->locator->locateIdentifier($reflector, $identifier); |
78 | | - $this->storeCache(); |
| 68 | + if (!array_key_exists($constantName, $this->cachedSymbols['constants'])) { |
| 69 | + $this->cachedSymbols['constants'][$constantName] = $this->locator->locateIdentifier($reflector, $identifier); |
| 70 | + $this->storeCache(); |
| 71 | + } |
| 72 | + return $this->cachedSymbols['constants'][$constantName]; |
79 | 73 | } |
80 | 74 |
|
81 | | - return $this->cached['identifier'][$key]; |
| 75 | + return null; |
82 | 76 | } |
83 | 77 |
|
| 78 | + #[Override] |
84 | 79 | public function locateIdentifiersByType(Reflector $reflector, IdentifierType $identifierType): array |
85 | 80 | { |
86 | | - $key = $identifierType->getName(); |
87 | | - |
88 | | - $this->cached['identifiersByType'] ??= []; |
89 | | - if (!array_key_exists($key, $this->cached['identifiersByType'])) { |
90 | | - $this->cached['identifiersByType'][$key] = $this->locator->locateIdentifiersByType($reflector, $identifierType); |
91 | | - $this->storeCache(); |
92 | | - } |
93 | | - |
94 | | - return $this->cached['identifiersByType'][$key]; |
| 81 | + return $this->locator->locateIdentifiersByType($reflector, $identifierType); |
95 | 82 | } |
96 | 83 |
|
| 84 | + /** @return non-empty-string */ |
97 | 85 | private function getVariableCacheKey(): string |
98 | 86 | { |
99 | | - return sprintf('v1-%s-%s', ComposerHelper::getBetterReflectionVersion(), $this->phpVersion->getVersionString()); |
| 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; |
| 103 | + } |
| 104 | + |
| 105 | + foreach ($cached['classes'] ?? [] as $class => $cachedReflection) { |
| 106 | + if ($cachedReflection === null) { |
| 107 | + $restored['classes'][$class] = null; |
| 108 | + continue; |
| 109 | + } |
| 110 | + |
| 111 | + if (array_key_exists('backingType', $cachedReflection)) { |
| 112 | + $restored['classes'][$class] = ReflectionEnum::importFromCache($reflector, $cachedReflection); |
| 113 | + continue; |
| 114 | + } |
| 115 | + |
| 116 | + $restored['classes'][$class] = ReflectionClass::importFromCache($reflector, $cachedReflection); |
| 117 | + } |
| 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); |
| 124 | + } |
| 125 | + foreach ($cached['constants'] ?? [] as $constantName => $cachedReflection) { |
| 126 | + if ($cachedReflection === null) { |
| 127 | + $restored['constants'][$constantName] = null; |
| 128 | + continue; |
| 129 | + } |
| 130 | + |
| 131 | + $restored['constants'][$constantName] = ReflectionConstant::importFromCache($reflector, $cachedReflection); |
| 132 | + } |
| 133 | + return $restored; |
100 | 134 | } |
101 | 135 |
|
102 | 136 | private function storeCache(): void |
103 | 137 | { |
104 | 138 | $variableCacheKey = $this->getVariableCacheKey(); |
105 | | - $this->cache->save($this->cacheKey, $variableCacheKey, $this->cached); |
| 139 | + |
| 140 | + $exported = [ |
| 141 | + 'classes' => [], |
| 142 | + 'functions' => [], |
| 143 | + 'constants' => [], |
| 144 | + ]; |
| 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 | + } |
| 163 | + |
| 164 | + $this->cache->save($this->cacheKey, $variableCacheKey, $exported); |
106 | 165 | } |
| 166 | + |
107 | 167 | } |
0 commit comments