9
9
use PHPStan \BetterReflection \Reflector \Reflector ;
10
10
use PHPStan \BetterReflection \SourceLocator \Ast \Strategy \NodeToReflection ;
11
11
use PHPStan \BetterReflection \SourceLocator \Type \SourceLocator ;
12
- use PHPStan \Php \PhpVersion ;
13
12
use PHPStan \Reflection \ConstantNameHelper ;
14
13
use PHPStan \ShouldNotHappenException ;
15
14
use function array_key_exists ;
16
15
use function array_values ;
17
- use function count ;
18
16
use function current ;
19
- use function ltrim ;
20
- use function php_strip_whitespace ;
21
- use function preg_match_all ;
22
- use function preg_replace ;
23
- use function sprintf ;
24
17
use function strtolower ;
25
18
26
19
class OptimizedDirectorySourceLocator implements SourceLocator
27
20
{
28
21
29
- private PhpFileCleaner $ cleaner ;
30
-
31
- private string $ extraTypes ;
32
-
33
- /** @var array<string, string>|null */
34
- private ?array $ classToFile = null ;
35
-
36
- /** @var array<string, string>|null */
37
- private ?array $ constantToFile = null ;
38
-
39
- /** @var array<string, array<int, string>>|null */
40
- private ?array $ functionToFiles = null ;
41
-
42
22
/**
43
- * @param string[] $files
23
+ * @param array<string, string> $classToFile
24
+ * @param array<string, array<int, string>> $functionToFiles
25
+ * @param array<string, string> $constantToFile
44
26
*/
45
27
public function __construct (
46
28
private FileNodesFetcher $ fileNodesFetcher ,
47
- private PhpVersion $ phpVersion ,
48
- private array $ files ,
29
+ private array $ classToFile ,
30
+ private array $ functionToFiles ,
31
+ private array $ constantToFile ,
49
32
)
50
33
{
51
- $ this ->extraTypes = $ this ->phpVersion ->supportsEnums () ? '|enum ' : '' ;
52
-
53
- $ this ->cleaner = new PhpFileCleaner ();
54
34
}
55
35
56
36
public function locateIdentifier (Reflector $ reflector , Identifier $ identifier ): ?Reflection
@@ -141,13 +121,6 @@ private function nodeToReflection(Reflector $reflector, FetchedNode $fetchedNode
141
121
142
122
private function findFileByClass (string $ className ): ?string
143
123
{
144
- if ($ this ->classToFile === null ) {
145
- $ this ->init ();
146
- if ($ this ->classToFile === null ) {
147
- throw new ShouldNotHappenException ();
148
- }
149
- }
150
-
151
124
if (!array_key_exists ($ className , $ this ->classToFile )) {
152
125
return null ;
153
126
}
@@ -157,13 +130,6 @@ private function findFileByClass(string $className): ?string
157
130
158
131
private function findFileByConstant (string $ constantName ): ?string
159
132
{
160
- if ($ this ->constantToFile === null ) {
161
- $ this ->init ();
162
- if ($ this ->constantToFile === null ) {
163
- throw new ShouldNotHappenException ();
164
- }
165
- }
166
-
167
133
if (!array_key_exists ($ constantName , $ this ->constantToFile )) {
168
134
return null ;
169
135
}
@@ -176,132 +142,18 @@ private function findFileByConstant(string $constantName): ?string
176
142
*/
177
143
private function findFilesByFunction (string $ functionName ): array
178
144
{
179
- if ($ this ->functionToFiles === null ) {
180
- $ this ->init ();
181
- if ($ this ->functionToFiles === null ) {
182
- throw new ShouldNotHappenException ();
183
- }
184
- }
185
-
186
145
if (!array_key_exists ($ functionName , $ this ->functionToFiles )) {
187
146
return [];
188
147
}
189
148
190
149
return $ this ->functionToFiles [$ functionName ];
191
150
}
192
151
193
- private function init (): void
194
- {
195
- $ classToFile = [];
196
- $ constantToFile = [];
197
- $ functionToFiles = [];
198
- foreach ($ this ->files as $ file ) {
199
- $ symbols = $ this ->findSymbols ($ file );
200
- foreach ($ symbols ['classes ' ] as $ classInFile ) {
201
- $ classToFile [$ classInFile ] = $ file ;
202
- }
203
- foreach ($ symbols ['constants ' ] as $ constantInFile ) {
204
- $ constantToFile [$ constantInFile ] = $ file ;
205
- }
206
- foreach ($ symbols ['functions ' ] as $ functionInFile ) {
207
- if (!array_key_exists ($ functionInFile , $ functionToFiles )) {
208
- $ functionToFiles [$ functionInFile ] = [];
209
- }
210
- $ functionToFiles [$ functionInFile ][] = $ file ;
211
- }
212
- }
213
-
214
- $ this ->classToFile = $ classToFile ;
215
- $ this ->functionToFiles = $ functionToFiles ;
216
- $ this ->constantToFile = $ constantToFile ;
217
- }
218
-
219
- /**
220
- * Inspired by Composer\Autoload\ClassMapGenerator::findClasses()
221
- * @link https://github.com/composer/composer/blob/45d3e133a4691eccb12e9cd6f9dfd76eddc1906d/src/Composer/Autoload/ClassMapGenerator.php#L216
222
- *
223
- * @return array{classes: string[], functions: string[], constants: string[]}
224
- */
225
- private function findSymbols (string $ file ): array
226
- {
227
- $ contents = @php_strip_whitespace ($ file );
228
- if ($ contents === '' ) {
229
- return ['classes ' => [], 'functions ' => [], 'constants ' => []];
230
- }
231
-
232
- $ matchResults = (bool ) preg_match_all (sprintf ('{\b(?:(?:class|interface|trait|const|function%s)\s)|(?:define\s*\()}i ' , $ this ->extraTypes ), $ contents , $ matches );
233
- if (!$ matchResults ) {
234
- return ['classes ' => [], 'functions ' => [], 'constants ' => []];
235
- }
236
-
237
- $ contents = $ this ->cleaner ->clean ($ contents , count ($ matches [0 ]));
238
-
239
- preg_match_all (sprintf ('{
240
- (?:
241
- \b(?<![\$:>])(?:
242
- (?: (?P<type>class|interface|trait%s) \s++ (?P<name>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\-]*+) )
243
- | (?: (?P<function>function) \s++ (?:&\s*)? (?P<fname>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\-]*+) \s*+ [&\(] )
244
- | (?: (?P<constant>const) \s++ (?P<cname>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff\-]*+) \s*+ [^;] )
245
- | (?: (?: \\\)? (?P<define>define) \s*+ \( \s*+ [ \'"] (?P<dname>[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:[ \\\\]{1,2}[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+) )
246
- | (?: (?P<ns>namespace) (?P<nsname>\s++[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\s*+ \\\\\s*+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)*+)? \s*+ [\{;] )
247
- )
248
- )
249
- }ix ' , $ this ->extraTypes ), $ contents , $ matches );
250
-
251
- $ classes = [];
252
- $ functions = [];
253
- $ constants = [];
254
- $ namespace = '' ;
255
-
256
- for ($ i = 0 , $ len = count ($ matches ['type ' ]); $ i < $ len ; $ i ++) {
257
- if (isset ($ matches ['ns ' ][$ i ]) && $ matches ['ns ' ][$ i ] !== '' ) {
258
- $ namespace = preg_replace ('~\s+~ ' , '' , strtolower ($ matches ['nsname ' ][$ i ])) . '\\' ;
259
- continue ;
260
- }
261
-
262
- if ($ matches ['function ' ][$ i ] !== '' ) {
263
- $ functions [] = strtolower (ltrim ($ namespace . $ matches ['fname ' ][$ i ], '\\' ));
264
- continue ;
265
- }
266
-
267
- if ($ matches ['constant ' ][$ i ] !== '' ) {
268
- $ constants [] = ConstantNameHelper::normalize (ltrim ($ namespace . $ matches ['cname ' ][$ i ], '\\' ));
269
- }
270
-
271
- if ($ matches ['define ' ][$ i ] !== '' ) {
272
- $ constants [] = ConstantNameHelper::normalize ($ matches ['dname ' ][$ i ]);
273
- continue ;
274
- }
275
-
276
- $ name = $ matches ['name ' ][$ i ];
277
-
278
- // skip anon classes extending/implementing
279
- if ($ name === 'extends ' || $ name === 'implements ' ) {
280
- continue ;
281
- }
282
-
283
- $ classes [] = strtolower (ltrim ($ namespace . $ name , '\\' ));
284
- }
285
-
286
- return [
287
- 'classes ' => $ classes ,
288
- 'functions ' => $ functions ,
289
- 'constants ' => $ constants ,
290
- ];
291
- }
292
-
293
152
/**
294
153
* @return list<Reflection>
295
154
*/
296
155
public function locateIdentifiersByType (Reflector $ reflector , IdentifierType $ identifierType ): array
297
156
{
298
- if ($ this ->classToFile === null || $ this ->functionToFiles === null || $ this ->constantToFile === null ) {
299
- $ this ->init ();
300
- if ($ this ->classToFile === null || $ this ->functionToFiles === null || $ this ->constantToFile === null ) {
301
- throw new ShouldNotHappenException ();
302
- }
303
- }
304
-
305
157
$ reflections = [];
306
158
if ($ identifierType ->isClass ()) {
307
159
foreach ($ this ->classToFile as $ file ) {
0 commit comments