2
2
3
3
namespace ShipMonk \ComposerDependencyAnalyser ;
4
4
5
- use function array_combine ;
6
5
use function array_fill_keys ;
6
+ use function array_map ;
7
7
use function array_merge ;
8
8
use function count ;
9
9
use function explode ;
10
10
use function is_array ;
11
11
use function ltrim ;
12
12
use function strlen ;
13
13
use function strpos ;
14
+ use function strtolower ;
14
15
use function substr ;
15
16
use function token_get_all ;
16
17
use const PHP_VERSION_ID ;
22
23
use const T_CURLY_OPEN ;
23
24
use const T_DOC_COMMENT ;
24
25
use const T_DOLLAR_OPEN_CURLY_BRACES ;
26
+ use const T_DOUBLE_COLON ;
25
27
use const T_ENUM ;
26
28
use const T_FUNCTION ;
29
+ use const T_INSTEADOF ;
27
30
use const T_INTERFACE ;
28
31
use const T_NAME_FULLY_QUALIFIED ;
29
32
use const T_NAME_QUALIFIED ;
30
33
use const T_NAMESPACE ;
31
34
use const T_NEW ;
32
35
use const T_NS_SEPARATOR ;
36
+ use const T_NULLSAFE_OBJECT_OPERATOR ;
37
+ use const T_OBJECT_OPERATOR ;
33
38
use const T_STRING ;
34
39
use const T_TRAIT ;
35
40
use const T_USE ;
@@ -65,10 +70,9 @@ public function __construct(string $code)
65
70
* It does not produce any local names in current namespace
66
71
* - this results in very limited functionality in files without namespace
67
72
*
68
- * @param array<string> $extClasses
69
- * @param array<string> $extFunctions
70
- * @param array<string> $extConstants
71
- *
73
+ * @param list<string> $extClasses
74
+ * @param list<string> $extFunctions
75
+ * @param list<string> $extConstants
72
76
* @return array<SymbolKind::*, array<string, list<int>>>
73
77
* @license Inspired by https://github.com/doctrine/annotations/blob/2.0.0/lib/Doctrine/Common/Annotations/TokenParser.php
74
78
*/
@@ -79,16 +83,13 @@ public function parseUsedSymbols(
79
83
): array
80
84
{
81
85
$ usedSymbols = [];
82
- $ useStatements = $ initialUseStatements = array_merge (
83
- array_combine ($ extClasses , $ extClasses ),
84
- array_combine ($ extFunctions , $ extFunctions ),
85
- array_combine ($ extConstants , $ extConstants )
86
- );
87
- $ useStatementKinds = $ initialUseStatementKinds = array_merge (
88
- array_fill_keys ($ extClasses , SymbolKind::CLASSLIKE ),
89
- array_fill_keys ($ extFunctions , SymbolKind::FUNCTION ),
90
- array_fill_keys ($ extConstants , SymbolKind::CONSTANT )
86
+ $ extensionSymbols = array_merge (
87
+ array_fill_keys (array_map ('strtolower ' , $ extClasses ), SymbolKind::CLASSLIKE ),
88
+ array_fill_keys (array_map ('strtolower ' , $ extFunctions ), SymbolKind::FUNCTION ),
89
+ array_fill_keys (array_map ('strtolower ' , $ extConstants ), SymbolKind::CONSTANT )
91
90
);
91
+ $ useStatements = [];
92
+ $ useStatementKinds = [];
92
93
93
94
$ level = 0 ; // {, }, {$, ${
94
95
$ squareLevel = 0 ; // [, ], #[
@@ -128,13 +129,14 @@ public function parseUsedSymbols(
128
129
case PHP_VERSION_ID >= 80000 ? T_NAMESPACE : -1 :
129
130
// namespace change
130
131
$ inGlobalScope = false ;
131
- $ useStatements = $ initialUseStatements ;
132
- $ useStatementKinds = $ initialUseStatementKinds ;
132
+ $ useStatements = [] ;
133
+ $ useStatementKinds = [] ;
133
134
break ;
134
135
135
136
case PHP_VERSION_ID >= 80000 ? T_NAME_FULLY_QUALIFIED : -1 :
136
137
$ symbolName = $ this ->normalizeBackslash ($ token [1 ]);
137
- $ kind = $ this ->getFqnSymbolKind ($ this ->pointer - 2 , $ this ->pointer , $ inAttributeSquareLevel !== null );
138
+ $ lowerSymbolName = strtolower ($ symbolName );
139
+ $ kind = $ extensionSymbols [$ lowerSymbolName ] ?? $ this ->getFqnSymbolKind ($ this ->pointer - 2 , $ this ->pointer , $ inAttributeSquareLevel !== null );
138
140
$ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
139
141
break ;
140
142
@@ -143,21 +145,34 @@ public function parseUsedSymbols(
143
145
144
146
if (isset ($ useStatements [$ neededAlias ])) {
145
147
$ symbolName = $ useStatements [$ neededAlias ] . substr ($ token [1 ], strlen ($ neededAlias ));
146
- $ kind = $ this ->getFqnSymbolKind ($ this ->pointer - 2 , $ this ->pointer , $ inAttributeSquareLevel !== null );
147
- $ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
148
-
149
148
} elseif ($ inGlobalScope ) {
150
149
$ symbolName = $ token [1 ];
151
- $ kind = $ this -> getFqnSymbolKind ( $ this -> pointer - 2 , $ this -> pointer , $ inAttributeSquareLevel !== null );
152
- $ usedSymbols [ $ kind ][ $ symbolName ][] = $ token [ 2 ] ;
150
+ } else {
151
+ break ;
153
152
}
154
153
154
+ $ lowerSymbolName = strtolower ($ symbolName );
155
+ $ kind = $ extensionSymbols [$ lowerSymbolName ] ?? $ this ->getFqnSymbolKind ($ this ->pointer - 2 , $ this ->pointer , $ inAttributeSquareLevel !== null );
156
+ $ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
157
+
155
158
break ;
156
159
157
160
case PHP_VERSION_ID >= 80000 ? T_STRING : -1 :
158
161
$ name = $ token [1 ];
162
+ $ lowerName = strtolower ($ name );
163
+ $ pointerBeforeName = $ this ->pointer - 2 ;
164
+ $ pointerAfterName = $ this ->pointer ;
165
+
166
+ if (!$ this ->canBeSymbolName ($ pointerBeforeName , $ pointerAfterName )) {
167
+ break ;
168
+ }
159
169
160
- if (isset ($ useStatements [$ name ])) {
170
+ if (isset ($ extensionSymbols [$ lowerName ])) {
171
+ $ symbolName = $ name ;
172
+ $ kind = $ extensionSymbols [$ lowerName ];
173
+ $ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
174
+
175
+ } elseif (isset ($ useStatements [$ name ])) {
161
176
$ symbolName = $ useStatements [$ name ];
162
177
$ kind = $ useStatementKinds [$ name ];
163
178
$ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
@@ -172,18 +187,19 @@ public function parseUsedSymbols(
172
187
if (substr ($ nextName , 0 , 1 ) !== '\\' ) { // not a namespace-relative name, but a new namespace declaration
173
188
// namespace change
174
189
$ inGlobalScope = false ;
175
- $ useStatements = $ initialUseStatements ;
176
- $ useStatementKinds = $ initialUseStatementKinds ;
190
+ $ useStatements = [] ;
191
+ $ useStatementKinds = [] ;
177
192
}
178
193
179
194
break ;
180
195
181
196
case PHP_VERSION_ID < 80000 ? T_NS_SEPARATOR : -1 :
182
197
$ pointerBeforeName = $ this ->pointer - 2 ;
183
198
$ symbolName = $ this ->normalizeBackslash ($ this ->parseNameForOldPhp ());
199
+ $ lowerSymbolName = strtolower ($ symbolName );
184
200
185
201
if ($ symbolName !== '' ) { // e.g. \array (NS separator followed by not-a-name)
186
- $ kind = $ this ->getFqnSymbolKind ($ pointerBeforeName , $ this ->pointer - 1 , false );
202
+ $ kind = $ extensionSymbols [ $ lowerSymbolName ] ?? $ this ->getFqnSymbolKind ($ pointerBeforeName , $ this ->pointer - 1 , false );
187
203
$ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
188
204
}
189
205
@@ -192,23 +208,34 @@ public function parseUsedSymbols(
192
208
case PHP_VERSION_ID < 80000 ? T_STRING : -1 :
193
209
$ pointerBeforeName = $ this ->pointer - 2 ;
194
210
$ name = $ this ->parseNameForOldPhp ();
211
+ $ lowerName = strtolower ($ name );
212
+ $ pointerAfterName = $ this ->pointer - 1 ;
213
+
214
+ if (!$ this ->canBeSymbolName ($ pointerBeforeName , $ pointerAfterName )) {
215
+ break ;
216
+ }
195
217
196
218
if (isset ($ useStatements [$ name ])) { // unqualified name
197
219
$ symbolName = $ useStatements [$ name ];
198
220
$ kind = $ useStatementKinds [$ name ];
199
221
$ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
200
222
223
+ } elseif (isset ($ extensionSymbols [$ lowerName ])) {
224
+ $ symbolName = $ name ;
225
+ $ kind = $ extensionSymbols [$ lowerName ];
226
+ $ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
227
+
201
228
} else {
202
229
[$ neededAlias ] = explode ('\\' , $ name , 2 );
203
230
204
231
if (isset ($ useStatements [$ neededAlias ])) { // qualified name
205
232
$ symbolName = $ useStatements [$ neededAlias ] . substr ($ name , strlen ($ neededAlias ));
206
- $ kind = $ this ->getFqnSymbolKind ($ pointerBeforeName , $ this -> pointer - 1 , false );
233
+ $ kind = $ this ->getFqnSymbolKind ($ pointerBeforeName , $ pointerAfterName , false );
207
234
$ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
208
235
209
236
} elseif ($ inGlobalScope && strpos ($ name , '\\' ) !== false ) {
210
237
$ symbolName = $ name ;
211
- $ kind = $ this ->getFqnSymbolKind ($ pointerBeforeName , $ this -> pointer - 1 , false );
238
+ $ kind = $ this ->getFqnSymbolKind ($ pointerBeforeName , $ pointerAfterName , false );
212
239
$ usedSymbols [$ kind ][$ symbolName ][] = $ token [2 ];
213
240
}
214
241
}
@@ -369,44 +396,87 @@ private function getFqnSymbolKind(
369
396
return SymbolKind::CLASSLIKE ;
370
397
}
371
398
399
+ $ tokenBeforeName = $ this ->getTokenBefore ($ pointerBeforeName );
400
+ $ tokenAfterName = $ this ->getTokenAfter ($ pointerAfterName );
401
+
402
+ if (
403
+ $ tokenAfterName === '( '
404
+ && $ tokenBeforeName [0 ] !== T_NEW // eliminate new \ClassName(
405
+ ) {
406
+ return SymbolKind::FUNCTION ;
407
+ }
408
+
409
+ return SymbolKind::CLASSLIKE ; // constant may fall here, this is eliminated later
410
+ }
411
+
412
+ private function canBeSymbolName (
413
+ int $ pointerBeforeName ,
414
+ int $ pointerAfterName
415
+ ): bool
416
+ {
417
+ $ tokenBeforeName = $ this ->getTokenBefore ($ pointerBeforeName );
418
+ $ tokenAfterName = $ this ->getTokenAfter ($ pointerAfterName );
419
+
420
+ if (
421
+ $ tokenBeforeName [0 ] === T_DOUBLE_COLON
422
+ || $ tokenBeforeName [0 ] === T_INSTEADOF
423
+ || $ tokenBeforeName [0 ] === T_AS
424
+ || $ tokenBeforeName [0 ] === T_FUNCTION
425
+ || $ tokenBeforeName [0 ] === T_OBJECT_OPERATOR
426
+ || $ tokenBeforeName [0 ] === (PHP_VERSION_ID > 80000 ? T_NULLSAFE_OBJECT_OPERATOR : -1 )
427
+ || $ tokenAfterName [0 ] === T_INSTEADOF
428
+ || $ tokenAfterName [0 ] === T_AS
429
+ ) {
430
+ return false ;
431
+ }
432
+
433
+ return true ;
434
+ }
435
+
436
+ /**
437
+ * @return array{int, string}|string
438
+ */
439
+ private function getTokenBefore (int $ pointer )
440
+ {
372
441
do {
373
- $ tokenBeforeName = $ this ->tokens [$ pointerBeforeName ];
442
+ $ token = $ this ->tokens [$ pointer ];
374
443
375
- if (!is_array ($ tokenBeforeName )) {
444
+ if (!is_array ($ token )) {
376
445
break ;
377
446
}
378
447
379
- if ($ tokenBeforeName [0 ] === T_WHITESPACE || $ tokenBeforeName [0 ] === T_COMMENT || $ tokenBeforeName [0 ] === T_DOC_COMMENT ) {
380
- $ pointerBeforeName --;
448
+ if ($ token [0 ] === T_WHITESPACE || $ token [0 ] === T_COMMENT || $ token [0 ] === T_DOC_COMMENT ) {
449
+ $ pointer --;
381
450
continue ;
382
451
}
383
452
384
453
break ;
385
- } while ($ pointerBeforeName >= 0 );
454
+ } while ($ pointer >= 0 );
386
455
456
+ return $ token ;
457
+ }
458
+
459
+ /**
460
+ * @return array{int, string}|string
461
+ */
462
+ private function getTokenAfter (int $ pointer )
463
+ {
387
464
do {
388
- $ tokenAfterName = $ this ->tokens [$ pointerAfterName ];
465
+ $ token = $ this ->tokens [$ pointer ];
389
466
390
- if (!is_array ($ tokenAfterName )) {
467
+ if (!is_array ($ token )) {
391
468
break ;
392
469
}
393
470
394
- if ($ tokenAfterName [0 ] === T_WHITESPACE || $ tokenAfterName [0 ] === T_COMMENT || $ tokenAfterName [0 ] === T_DOC_COMMENT ) {
395
- $ pointerAfterName ++;
471
+ if ($ token [0 ] === T_WHITESPACE || $ token [0 ] === T_COMMENT || $ token [0 ] === T_DOC_COMMENT ) {
472
+ $ pointer ++;
396
473
continue ;
397
474
}
398
475
399
476
break ;
400
- } while ($ pointerAfterName < $ this ->numTokens );
477
+ } while ($ pointer < $ this ->numTokens );
401
478
402
- if (
403
- $ tokenAfterName === '( '
404
- && $ tokenBeforeName [0 ] !== T_NEW // eliminate new \ClassName(
405
- ) {
406
- return SymbolKind::FUNCTION ;
407
- }
408
-
409
- return SymbolKind::CLASSLIKE ; // constant may fall here, this is eliminated later
479
+ return $ token ;
410
480
}
411
481
412
482
}
0 commit comments