PluginProbe
WPIDE – File Manager & Code Editor / 3.5.9
WPIDE – File Manager & Code Editor v3.5.9
3.5.9 3.5.8 3.5.7 2.0.14 2.0.15 2.0.16 2.0.2 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 2.1 2.2 2.3 2.3.1 2.3.2 2.4.0 2.5 2.6 3.0 3.1 3.2 3.3 All 55 releases
wpide / vendor / nikic / php-parser / lib / PhpParser / Lexer.php

Lexer.php in WPIDE – File Manager & Code Editor 3.5.9, at vendor/nikic/php-parser/lib/PhpParser/Lexer.php

561 lines 23.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php declare(strict_types=1);
2
3 namespace PhpParser;
4
5 use PhpParser\Parser\Tokens;
6
7 class Lexer
8 {
9 protected $code;
10 protected $tokens;
11 protected $pos;
12 protected $line;
13 protected $filePos;
14 protected $prevCloseTagHasNewline;
15
16 protected $tokenMap;
17 protected $dropTokens;
18 protected $identifierTokens;
19
20 private $attributeStartLineUsed;
21 private $attributeEndLineUsed;
22 private $attributeStartTokenPosUsed;
23 private $attributeEndTokenPosUsed;
24 private $attributeStartFilePosUsed;
25 private $attributeEndFilePosUsed;
26 private $attributeCommentsUsed;
27
28 /**
29 * Creates a Lexer.
30 *
31 * @param array $options Options array. Currently only the 'usedAttributes' option is supported,
32 * which is an array of attributes to add to the AST nodes. Possible
33 * attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos',
34 * 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the
35 * first three. For more info see getNextToken() docs.
36 */
37 public function __construct(array $options = []) {
38 // Create Map from internal tokens to PhpParser tokens.
39 $this->defineCompatibilityTokens();
40 $this->tokenMap = $this->createTokenMap();
41 $this->identifierTokens = $this->createIdentifierTokenMap();
42
43 // map of tokens to drop while lexing (the map is only used for isset lookup,
44 // that's why the value is simply set to 1; the value is never actually used.)
45 $this->dropTokens = array_fill_keys(
46 [\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
47 );
48
49 $defaultAttributes = ['comments', 'startLine', 'endLine'];
50 $usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true);
51
52 // Create individual boolean properties to make these checks faster.
53 $this->attributeStartLineUsed = isset($usedAttributes['startLine']);
54 $this->attributeEndLineUsed = isset($usedAttributes['endLine']);
55 $this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']);
56 $this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']);
57 $this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']);
58 $this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']);
59 $this->attributeCommentsUsed = isset($usedAttributes['comments']);
60 }
61
62 /**
63 * Initializes the lexer for lexing the provided source code.
64 *
65 * This function does not throw if lexing errors occur. Instead, errors may be retrieved using
66 * the getErrors() method.
67 *
68 * @param string $code The source code to lex
69 * @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
70 * ErrorHandler\Throwing
71 */
72 public function startLexing(string $code, ?ErrorHandler $errorHandler = null) {
73 if (null === $errorHandler) {
74 $errorHandler = new ErrorHandler\Throwing();
75 }
76
77 $this->code = $code; // keep the code around for __halt_compiler() handling
78 $this->pos = -1;
79 $this->line = 1;
80 $this->filePos = 0;
81
82 // If inline HTML occurs without preceding code, treat it as if it had a leading newline.
83 // This ensures proper composability, because having a newline is the "safe" assumption.
84 $this->prevCloseTagHasNewline = true;
85
86 $scream = ini_set('xdebug.scream', '0');
87
88 $this->tokens = @token_get_all($code);
89 $this->postprocessTokens($errorHandler);
90
91 if (false !== $scream) {
92 ini_set('xdebug.scream', $scream);
93 }
94 }
95
96 private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
97 $tokens = [];
98 for ($i = $start; $i < $end; $i++) {
99 $chr = $this->code[$i];
100 if ($chr === "\0") {
101 // PHP cuts error message after null byte, so need special case
102 $errorMsg = 'Unexpected null byte';
103 } else {
104 $errorMsg = sprintf(
105 'Unexpected character "%s" (ASCII %d)', $chr, ord($chr)
106 );
107 }
108
109 $tokens[] = [\T_BAD_CHARACTER, $chr, $line];
110 $errorHandler->handleError(new Error($errorMsg, [
111 'startLine' => $line,
112 'endLine' => $line,
113 'startFilePos' => $i,
114 'endFilePos' => $i,
115 ]));
116 }
117 return $tokens;
118 }
119
120 /**
121 * Check whether comment token is unterminated.
122 *
123 * @return bool
124 */
125 private function isUnterminatedComment($token) : bool {
126 return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT)
127 && substr($token[1], 0, 2) === '/*'
128 && substr($token[1], -2) !== '*/';
129 }
130
131 protected function postprocessTokens(ErrorHandler $errorHandler) {
132 // PHP's error handling for token_get_all() is rather bad, so if we want detailed
133 // error information we need to compute it ourselves. Invalid character errors are
134 // detected by finding "gaps" in the token array. Unterminated comments are detected
135 // by checking if a trailing comment has a "*/" at the end.
136 //
137 // Additionally, we perform a number of canonicalizations here:
138 // * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore.
139 // * Use PHP 8.0 T_NAME_* tokens.
140 // * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
141 // T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
142
143 $filePos = 0;
144 $line = 1;
145 $numTokens = \count($this->tokens);
146 for ($i = 0; $i < $numTokens; $i++) {
147 $token = $this->tokens[$i];
148
149 // Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token.
150 // In this case we only need to emit an error.
151 if ($token[0] === \T_BAD_CHARACTER) {
152 $this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
153 }
154
155 if ($token[0] === \T_COMMENT && substr($token[1], 0, 2) !== '/*'
156 && preg_match('/(\r\n|\n|\r)$/D', $token[1], $matches)) {
157 $trailingNewline = $matches[0];
158 $token[1] = substr($token[1], 0, -strlen($trailingNewline));
159 $this->tokens[$i] = $token;
160 if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) {
161 // Move trailing newline into following T_WHITESPACE token, if it already exists.
162 $this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1];
163 $this->tokens[$i + 1][2]--;
164 } else {
165 // Otherwise, we need to create a new T_WHITESPACE token.
166 array_splice($this->tokens, $i + 1, 0, [
167 [\T_WHITESPACE, $trailingNewline, $line],
168 ]);
169 $numTokens++;
170 }
171 }
172
173 // Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING
174 // into a single token.
175 if (\is_array($token)
176 && ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) {
177 $lastWasSeparator = $token[0] === \T_NS_SEPARATOR;
178 $text = $token[1];
179 for ($j = $i + 1; isset($this->tokens[$j]); $j++) {
180 if ($lastWasSeparator) {
181 if (!isset($this->identifierTokens[$this->tokens[$j][0]])) {
182 break;
183 }
184 $lastWasSeparator = false;
185 } else {
186 if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) {
187 break;
188 }
189 $lastWasSeparator = true;
190 }
191 $text .= $this->tokens[$j][1];
192 }
193 if ($lastWasSeparator) {
194 // Trailing separator is not part of the name.
195 $j--;
196 $text = substr($text, 0, -1);
197 }
198 if ($j > $i + 1) {
199 if ($token[0] === \T_NS_SEPARATOR) {
200 $type = \T_NAME_FULLY_QUALIFIED;
201 } else if ($token[0] === \T_NAMESPACE) {
202 $type = \T_NAME_RELATIVE;
203 } else {
204 $type = \T_NAME_QUALIFIED;
205 }
206 $token = [$type, $text, $line];
207 array_splice($this->tokens, $i, $j - $i, [$token]);
208 $numTokens -= $j - $i - 1;
209 }
210 }
211
212 if ($token === '&') {
213 $next = $i + 1;
214 while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) {
215 $next++;
216 }
217 $followedByVarOrVarArg = isset($this->tokens[$next]) &&
218 ($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS);
219 $this->tokens[$i] = $token = [
220 $followedByVarOrVarArg
221 ? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
222 : \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
223 '&',
224 $line,
225 ];
226 }
227
228 $tokenValue = \is_string($token) ? $token : $token[1];
229 $tokenLen = \strlen($tokenValue);
230
231 if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
232 // Something is missing, must be an invalid character
233 $nextFilePos = strpos($this->code, $tokenValue, $filePos);
234 $badCharTokens = $this->handleInvalidCharacterRange(
235 $filePos, $nextFilePos, $line, $errorHandler);
236 $filePos = (int) $nextFilePos;
237
238 array_splice($this->tokens, $i, 0, $badCharTokens);
239 $numTokens += \count($badCharTokens);
240 $i += \count($badCharTokens);
241 }
242
243 $filePos += $tokenLen;
244 $line += substr_count($tokenValue, "\n");
245 }
246
247 if ($filePos !== \strlen($this->code)) {
248 if (substr($this->code, $filePos, 2) === '/*') {
249 // Unlike PHP, HHVM will drop unterminated comments entirely
250 $comment = substr($this->code, $filePos);
251 $errorHandler->handleError(new Error('Unterminated comment', [
252 'startLine' => $line,
253 'endLine' => $line + substr_count($comment, "\n"),
254 'startFilePos' => $filePos,
255 'endFilePos' => $filePos + \strlen($comment),
256 ]));
257
258 // Emulate the PHP behavior
259 $isDocComment = isset($comment[3]) && $comment[3] === '*';
260 $this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
261 } else {
262 // Invalid characters at the end of the input
263 $badCharTokens = $this->handleInvalidCharacterRange(
264 $filePos, \strlen($this->code), $line, $errorHandler);
265 $this->tokens = array_merge($this->tokens, $badCharTokens);
266 }
267 return;
268 }
269
270 if (count($this->tokens) > 0) {
271 // Check for unterminated comment
272 $lastToken = $this->tokens[count($this->tokens) - 1];
273 if ($this->isUnterminatedComment($lastToken)) {
274 $errorHandler->handleError(new Error('Unterminated comment', [
275 'startLine' => $line - substr_count($lastToken[1], "\n"),
276 'endLine' => $line,
277 'startFilePos' => $filePos - \strlen($lastToken[1]),
278 'endFilePos' => $filePos,
279 ]));
280 }
281 }
282 }
283
284 /**
285 * Fetches the next token.
286 *
287 * The available attributes are determined by the 'usedAttributes' option, which can
288 * be specified in the constructor. The following attributes are supported:
289 *
290 * * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances,
291 * representing all comments that occurred between the previous
292 * non-discarded token and the current one.
293 * * 'startLine' => Line in which the node starts.
294 * * 'endLine' => Line in which the node ends.
295 * * 'startTokenPos' => Offset into the token array of the first token in the node.
296 * * 'endTokenPos' => Offset into the token array of the last token in the node.
297 * * 'startFilePos' => Offset into the code string of the first character that is part of the node.
298 * * 'endFilePos' => Offset into the code string of the last character that is part of the node.
299 *
300 * @param mixed $value Variable to store token content in
301 * @param mixed $startAttributes Variable to store start attributes in
302 * @param mixed $endAttributes Variable to store end attributes in
303 *
304 * @return int Token id
305 */
306 public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int {
307 $startAttributes = [];
308 $endAttributes = [];
309
310 while (1) {
311 if (isset($this->tokens[++$this->pos])) {
312 $token = $this->tokens[$this->pos];
313 } else {
314 // EOF token with ID 0
315 $token = "\0";
316 }
317
318 if ($this->attributeStartLineUsed) {
319 $startAttributes['startLine'] = $this->line;
320 }
321 if ($this->attributeStartTokenPosUsed) {
322 $startAttributes['startTokenPos'] = $this->pos;
323 }
324 if ($this->attributeStartFilePosUsed) {
325 $startAttributes['startFilePos'] = $this->filePos;
326 }
327
328 if (\is_string($token)) {
329 $value = $token;
330 if (isset($token[1])) {
331 // bug in token_get_all
332 $this->filePos += 2;
333 $id = ord('"');
334 } else {
335 $this->filePos += 1;
336 $id = ord($token);
337 }
338 } elseif (!isset($this->dropTokens[$token[0]])) {
339 $value = $token[1];
340 $id = $this->tokenMap[$token[0]];
341 if (\T_CLOSE_TAG === $token[0]) {
342 $this->prevCloseTagHasNewline = false !== strpos($token[1], "\n")
343 || false !== strpos($token[1], "\r");
344 } elseif (\T_INLINE_HTML === $token[0]) {
345 $startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
346 }
347
348 $this->line += substr_count($value, "\n");
349 $this->filePos += \strlen($value);
350 } else {
351 $origLine = $this->line;
352 $origFilePos = $this->filePos;
353 $this->line += substr_count($token[1], "\n");
354 $this->filePos += \strlen($token[1]);
355
356 if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
357 if ($this->attributeCommentsUsed) {
358 $comment = \T_DOC_COMMENT === $token[0]
359 ? new Comment\Doc($token[1],
360 $origLine, $origFilePos, $this->pos,
361 $this->line, $this->filePos - 1, $this->pos)
362 : new Comment($token[1],
363 $origLine, $origFilePos, $this->pos,
364 $this->line, $this->filePos - 1, $this->pos);
365 $startAttributes['comments'][] = $comment;
366 }
367 }
368 continue;
369 }
370
371 if ($this->attributeEndLineUsed) {
372 $endAttributes['endLine'] = $this->line;
373 }
374 if ($this->attributeEndTokenPosUsed) {
375 $endAttributes['endTokenPos'] = $this->pos;
376 }
377 if ($this->attributeEndFilePosUsed) {
378 $endAttributes['endFilePos'] = $this->filePos - 1;
379 }
380
381 return $id;
382 }
383
384 throw new \RuntimeException('Reached end of lexer loop');
385 }
386
387 /**
388 * Returns the token array for current code.
389 *
390 * The token array is in the same format as provided by the
391 * token_get_all() function and does not discard tokens (i.e.
392 * whitespace and comments are included). The token position
393 * attributes are against this token array.
394 *
395 * @return array Array of tokens in token_get_all() format
396 */
397 public function getTokens() : array {
398 return $this->tokens;
399 }
400
401 /**
402 * Handles __halt_compiler() by returning the text after it.
403 *
404 * @return string Remaining text
405 */
406 public function handleHaltCompiler() : string {
407 // text after T_HALT_COMPILER, still including ();
408 $textAfter = substr($this->code, $this->filePos);
409
410 // ensure that it is followed by ();
411 // this simplifies the situation, by not allowing any comments
412 // in between of the tokens.
413 if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
414 throw new Error('__HALT_COMPILER must be followed by "();"');
415 }
416
417 // prevent the lexer from returning any further tokens
418 $this->pos = count($this->tokens);
419
420 // return with (); removed
421 return substr($textAfter, strlen($matches[0]));
422 }
423
424 private function defineCompatibilityTokens() {
425 static $compatTokensDefined = false;
426 if ($compatTokensDefined) {
427 return;
428 }
429
430 $compatTokens = [
431 // PHP 7.4
432 'T_BAD_CHARACTER',
433 'T_FN',
434 'T_COALESCE_EQUAL',
435 // PHP 8.0
436 'T_NAME_QUALIFIED',
437 'T_NAME_FULLY_QUALIFIED',
438 'T_NAME_RELATIVE',
439 'T_MATCH',
440 'T_NULLSAFE_OBJECT_OPERATOR',
441 'T_ATTRIBUTE',
442 // PHP 8.1
443 'T_ENUM',
444 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
445 'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
446 'T_READONLY',
447 ];
448
449 // PHP-Parser might be used together with another library that also emulates some or all
450 // of these tokens. Perform a sanity-check that all already defined tokens have been
451 // assigned a unique ID.
452 $usedTokenIds = [];
453 foreach ($compatTokens as $token) {
454 if (\defined($token)) {
455 $tokenId = \constant($token);
456 $clashingToken = $usedTokenIds[$tokenId] ?? null;
457 if ($clashingToken !== null) {
458 throw new \Error(sprintf(
459 'Token %s has same ID as token %s, ' .
460 'you may be using a library with broken token emulation',
461 $token, $clashingToken
462 ));
463 }
464 $usedTokenIds[$tokenId] = $token;
465 }
466 }
467
468 // Now define any tokens that have not yet been emulated. Try to assign IDs from -1
469 // downwards, but skip any IDs that may already be in use.
470 $newTokenId = -1;
471 foreach ($compatTokens as $token) {
472 if (!\defined($token)) {
473 while (isset($usedTokenIds[$newTokenId])) {
474 $newTokenId--;
475 }
476 \define($token, $newTokenId);
477 $newTokenId--;
478 }
479 }
480
481 $compatTokensDefined = true;
482 }
483
484 /**
485 * Creates the token map.
486 *
487 * The token map maps the PHP internal token identifiers
488 * to the identifiers used by the Parser. Additionally it
489 * maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
490 *
491 * @return array The token map
492 */
493 protected function createTokenMap() : array {
494 $tokenMap = [];
495
496 // 256 is the minimum possible token number, as everything below
497 // it is an ASCII value
498 for ($i = 256; $i < 1000; ++$i) {
499 if (\T_DOUBLE_COLON === $i) {
500 // T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
501 $tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
502 } elseif(\T_OPEN_TAG_WITH_ECHO === $i) {
503 // T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
504 $tokenMap[$i] = Tokens::T_ECHO;
505 } elseif(\T_CLOSE_TAG === $i) {
506 // T_CLOSE_TAG is equivalent to ';'
507 $tokenMap[$i] = ord(';');
508 } elseif ('UNKNOWN' !== $name = token_name($i)) {
509 if ('T_HASHBANG' === $name) {
510 // HHVM uses a special token for #! hashbang lines
511 $tokenMap[$i] = Tokens::T_INLINE_HTML;
512 } elseif (defined($name = Tokens::class . '::' . $name)) {
513 // Other tokens can be mapped directly
514 $tokenMap[$i] = constant($name);
515 }
516 }
517 }
518
519 // HHVM uses a special token for numbers that overflow to double
520 if (defined('T_ONUMBER')) {
521 $tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER;
522 }
523 // HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
524 if (defined('T_COMPILER_HALT_OFFSET')) {
525 $tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
526 }
527
528 // Assign tokens for which we define compatibility constants, as token_name() does not know them.
529 $tokenMap[\T_FN] = Tokens::T_FN;
530 $tokenMap[\T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
531 $tokenMap[\T_NAME_QUALIFIED] = Tokens::T_NAME_QUALIFIED;
532 $tokenMap[\T_NAME_FULLY_QUALIFIED] = Tokens::T_NAME_FULLY_QUALIFIED;
533 $tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE;
534 $tokenMap[\T_MATCH] = Tokens::T_MATCH;
535 $tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
536 $tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
537 $tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
538 $tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
539 $tokenMap[\T_ENUM] = Tokens::T_ENUM;
540 $tokenMap[\T_READONLY] = Tokens::T_READONLY;
541
542 return $tokenMap;
543 }
544
545 private function createIdentifierTokenMap(): array {
546 // Based on semi_reserved production.
547 return array_fill_keys([
548 \T_STRING,
549 \T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
550 \T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
551 \T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
552 \T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
553 \T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
554 \T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
555 \T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
556 \T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
557 \T_MATCH,
558 ], true);
559 }
560 }
561