55use PHPStan \Analyser \NameScope ;
66use PHPStan \DependencyInjection \AutowiredParameter ;
77use PHPStan \DependencyInjection \AutowiredService ;
8+ use PHPStan \Internal \LruCache ;
89use PHPStan \PhpDoc \TypeNodeResolver ;
910use PHPStan \PhpDoc \TypeStringResolver ;
1011use PHPStan \Reflection \ReflectionProvider ;
1112use PHPStan \ShouldNotHappenException ;
1213use function array_key_exists ;
13- use function array_key_first ;
14- use function count ;
1514use function sprintf ;
1615
1716#[AutowiredService(as: TypeAliasResolver::class)]
@@ -21,8 +20,8 @@ final class UsefulTypeAliasResolver implements TypeAliasResolver
2120 /** @var array<string, Type> */
2221 private array $ resolvedGlobalTypeAliases = [];
2322
24- /** @var array<string, Type> LRU; first entry = least recently used */
25- private array $ resolvedLocalTypeAliases = [] ;
23+ /** @var LruCache< Type> */
24+ private LruCache $ resolvedLocalTypeAliases ;
2625
2726 /** @var array<string, true> */
2827 private array $ resolvingClassTypeAliases = [];
@@ -40,9 +39,10 @@ public function __construct(
4039 private TypeNodeResolver $ typeNodeResolver ,
4140 private ReflectionProvider $ reflectionProvider ,
4241 #[AutowiredParameter(ref: '%cache.resolvedLocalTypeAliasesCountMax% ' )]
43- private int $ resolvedLocalTypeAliasesCountMax ,
42+ int $ resolvedLocalTypeAliasesCountMax ,
4443 )
4544 {
45+ $ this ->resolvedLocalTypeAliases = new LruCache ($ resolvedLocalTypeAliasesCountMax );
4646 }
4747
4848 public function hasTypeAlias (string $ aliasName , ?string $ classNameScope ): bool
@@ -84,12 +84,9 @@ private function resolveLocalTypeAlias(string $aliasName, NameScope $nameScope):
8484
8585 $ aliasNameInClassScope = $ className . ':: ' . $ aliasName ;
8686
87- if (array_key_exists ($ aliasNameInClassScope , $ this ->resolvedLocalTypeAliases )) {
88- // LRU: move to the most-recently-used position
89- $ resolvedAliasType = $ this ->resolvedLocalTypeAliases [$ aliasNameInClassScope ];
90- unset($ this ->resolvedLocalTypeAliases [$ aliasNameInClassScope ]);
91-
92- return $ this ->resolvedLocalTypeAliases [$ aliasNameInClassScope ] = $ resolvedAliasType ;
87+ $ resolvedAliasType = $ this ->resolvedLocalTypeAliases ->get ($ aliasNameInClassScope );
88+ if ($ resolvedAliasType !== null ) {
89+ return $ resolvedAliasType ;
9390 }
9491
9592 // prevent infinite recursion
@@ -127,12 +124,9 @@ private function resolveLocalTypeAlias(string $aliasName, NameScope $nameScope):
127124 $ resolvedAliasType = new CircularTypeAliasErrorType ();
128125 }
129126
130- $ this ->resolvedLocalTypeAliases [$ aliasNameInClassScope ] = $ resolvedAliasType ;
131- if ($ this ->resolvedLocalTypeAliasesCountMax !== 0 && count ($ this ->resolvedLocalTypeAliases ) > $ this ->resolvedLocalTypeAliasesCountMax ) {
132- // resolved alias types transitively pin ClassReflections and their whole
133- // reflection trees — evict the least recently used
134- unset($ this ->resolvedLocalTypeAliases [array_key_first ($ this ->resolvedLocalTypeAliases )]);
135- }
127+ // resolved alias types transitively pin ClassReflections and their whole
128+ // reflection trees, so the cache is bounded and evicts the least recently used
129+ $ this ->resolvedLocalTypeAliases ->set ($ aliasNameInClassScope , $ resolvedAliasType , 0 );
136130 unset($ this ->inProcess [$ aliasNameInClassScope ]);
137131
138132 return $ resolvedAliasType ;
0 commit comments