22
33namespace PHPStan \Reflection \BetterReflection \SourceLocator ;
44
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_ ;
115use PHPStan \BetterReflection \Identifier \Identifier ;
126use PHPStan \BetterReflection \Identifier \IdentifierType ;
137use PHPStan \BetterReflection \Reflection \Reflection ;
8+ use PHPStan \BetterReflection \Reflection \ReflectionClass ;
149use PHPStan \BetterReflection \Reflection \ReflectionConstant ;
10+ use PHPStan \BetterReflection \Reflection \ReflectionEnum ;
11+ use PHPStan \BetterReflection \Reflection \ReflectionFunction ;
1512use PHPStan \BetterReflection \Reflector \Reflector ;
16- use PHPStan \BetterReflection \SourceLocator \Ast \Strategy \NodeToReflection ;
17- use PHPStan \BetterReflection \SourceLocator \Located \LocatedSource ;
1813use PHPStan \BetterReflection \SourceLocator \Type \SourceLocator ;
1914use PHPStan \Cache \Cache ;
2015use PHPStan \Internal \ComposerHelper ;
21- use PHPStan \Node \Expr \TypeExpr ;
2216use PHPStan \Php \PhpVersion ;
2317use PHPStan \Reflection \ConstantNameHelper ;
24- use PHPStan \ShouldNotHappenException ;
25- use PHPStan \Type \ConstantTypeHelper ;
26- use ReflectionClass ;
27- use ReflectionFunction ;
2818use 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 ;
19+ use function register_shutdown_function ;
20+ use function sprintf ;
4221use 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- */
22+
5423final class FileCachedSourceLocator implements SourceLocator
5524{
56- /** @var array<string, mixed> */
57- private array $ cached ;
25+
26+ /** @var array{classes: array<string, ReflectionClass>, functions: array<string, ReflectionFunction>, constants: array<string, ReflectionConstant>}|null */
27+ private ?array $ cachedSymbols = null ;
28+
29+ private bool $ storeOnShutdown = false ;
5830
5931 public function __construct (
6032 private SourceLocator $ locator ,
@@ -63,45 +35,131 @@ public function __construct(
6335 private string $ cacheKey ,
6436 )
6537 {
66- $ variableCacheKey = $ this ->getVariableCacheKey ();
67- $ this ->cached = $ this ->cache ->load ($ this ->cacheKey , $ variableCacheKey ) ?? [];
6838 }
6939
70-
71- public function locateIdentifier (Reflector $ reflector , Identifier $ identifier ): ?\PHPStan \BetterReflection \Reflection \Reflection
40+ public function locateIdentifier (Reflector $ reflector , Identifier $ identifier ): ?Reflection
7241 {
73- $ key = $ identifier ->getName ();
42+ if ($ this ->cachedSymbols === null ) {
43+ $ this ->cachedSymbols = $ this ->loadCache ($ reflector );
44+ }
45+
46+ if ($ identifier ->isClass ()) {
47+ $ className = strtolower ($ identifier ->getName ());
48+
49+ if (!array_key_exists ($ className , $ this ->cachedSymbols ['classes ' ])) {
50+ $ this ->cachedSymbols ['classes ' ][$ className ] ??= $ this ->locator ->locateIdentifier ($ reflector , $ identifier );
51+ $ this ->storeOnShutdown ();
52+ }
53+ return $ this ->cachedSymbols ['classes ' ][$ className ];
54+ }
55+ if ($ identifier ->isFunction ()) {
56+ $ className = strtolower ($ identifier ->getName ());
57+
58+ if (!array_key_exists ($ className , $ this ->cachedSymbols ['functions ' ])) {
59+ $ this ->cachedSymbols ['functions ' ][$ className ] ??= $ this ->locator ->locateIdentifier ($ reflector , $ identifier );
60+ $ this ->storeOnShutdown ();
61+ }
62+ return $ this ->cachedSymbols ['functions ' ][$ className ];
63+ }
64+ if ($ identifier ->isConstant ()) {
65+ $ constantName = ConstantNameHelper::normalize ($ identifier ->getName ());
7466
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 ();
67+ if (!array_key_exists ($ constantName , $ this ->cachedSymbols ['constants ' ])) {
68+ $ this ->cachedSymbols ['constants ' ][$ constantName ] ??= $ this ->locator ->locateIdentifier ($ reflector , $ identifier );
69+ $ this ->storeOnShutdown ();
70+ }
71+ return $ this ->cachedSymbols ['constants ' ][$ constantName ];
7972 }
8073
81- return $ this -> cached [ ' identifier ' ][ $ key ] ;
74+ return null ;
8275 }
8376
8477 public function locateIdentifiersByType (Reflector $ reflector , IdentifierType $ identifierType ): array
8578 {
86- $ key = $ identifierType ->getName ();
79+ return $ this ->locator ->locateIdentifiersByType ($ reflector , $ identifierType );
80+ }
81+
82+ private function getVariableCacheKey (): string
83+ {
84+ return sprintf ('v2-%s-%s ' , ComposerHelper::getBetterReflectionVersion (), $ this ->phpVersion ->getVersionString ());
85+ }
8786
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 () ;
87+ private function storeOnShutdown (): void
88+ {
89+ if ( $ this ->storeOnShutdown ) {
90+ return ;
9291 }
9392
94- return $ this ->cached ['identifiersByType ' ][$ key ];
93+ $ this ->storeOnShutdown = true ;
94+ register_shutdown_function ([$ this , 'storeCache ' ]);
9595 }
9696
97- private function getVariableCacheKey (): string
97+ /** @return array{classes: array<string, ReflectionClass>, functions: array<string, ReflectionFunction>, constants: array<string, ReflectionConstant>} */
98+ private function loadCache (Reflector $ reflector ): array
9899 {
99- return sprintf ('v1-%s-%s ' , ComposerHelper::getBetterReflectionVersion (), $ this ->phpVersion ->getVersionString ());
100+ $ variableCacheKey = $ this ->getVariableCacheKey ();
101+ $ cached = $ this ->cache ->load ($ this ->cacheKey , $ variableCacheKey );
102+
103+ $ restored = [
104+ 'classes ' => [],
105+ 'functions ' => [],
106+ 'constants ' => [],
107+ ];
108+ if ($ cached === null ) {
109+ return $ restored ;
110+ }
111+
112+ foreach ($ cached ['classes ' ] ?? [] as $ class => $ cachedReflection ) {
113+ if ($ cachedReflection === null ) {
114+ $ restored ['classes ' ][$ class ] = null ;
115+ continue ;
116+ }
117+
118+ if (array_key_exists ('backingType ' , $ cachedReflection )) {
119+ $ restored ['classes ' ][$ class ] = ReflectionEnum::importFromCache ($ reflector , $ cachedReflection );
120+ continue ;
121+ }
122+
123+ $ restored ['classes ' ][$ class ] = ReflectionClass::importFromCache ($ reflector , $ cachedReflection );
124+ }
125+ foreach ($ cached ['functions ' ] ?? [] as $ class => $ cachedReflection ) {
126+ if ($ cachedReflection === null ) {
127+ $ restored ['functions ' ][$ class ] = null ;
128+ continue ;
129+ }
130+ $ restored ['functions ' ][$ class ] = ReflectionFunction::importFromCache ($ reflector , $ cachedReflection );
131+ }
132+ foreach ($ cached ['constants ' ] ?? [] as $ constantName => $ cachedReflection ) {
133+ if ($ cachedReflection === null ) {
134+ $ restored ['constants ' ][$ constantName ] = null ;
135+ continue ;
136+ }
137+
138+ $ restored ['constants ' ][$ constantName ] = ReflectionConstant::importFromCache ($ reflector , $ cachedReflection );
139+ }
140+ return $ restored ;
100141 }
101142
102143 private function storeCache (): void
103144 {
104145 $ variableCacheKey = $ this ->getVariableCacheKey ();
105- $ this ->cache ->save ($ this ->cacheKey , $ variableCacheKey , $ this ->cached );
146+
147+ $ exported = [
148+ 'classes ' => [],
149+ 'functions ' => [],
150+ 'constants ' => [],
151+ ];
152+ foreach ($ this ->cachedSymbols ?? [] as $ type => $ data ) {
153+ foreach ($ data as $ name => $ reflection ) {
154+ if ($ reflection === null ) {
155+ $ exported [$ type ][$ name ] = $ reflection ;
156+ continue ;
157+ }
158+ $ exported [$ type ][$ name ] = $ reflection ->exportToCache ();
159+ }
160+ }
161+
162+ $ this ->cache ->save ($ this ->cacheKey , $ variableCacheKey , $ exported );
106163 }
164+
107165}
0 commit comments