77use PHPStan \BetterReflection \Identifier \Identifier ;
88use PHPStan \BetterReflection \Identifier \IdentifierType ;
99use PHPStan \BetterReflection \Reflection \Reflection ;
10+ use PHPStan \BetterReflection \Reflection \ReflectionClass ;
11+ use PHPStan \BetterReflection \Reflection \ReflectionConstant ;
12+ use PHPStan \BetterReflection \Reflection \ReflectionEnum ;
13+ use PHPStan \BetterReflection \Reflection \ReflectionFunction ;
1014use PHPStan \BetterReflection \Reflector \Reflector ;
1115use PHPStan \BetterReflection \SourceLocator \Ast \Strategy \NodeToReflection ;
1216use PHPStan \BetterReflection \SourceLocator \Type \SourceLocator ;
17+ use PHPStan \Cache \Cache ;
18+ use PHPStan \File \CouldNotReadFileException ;
1319use PHPStan \Reflection \ConstantNameHelper ;
1420use PHPStan \ShouldNotHappenException ;
1521use function array_key_exists ;
1622use function array_values ;
1723use function current ;
24+ use function hash_file ;
25+ use function sprintf ;
1826use function strtolower ;
1927
2028final class OptimizedDirectorySourceLocator implements SourceLocator
@@ -27,80 +35,148 @@ final class OptimizedDirectorySourceLocator implements SourceLocator
2735 */
2836 public function __construct (
2937 private FileNodesFetcher $ fileNodesFetcher ,
38+ private Cache $ cache ,
3039 private array $ classToFile ,
3140 private array $ functionToFiles ,
3241 private array $ constantToFile ,
3342 )
3443 {
3544 }
3645
46+ /**
47+ * @return array{non-empty-string, string}
48+ */
49+ private function getCacheKeys (string $ file , Identifier $ identifier ): array
50+ {
51+ $ fileHash = hash_file ('sha256 ' , $ file );
52+ if ($ fileHash === false ) {
53+ throw new CouldNotReadFileException ($ file );
54+ }
55+
56+ $ reflectionCacheKey = sprintf ('odsl-%s-%s-%s ' , $ file , $ identifier ->getType ()->getName (), $ identifier ->getName ());
57+ $ variableCacheKey = sprintf ('v1-%s ' , $ fileHash );
58+
59+ return [$ reflectionCacheKey , $ variableCacheKey ];
60+ }
61+
3762 #[Override]
3863 public function locateIdentifier (Reflector $ reflector , Identifier $ identifier ): ?Reflection
3964 {
4065 if ($ identifier ->isClass ()) {
41- $ className = strtolower ($ identifier ->getName ());
42- $ file = $ this ->findFileByClass ($ className );
66+ $ identifierName = strtolower ($ identifier ->getName ());
67+ $ file = $ this ->findFileByClass ($ identifierName );
4368 if ($ file === null ) {
4469 return null ;
4570 }
71+ $ files = [$ file ];
72+ } elseif ($ identifier ->isFunction ()) {
73+ $ identifierName = strtolower ($ identifier ->getName ());
74+ $ files = $ this ->findFilesByFunction ($ identifierName );
75+ } elseif ($ identifier ->isConstant ()) {
76+ $ identifierName = ConstantNameHelper::normalize ($ identifier ->getName ());
77+ $ file = $ this ->findFileByConstant ($ identifierName );
4678
47- $ fetchedClassNodes = $ this ->fileNodesFetcher ->fetchNodes ($ file )->getClassNodes ();
48-
49- if (!array_key_exists ($ className , $ fetchedClassNodes )) {
79+ if ($ file === null ) {
5080 return null ;
5181 }
5282
53- /** @var FetchedNode<Node\Stmt\ClassLike> $fetchedClassNode */
54- $ fetchedClassNode = current ($ fetchedClassNodes [$ className ]);
83+ $ files = [$ file ];
84+ } else {
85+ return null ;
86+ }
5587
56- return $ this ->nodeToReflection ($ reflector , $ fetchedClassNode );
88+ foreach ($ files as $ file ) {
89+ [$ reflectionCacheKey , $ variableCacheKey ] = $ this ->getCacheKeys ($ file , $ identifier );
90+ $ cachedReflection = $ this ->cache ->load ($ reflectionCacheKey , $ variableCacheKey );
91+ if ($ cachedReflection === null ) {
92+ continue ;
93+ }
94+
95+ if ($ identifier ->isConstant ()) {
96+ return ReflectionConstant::importFromCache ($ reflector , $ cachedReflection );
97+ }
98+ if ($ identifier ->isFunction ()) {
99+ return ReflectionFunction::importFromCache ($ reflector , $ cachedReflection );
100+ }
101+ if ($ identifier ->isClass ()) {
102+ if (array_key_exists ('backingType ' , $ cachedReflection )) {
103+ return ReflectionEnum::importFromCache ($ reflector , $ cachedReflection );
104+ }
105+
106+ return ReflectionClass::importFromCache ($ reflector , $ cachedReflection );
107+ }
57108 }
58109
59- if ($ identifier ->isFunction ()) {
60- $ functionName = strtolower ($ identifier ->getName ());
61- $ files = $ this ->findFilesByFunction ($ functionName );
110+ if ($ identifier ->isClass ()) {
111+ $ fetchedClassNode = null ;
112+ foreach ($ files as $ file ) {
113+ $ fetchedClassNodes = $ this ->fileNodesFetcher ->fetchNodes ($ file )->getClassNodes ();
114+
115+ if (!array_key_exists ($ identifierName , $ fetchedClassNodes )) {
116+ return null ;
117+ }
118+
119+ /** @var FetchedNode<Node\Stmt\ClassLike> $fetchedClassNode */
120+ $ fetchedClassNode = current ($ fetchedClassNodes [$ identifierName ]);
121+ }
122+
123+ if ($ fetchedClassNode === null ) {
124+ return null ;
125+ }
126+
127+ [$ reflectionCacheKey , $ variableCacheKey ] = $ this ->getCacheKeys ($ file , $ identifier ); // @phpstan-ignore variable.undefined
128+ $ classReflection = $ this ->nodeToReflection ($ reflector , $ fetchedClassNode );
129+ $ this ->cache ->save ($ reflectionCacheKey , $ variableCacheKey , $ classReflection ->exportToCache ());
62130
131+ return $ classReflection ;
132+ } elseif ($ identifier ->isFunction ()) {
63133 $ fetchedFunctionNode = null ;
64134 foreach ($ files as $ file ) {
65135 $ fetchedFunctionNodes = $ this ->fileNodesFetcher ->fetchNodes ($ file )->getFunctionNodes ();
66136
67- if (!array_key_exists ($ functionName , $ fetchedFunctionNodes )) {
137+ if (!array_key_exists ($ identifierName , $ fetchedFunctionNodes )) {
68138 continue ;
69139 }
70140
71141 /** @var FetchedNode<Node\Stmt\Function_> $fetchedFunctionNode */
72- $ fetchedFunctionNode = current ($ fetchedFunctionNodes [$ functionName ]);
142+ $ fetchedFunctionNode = current ($ fetchedFunctionNodes [$ identifierName ]);
73143 }
74144
75145 if ($ fetchedFunctionNode === null ) {
76146 return null ;
77147 }
78148
79- return $ this ->nodeToReflection ($ reflector , $ fetchedFunctionNode );
80- }
149+ [$ reflectionCacheKey , $ variableCacheKey ] = $ this ->getCacheKeys ($ file , $ identifier ); // @phpstan-ignore variable.undefined
150+ $ functionReflection = $ this ->nodeToReflection ($ reflector , $ fetchedFunctionNode );
151+ $ this ->cache ->save ($ reflectionCacheKey , $ variableCacheKey , $ functionReflection ->exportToCache ());
81152
82- if ($ identifier ->isConstant ()) {
83- $ constantName = ConstantNameHelper::normalize ($ identifier ->getName ());
84- $ file = $ this ->findFileByConstant ($ constantName );
153+ return $ functionReflection ;
154+ } elseif ($ identifier ->isConstant ()) {
155+ $ fetchedConstantNode = null ;
156+ foreach ($ files as $ file ) {
157+ $ fetchedConstantNodes = $ this ->fileNodesFetcher ->fetchNodes ($ file )->getConstantNodes ();
85158
86- if ($ file === null ) {
87- return null ;
88- }
159+ if (! array_key_exists ( $ identifierName , $ fetchedConstantNodes ) ) {
160+ return null ;
161+ }
89162
90- $ fetchedConstantNodes = $ this ->fileNodesFetcher ->fetchNodes ($ file )->getConstantNodes ();
163+ /** @var FetchedNode<Node\Stmt\Const_|Node\Expr\FuncCall> $fetchedConstantNode */
164+ $ fetchedConstantNode = current ($ fetchedConstantNodes [$ identifierName ]);
165+ }
91166
92- if (! array_key_exists ( $ constantName , $ fetchedConstantNodes ) ) {
167+ if ($ fetchedConstantNode === null ) {
93168 return null ;
94169 }
95170
96- /** @var FetchedNode<Node\Stmt\Const_|Node\Expr\FuncCall> $fetchedConstantNode */
97- $ fetchedConstantNode = current ($ fetchedConstantNodes [$ constantName ]);
98-
99- return $ this ->nodeToReflection (
171+ [$ reflectionCacheKey , $ variableCacheKey ] = $ this ->getCacheKeys ($ file , $ identifier ); // @phpstan-ignore variable.undefined
172+ $ constantReflection = $ this ->nodeToReflection (
100173 $ reflector ,
101174 $ fetchedConstantNode ,
102- $ this ->findConstantPositionInConstNode ($ fetchedConstantNode ->getNode (), $ constantName ),
175+ $ this ->findConstantPositionInConstNode ($ fetchedConstantNode ->getNode (), $ identifierName ),
103176 );
177+ $ this ->cache ->save ($ reflectionCacheKey , $ variableCacheKey , $ constantReflection ->exportToCache ());
178+
179+ return $ constantReflection ;
104180 }
105181
106182 return null ;
@@ -109,7 +185,7 @@ public function locateIdentifier(Reflector $reflector, Identifier $identifier):
109185 /**
110186 * @param FetchedNode<Node\Stmt\ClassLike>|FetchedNode<Node\Stmt\Function_>|FetchedNode<Node\Stmt\Const_|Node\Expr\FuncCall> $fetchedNode
111187 */
112- private function nodeToReflection (Reflector $ reflector , FetchedNode $ fetchedNode , ?int $ positionInNode = null ): Reflection
188+ private function nodeToReflection (Reflector $ reflector , FetchedNode $ fetchedNode , ?int $ positionInNode = null ): ReflectionClass | ReflectionConstant | ReflectionFunction
113189 {
114190 $ nodeToReflection = new NodeToReflection ();
115191 return $ nodeToReflection ->__invoke (
0 commit comments