@@ -34,14 +34,25 @@ class Lexer
34
34
* first three. For more info see getNextToken() docs.
35
35
*/
36
36
public function __construct (array $ options = []) {
37
- // map from internal tokens to PhpParser tokens
38
- $ this ->tokenMap = $ this ->createTokenMap ();
39
-
40
- // Compatibility define for PHP < 7.4
37
+ // Compatibility define for PHP < 7.4.
41
38
if (!defined ('T_BAD_CHARACTER ' )) {
42
39
\define ('T_BAD_CHARACTER ' , -1 );
43
40
}
44
41
42
+ // Compatibility defines for PHP < 8.0.
43
+ if (!defined ('T_NAME_QUALIFIED ' )) {
44
+ \define ('T_NAME_QUALIFIED ' , -2 );
45
+ }
46
+ if (!defined ('T_NAME_FULLY_QUALIFIED ' )) {
47
+ \define ('T_NAME_FULLY_QUALIFIED ' , -3 );
48
+ }
49
+ if (!defined ('T_NAME_RELATIVE ' )) {
50
+ \define ('T_NAME_RELATIVE ' , -4 );
51
+ }
52
+
53
+ // Create Map from internal tokens to PhpParser tokens.
54
+ $ this ->tokenMap = $ this ->createTokenMap ();
55
+
45
56
// map of tokens to drop while lexing (the map is only used for isset lookup,
46
57
// that's why the value is simply set to 1; the value is never actually used.)
47
58
$ this ->dropTokens = array_fill_keys (
@@ -138,7 +149,9 @@ protected function postprocessTokens(ErrorHandler $errorHandler) {
138
149
// by checking if a trailing comment has a "*/" at the end.
139
150
//
140
151
// Additionally, we canonicalize to the PHP 8 comment format here, which does not include
141
- // the trailing whitespace anymore
152
+ // the trailing whitespace anymore.
153
+ //
154
+ // We also canonicalize to the PHP 8 T_NAME_* tokens.
142
155
143
156
$ filePos = 0 ;
144
157
$ line = 1 ;
@@ -170,6 +183,46 @@ protected function postprocessTokens(ErrorHandler $errorHandler) {
170
183
}
171
184
}
172
185
186
+ // Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING
187
+ // into a single token.
188
+ // TODO: Also handle reserved keywords in namespaced names.
189
+ if (\is_array ($ token )
190
+ && ($ token [0 ] === \T_NS_SEPARATOR || $ token [0 ] === \T_STRING || $ token [0 ] === \T_NAMESPACE )) {
191
+ $ lastWasSeparator = $ token [0 ] === \T_NS_SEPARATOR ;
192
+ $ text = $ token [1 ];
193
+ for ($ j = $ i + 1 ; isset ($ this ->tokens [$ j ]); $ j ++) {
194
+ if ($ lastWasSeparator ) {
195
+ if ($ this ->tokens [$ j ][0 ] !== \T_STRING ) {
196
+ break ;
197
+ }
198
+ $ lastWasSeparator = false ;
199
+ } else {
200
+ if ($ this ->tokens [$ j ][0 ] !== \T_NS_SEPARATOR ) {
201
+ break ;
202
+ }
203
+ $ lastWasSeparator = true ;
204
+ }
205
+ $ text .= $ this ->tokens [$ j ][1 ];
206
+ }
207
+ if ($ lastWasSeparator ) {
208
+ // Trailing separator is not part of the name.
209
+ $ j --;
210
+ $ text = substr ($ text , 0 , -1 );
211
+ }
212
+ if ($ j > $ i + 1 ) {
213
+ if ($ token [0 ] === \T_NS_SEPARATOR ) {
214
+ $ type = \T_NAME_FULLY_QUALIFIED ;
215
+ } else if ($ token [0 ] === \T_NAMESPACE ) {
216
+ $ type = \T_NAME_RELATIVE ;
217
+ } else {
218
+ $ type = \T_NAME_QUALIFIED ;
219
+ }
220
+ $ token = [$ type , $ text , $ line ];
221
+ array_splice ($ this ->tokens , $ i , $ j - $ i , [$ token ]);
222
+ $ numTokens -= $ j - $ i - 1 ;
223
+ }
224
+ }
225
+
173
226
$ tokenValue = \is_string ($ token ) ? $ token : $ token [1 ];
174
227
$ tokenLen = \strlen ($ tokenValue );
175
228
@@ -409,6 +462,11 @@ protected function createTokenMap() : array {
409
462
$ tokenMap [\T_COMPILER_HALT_OFFSET ] = Tokens::T_STRING ;
410
463
}
411
464
465
+ // Assign tokens for which we define compatibility constants, as token_name() does not know them.
466
+ $ tokenMap [\T_NAME_QUALIFIED ] = Tokens::T_NAME_QUALIFIED ;
467
+ $ tokenMap [\T_NAME_FULLY_QUALIFIED ] = Tokens::T_NAME_FULLY_QUALIFIED ;
468
+ $ tokenMap [\T_NAME_RELATIVE ] = Tokens::T_NAME_RELATIVE ;
469
+
412
470
return $ tokenMap ;
413
471
}
414
472
}
0 commit comments