1818use PHPStan \Reflection \ConstantNameHelper ;
1919use PHPStan \ShouldNotHappenException ;
2020use function array_key_exists ;
21+ use function hash ;
2122use function sprintf ;
2223use function strtolower ;
2324
2425final 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