PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.5.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.5.4
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
betterdocs / includes / Dependencies / PhpParser / ParserAbstract.php

ParserAbstract.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.5.4, at includes/Dependencies/PhpParser/ParserAbstract.php

730 lines 29.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\Dependencies\PhpParser;
4
5 /*
6 * This parser is based on a skeleton written by Moriyoshi Koizumi, which in
7 * turn is based on work by Masato Bito.
8 */
9 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Name;
10 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Param;
11 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Scalar\LNumber;
12 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Scalar\String_;
13 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Class_;
14 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\ClassConst;
15 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\ClassMethod;
16 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Interface_;
17 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Namespace_;
18 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\Property;
19 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\TryCatch;
20 use WPDeveloper\BetterDocs\Dependencies\PhpParser\Node\Stmt\UseUse;
21
22 abstract class ParserAbstract implements Parser
23 {
24 const SYMBOL_NONE = -1;
25
26 /*
27 * The following members will be filled with generated parsing data:
28 */
29
30 /** @var int Size of $tokenToSymbol map */
31 protected $tokenToSymbolMapSize;
32 /** @var int Size of $action table */
33 protected $actionTableSize;
34 /** @var int Size of $goto table */
35 protected $gotoTableSize;
36
37 /** @var int Symbol number signifying an invalid token */
38 protected $invalidSymbol;
39 /** @var int Symbol number of error recovery token */
40 protected $errorSymbol;
41 /** @var int Action number signifying default action */
42 protected $defaultAction;
43 /** @var int Rule number signifying that an unexpected token was encountered */
44 protected $unexpectedTokenRule;
45
46 protected $YY2TBLSTATE;
47 protected $YYNLSTATES;
48
49 /** @var array Map of lexer tokens to internal symbols */
50 protected $tokenToSymbol;
51 /** @var array Map of symbols to their names */
52 protected $symbolToName;
53 /** @var array Names of the production rules (only necessary for debugging) */
54 protected $productions;
55
56 /** @var array Map of states to a displacement into the $action table. The corresponding action for this
57 * state/symbol pair is $action[$actionBase[$state] + $symbol]. If $actionBase[$state] is 0, the
58 action is defaulted, i.e. $actionDefault[$state] should be used instead. */
59 protected $actionBase;
60 /** @var array Table of actions. Indexed according to $actionBase comment. */
61 protected $action;
62 /** @var array Table indexed analogously to $action. If $actionCheck[$actionBase[$state] + $symbol] != $symbol
63 * then the action is defaulted, i.e. $actionDefault[$state] should be used instead. */
64 protected $actionCheck;
65 /** @var array Map of states to their default action */
66 protected $actionDefault;
67
68 /** @var array Map of non-terminals to a displacement into the $goto table. The corresponding goto state for this
69 * non-terminal/state pair is $goto[$gotoBase[$nonTerminal] + $state] (unless defaulted) */
70 protected $gotoBase;
71 /** @var array Table of states to goto after reduction. Indexed according to $gotoBase comment. */
72 protected $goto;
73 /** @var array Table indexed analogously to $goto. If $gotoCheck[$gotoBase[$nonTerminal] + $state] != $nonTerminal
74 * then the goto state is defaulted, i.e. $gotoDefault[$nonTerminal] should be used. */
75 protected $gotoCheck;
76 /** @var array Map of non-terminals to the default state to goto after their reduction */
77 protected $gotoDefault;
78
79 /** @var array Map of rules to the non-terminal on their left-hand side, i.e. the non-terminal to use for
80 * determining the state to goto after reduction. */
81 protected $ruleToNonTerminal;
82 /** @var array Map of rules to the length of their right-hand side, which is the number of elements that have to
83 * be popped from the stack(s) on reduction. */
84 protected $ruleToLength;
85
86 /*
87 * The following members are part of the parser state:
88 */
89
90 /** @var Lexer Lexer that is used when parsing */
91 protected $lexer;
92 /** @var mixed Temporary value containing the result of last semantic action (reduction) */
93 protected $semValue;
94 /** @var int Position in stacks (state stack, semantic value stack, attribute stack) */
95 protected $stackPos;
96 /** @var array Semantic value stack (contains values of tokens and semantic action results) */
97 protected $semStack;
98 /** @var array[] Start attribute stack */
99 protected $startAttributeStack;
100 /** @var array[] End attribute stack */
101 protected $endAttributeStack;
102 /** @var array End attributes of last *shifted* token */
103 protected $endAttributes;
104 /** @var array Start attributes of last *read* token */
105 protected $lookaheadStartAttributes;
106
107 /** @var ErrorHandler Error handler */
108 protected $errorHandler;
109 /** @var Error[] Errors collected during last parse */
110 protected $errors;
111 /** @var int Error state, used to avoid error floods */
112 protected $errorState;
113
114 /**
115 * Creates a parser instance.
116 *
117 * @param Lexer $lexer A lexer
118 * @param array $options Options array. Currently no options are supported.
119 */
120 public function __construct(Lexer $lexer, array $options = array()) {
121 $this->lexer = $lexer;
122 $this->errors = array();
123
124 if (isset($options['throwOnError'])) {
125 throw new \LogicException(
126 '"throwOnError" is no longer supported, use "errorHandler" instead');
127 }
128 }
129
130 /**
131 * Parses PHP code into a node tree.
132 *
133 * If a non-throwing error handler is used, the parser will continue parsing after an error
134 * occurred and attempt to build a partial AST.
135 *
136 * @param string $code The source code to parse
137 * @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
138 * to ErrorHandler\Throwing.
139 *
140 * @return Node[]|null Array of statements (or null if the 'throwOnError' option is disabled and the parser was
141 * unable to recover from an error).
142 */
143 public function parse($code, ?ErrorHandler $errorHandler = null) {
144 $this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
145
146 // Initialize the lexer
147 $this->lexer->startLexing($code, $this->errorHandler);
148
149 // We start off with no lookahead-token
150 $symbol = self::SYMBOL_NONE;
151
152 // The attributes for a node are taken from the first and last token of the node.
153 // From the first token only the startAttributes are taken and from the last only
154 // the endAttributes. Both are merged using the array union operator (+).
155 $startAttributes = '*POISON';
156 $endAttributes = '*POISON';
157 $this->endAttributes = $endAttributes;
158
159 // Keep stack of start and end attributes
160 $this->startAttributeStack = array();
161 $this->endAttributeStack = array($endAttributes);
162
163 // Start off in the initial state and keep a stack of previous states
164 $state = 0;
165 $stateStack = array($state);
166
167 // Semantic value stack (contains values of tokens and semantic action results)
168 $this->semStack = array();
169
170 // Current position in the stack(s)
171 $this->stackPos = 0;
172
173 $this->errorState = 0;
174
175 for (;;) {
176 //$this->traceNewState($state, $symbol);
177
178 if ($this->actionBase[$state] == 0) {
179 $rule = $this->actionDefault[$state];
180 } else {
181 if ($symbol === self::SYMBOL_NONE) {
182 // Fetch the next token id from the lexer and fetch additional info by-ref.
183 // The end attributes are fetched into a temporary variable and only set once the token is really
184 // shifted (not during read). Otherwise you would sometimes get off-by-one errors, when a rule is
185 // reduced after a token was read but not yet shifted.
186 $tokenId = $this->lexer->getNextToken($tokenValue, $startAttributes, $endAttributes);
187
188 // map the lexer token id to the internally used symbols
189 $symbol = $tokenId >= 0 && $tokenId < $this->tokenToSymbolMapSize
190 ? $this->tokenToSymbol[$tokenId]
191 : $this->invalidSymbol;
192
193 if ($symbol === $this->invalidSymbol) {
194 throw new \RangeException(sprintf(
195 'The lexer returned an invalid token (id=%d, value=%s)',
196 $tokenId, $tokenValue
197 ));
198 }
199
200 // This is necessary to assign some meaningful attributes to /* empty */ productions. They'll get
201 // the attributes of the next token, even though they don't contain it themselves.
202 $this->startAttributeStack[$this->stackPos+1] = $startAttributes;
203 $this->endAttributeStack[$this->stackPos+1] = $endAttributes;
204 $this->lookaheadStartAttributes = $startAttributes;
205
206 //$this->traceRead($symbol);
207 }
208
209 $idx = $this->actionBase[$state] + $symbol;
210 if ((($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $symbol)
211 || ($state < $this->YY2TBLSTATE
212 && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $symbol) >= 0
213 && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $symbol))
214 && ($action = $this->action[$idx]) != $this->defaultAction) {
215 /*
216 * >= YYNLSTATES: shift and reduce
217 * > 0: shift
218 * = 0: accept
219 * < 0: reduce
220 * = -YYUNEXPECTED: error
221 */
222 if ($action > 0) {
223 /* shift */
224 //$this->traceShift($symbol);
225
226 ++$this->stackPos;
227 $stateStack[$this->stackPos] = $state = $action;
228 $this->semStack[$this->stackPos] = $tokenValue;
229 $this->startAttributeStack[$this->stackPos] = $startAttributes;
230 $this->endAttributeStack[$this->stackPos] = $endAttributes;
231 $this->endAttributes = $endAttributes;
232 $symbol = self::SYMBOL_NONE;
233
234 if ($this->errorState) {
235 --$this->errorState;
236 }
237
238 if ($action < $this->YYNLSTATES) {
239 continue;
240 }
241
242 /* $yyn >= YYNLSTATES means shift-and-reduce */
243 $rule = $action - $this->YYNLSTATES;
244 } else {
245 $rule = -$action;
246 }
247 } else {
248 $rule = $this->actionDefault[$state];
249 }
250 }
251
252 for (;;) {
253 if ($rule === 0) {
254 /* accept */
255 //$this->traceAccept();
256 return $this->semValue;
257 } elseif ($rule !== $this->unexpectedTokenRule) {
258 /* reduce */
259 //$this->traceReduce($rule);
260
261 try {
262 $this->{'reduceRule' . $rule}();
263 } catch (Error $e) {
264 if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) {
265 $e->setStartLine($startAttributes['startLine']);
266 }
267
268 $this->emitError($e);
269 // Can't recover from this type of error
270 return null;
271 }
272
273 /* Goto - shift nonterminal */
274 $lastEndAttributes = $this->endAttributeStack[$this->stackPos];
275 $this->stackPos -= $this->ruleToLength[$rule];
276 $nonTerminal = $this->ruleToNonTerminal[$rule];
277 $idx = $this->gotoBase[$nonTerminal] + $stateStack[$this->stackPos];
278 if ($idx >= 0 && $idx < $this->gotoTableSize && $this->gotoCheck[$idx] == $nonTerminal) {
279 $state = $this->goto[$idx];
280 } else {
281 $state = $this->gotoDefault[$nonTerminal];
282 }
283
284 ++$this->stackPos;
285 $stateStack[$this->stackPos] = $state;
286 $this->semStack[$this->stackPos] = $this->semValue;
287 $this->endAttributeStack[$this->stackPos] = $lastEndAttributes;
288 } else {
289 /* error */
290 switch ($this->errorState) {
291 case 0:
292 $msg = $this->getErrorMessage($symbol, $state);
293 $this->emitError(new Error($msg, $startAttributes + $endAttributes));
294 // Break missing intentionally
295 case 1:
296 case 2:
297 $this->errorState = 3;
298
299 // Pop until error-expecting state uncovered
300 while (!(
301 (($idx = $this->actionBase[$state] + $this->errorSymbol) >= 0
302 && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
303 || ($state < $this->YY2TBLSTATE
304 && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $this->errorSymbol) >= 0
305 && $idx < $this->actionTableSize && $this->actionCheck[$idx] == $this->errorSymbol)
306 ) || ($action = $this->action[$idx]) == $this->defaultAction) { // Not totally sure about this
307 if ($this->stackPos <= 0) {
308 // Could not recover from error
309 return null;
310 }
311 $state = $stateStack[--$this->stackPos];
312 //$this->tracePop($state);
313 }
314
315 //$this->traceShift($this->errorSymbol);
316 ++$this->stackPos;
317 $stateStack[$this->stackPos] = $state = $action;
318
319 // We treat the error symbol as being empty, so we reset the end attributes
320 // to the end attributes of the last non-error symbol
321 $this->endAttributeStack[$this->stackPos] = $this->endAttributeStack[$this->stackPos - 1];
322 $this->endAttributes = $this->endAttributeStack[$this->stackPos - 1];
323 break;
324
325 case 3:
326 if ($symbol === 0) {
327 // Reached EOF without recovering from error
328 return null;
329 }
330
331 //$this->traceDiscard($symbol);
332 $symbol = self::SYMBOL_NONE;
333 break 2;
334 }
335 }
336
337 if ($state < $this->YYNLSTATES) {
338 break;
339 }
340
341 /* >= YYNLSTATES means shift-and-reduce */
342 $rule = $state - $this->YYNLSTATES;
343 }
344 }
345
346 throw new \RuntimeException('Reached end of parser loop');
347 }
348
349 protected function emitError(Error $error) {
350 $this->errorHandler->handleError($error);
351 }
352
353 protected function getErrorMessage($symbol, $state) {
354 $expectedString = '';
355 if ($expected = $this->getExpectedTokens($state)) {
356 $expectedString = ', expecting ' . implode(' or ', $expected);
357 }
358
359 return 'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString;
360 }
361
362 protected function getExpectedTokens($state) {
363 $expected = array();
364
365 $base = $this->actionBase[$state];
366 foreach ($this->symbolToName as $symbol => $name) {
367 $idx = $base + $symbol;
368 if ($idx >= 0 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
369 || $state < $this->YY2TBLSTATE
370 && ($idx = $this->actionBase[$state + $this->YYNLSTATES] + $symbol) >= 0
371 && $idx < $this->actionTableSize && $this->actionCheck[$idx] === $symbol
372 ) {
373 if ($this->action[$idx] != $this->unexpectedTokenRule
374 && $this->action[$idx] != $this->defaultAction
375 && $symbol != $this->errorSymbol
376 ) {
377 if (count($expected) == 4) {
378 /* Too many expected tokens */
379 return array();
380 }
381
382 $expected[] = $name;
383 }
384 }
385 }
386
387 return $expected;
388 }
389
390 /*
391 * Tracing functions used for debugging the parser.
392 */
393
394 /*
395 protected function traceNewState($state, $symbol) {
396 echo '% State ' . $state
397 . ', Lookahead ' . ($symbol == self::SYMBOL_NONE ? '--none--' : $this->symbolToName[$symbol]) . "\n";
398 }
399
400 protected function traceRead($symbol) {
401 echo '% Reading ' . $this->symbolToName[$symbol] . "\n";
402 }
403
404 protected function traceShift($symbol) {
405 echo '% Shift ' . $this->symbolToName[$symbol] . "\n";
406 }
407
408 protected function traceAccept() {
409 echo "% Accepted.\n";
410 }
411
412 protected function traceReduce($n) {
413 echo '% Reduce by (' . $n . ') ' . $this->productions[$n] . "\n";
414 }
415
416 protected function tracePop($state) {
417 echo '% Recovering, uncovered state ' . $state . "\n";
418 }
419
420 protected function traceDiscard($symbol) {
421 echo '% Discard ' . $this->symbolToName[$symbol] . "\n";
422 }
423 */
424
425 /*
426 * Helper functions invoked by semantic actions
427 */
428
429 /**
430 * Moves statements of semicolon-style namespaces into $ns->stmts and checks various error conditions.
431 *
432 * @param Node[] $stmts
433 * @return Node[]
434 */
435 protected function handleNamespaces(array $stmts) {
436 $hasErrored = false;
437 $style = $this->getNamespacingStyle($stmts);
438 if (null === $style) {
439 // not namespaced, nothing to do
440 return $stmts;
441 } elseif ('brace' === $style) {
442 // For braced namespaces we only have to check that there are no invalid statements between the namespaces
443 $afterFirstNamespace = false;
444 foreach ($stmts as $stmt) {
445 if ($stmt instanceof Node\Stmt\Namespace_) {
446 $afterFirstNamespace = true;
447 } elseif (!$stmt instanceof Node\Stmt\HaltCompiler
448 && !$stmt instanceof Node\Stmt\Nop
449 && $afterFirstNamespace && !$hasErrored) {
450 $this->emitError(new Error(
451 'No code may exist outside of namespace {}', $stmt->getAttributes()));
452 $hasErrored = true; // Avoid one error for every statement
453 }
454 }
455 return $stmts;
456 } else {
457 // For semicolon namespaces we have to move the statements after a namespace declaration into ->stmts
458 $resultStmts = array();
459 $targetStmts =& $resultStmts;
460 foreach ($stmts as $stmt) {
461 if ($stmt instanceof Node\Stmt\Namespace_) {
462 if ($stmt->stmts === null) {
463 $stmt->stmts = array();
464 $targetStmts =& $stmt->stmts;
465 $resultStmts[] = $stmt;
466 } else {
467 // This handles the invalid case of mixed style namespaces
468 $resultStmts[] = $stmt;
469 $targetStmts =& $resultStmts;
470 }
471 } elseif ($stmt instanceof Node\Stmt\HaltCompiler) {
472 // __halt_compiler() is not moved into the namespace
473 $resultStmts[] = $stmt;
474 } else {
475 $targetStmts[] = $stmt;
476 }
477 }
478 return $resultStmts;
479 }
480 }
481
482 private function getNamespacingStyle(array $stmts) {
483 $style = null;
484 $hasNotAllowedStmts = false;
485 foreach ($stmts as $i => $stmt) {
486 if ($stmt instanceof Node\Stmt\Namespace_) {
487 $currentStyle = null === $stmt->stmts ? 'semicolon' : 'brace';
488 if (null === $style) {
489 $style = $currentStyle;
490 if ($hasNotAllowedStmts) {
491 $this->emitError(new Error(
492 'Namespace declaration statement has to be the very first statement in the script',
493 $stmt->getLine() // Avoid marking the entire namespace as an error
494 ));
495 }
496 } elseif ($style !== $currentStyle) {
497 $this->emitError(new Error(
498 'Cannot mix bracketed namespace declarations with unbracketed namespace declarations',
499 $stmt->getLine() // Avoid marking the entire namespace as an error
500 ));
501 // Treat like semicolon style for namespace normalization
502 return 'semicolon';
503 }
504 continue;
505 }
506
507 /* declare(), __halt_compiler() and nops can be used before a namespace declaration */
508 if ($stmt instanceof Node\Stmt\Declare_
509 || $stmt instanceof Node\Stmt\HaltCompiler
510 || $stmt instanceof Node\Stmt\Nop) {
511 continue;
512 }
513
514 /* There may be a hashbang line at the very start of the file */
515 if ($i == 0 && $stmt instanceof Node\Stmt\InlineHTML && preg_match('/\A#!.*\r?\n\z/', $stmt->value)) {
516 continue;
517 }
518
519 /* Everything else if forbidden before namespace declarations */
520 $hasNotAllowedStmts = true;
521 }
522 return $style;
523 }
524
525 protected function handleBuiltinTypes(Name $name) {
526 $scalarTypes = [
527 'bool' => true,
528 'int' => true,
529 'float' => true,
530 'string' => true,
531 'iterable' => true,
532 'void' => true,
533 'object' => true,
534 ];
535
536 if (!$name->isUnqualified()) {
537 return $name;
538 }
539
540 $lowerName = strtolower($name->toString());
541 return isset($scalarTypes[$lowerName]) ? $lowerName : $name;
542 }
543
544 protected static $specialNames = array(
545 'self' => true,
546 'parent' => true,
547 'static' => true,
548 );
549
550 protected function getAttributesAt($pos) {
551 return $this->startAttributeStack[$pos] + $this->endAttributeStack[$pos];
552 }
553
554 protected function parseLNumber($str, $attributes, $allowInvalidOctal = false) {
555 try {
556 return LNumber::fromString($str, $attributes, $allowInvalidOctal);
557 } catch (Error $error) {
558 $this->emitError($error);
559 // Use dummy value
560 return new LNumber(0, $attributes);
561 }
562 }
563
564 protected function parseNumString($str, $attributes) {
565 if (!preg_match('/^(?:0|-?[1-9][0-9]*)$/', $str)) {
566 return new String_($str, $attributes);
567 }
568
569 $num = +$str;
570 if (!is_int($num)) {
571 return new String_($str, $attributes);
572 }
573
574 return new LNumber($num, $attributes);
575 }
576
577 protected function checkModifier($a, $b, $modifierPos) {
578 // Jumping through some hoops here because verifyModifier() is also used elsewhere
579 try {
580 Class_::verifyModifier($a, $b);
581 } catch (Error $error) {
582 $error->setAttributes($this->getAttributesAt($modifierPos));
583 $this->emitError($error);
584 }
585 }
586
587 protected function checkParam(Param $node) {
588 if ($node->variadic && null !== $node->default) {
589 $this->emitError(new Error(
590 'Variadic parameter cannot have a default value',
591 $node->default->getAttributes()
592 ));
593 }
594 }
595
596 protected function checkTryCatch(TryCatch $node) {
597 if (empty($node->catches) && null === $node->finally) {
598 $this->emitError(new Error(
599 'Cannot use try without catch or finally', $node->getAttributes()
600 ));
601 }
602 }
603
604 protected function checkNamespace(Namespace_ $node) {
605 if (isset(self::$specialNames[strtolower($node->name)])) {
606 $this->emitError(new Error(
607 sprintf('Cannot use \'%s\' as namespace name', $node->name),
608 $node->name->getAttributes()
609 ));
610 }
611
612 if (null !== $node->stmts) {
613 foreach ($node->stmts as $stmt) {
614 if ($stmt instanceof Namespace_) {
615 $this->emitError(new Error(
616 'Namespace declarations cannot be nested', $stmt->getAttributes()
617 ));
618 }
619 }
620 }
621 }
622
623 protected function checkClass(Class_ $node, $namePos) {
624 if (null !== $node->name && isset(self::$specialNames[strtolower($node->name)])) {
625 $this->emitError(new Error(
626 sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
627 $this->getAttributesAt($namePos)
628 ));
629 }
630
631 if (isset(self::$specialNames[strtolower($node->extends)])) {
632 $this->emitError(new Error(
633 sprintf('Cannot use \'%s\' as class name as it is reserved', $node->extends),
634 $node->extends->getAttributes()
635 ));
636 }
637
638 foreach ($node->implements as $interface) {
639 if (isset(self::$specialNames[strtolower($interface)])) {
640 $this->emitError(new Error(
641 sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
642 $interface->getAttributes()
643 ));
644 }
645 }
646 }
647
648 protected function checkInterface(Interface_ $node, $namePos) {
649 if (null !== $node->name && isset(self::$specialNames[strtolower($node->name)])) {
650 $this->emitError(new Error(
651 sprintf('Cannot use \'%s\' as class name as it is reserved', $node->name),
652 $this->getAttributesAt($namePos)
653 ));
654 }
655
656 foreach ($node->extends as $interface) {
657 if (isset(self::$specialNames[strtolower($interface)])) {
658 $this->emitError(new Error(
659 sprintf('Cannot use \'%s\' as interface name as it is reserved', $interface),
660 $interface->getAttributes()
661 ));
662 }
663 }
664 }
665
666 protected function checkClassMethod(ClassMethod $node, $modifierPos) {
667 if ($node->flags & Class_::MODIFIER_STATIC) {
668 switch (strtolower($node->name)) {
669 case '__construct':
670 $this->emitError(new Error(
671 sprintf('Constructor %s() cannot be static', $node->name),
672 $this->getAttributesAt($modifierPos)));
673 break;
674 case '__destruct':
675 $this->emitError(new Error(
676 sprintf('Destructor %s() cannot be static', $node->name),
677 $this->getAttributesAt($modifierPos)));
678 break;
679 case '__clone':
680 $this->emitError(new Error(
681 sprintf('Clone method %s() cannot be static', $node->name),
682 $this->getAttributesAt($modifierPos)));
683 break;
684 }
685 }
686 }
687
688 protected function checkClassConst(ClassConst $node, $modifierPos) {
689 if ($node->flags & Class_::MODIFIER_STATIC) {
690 $this->emitError(new Error(
691 "Cannot use 'static' as constant modifier",
692 $this->getAttributesAt($modifierPos)));
693 }
694 if ($node->flags & Class_::MODIFIER_ABSTRACT) {
695 $this->emitError(new Error(
696 "Cannot use 'abstract' as constant modifier",
697 $this->getAttributesAt($modifierPos)));
698 }
699 if ($node->flags & Class_::MODIFIER_FINAL) {
700 $this->emitError(new Error(
701 "Cannot use 'final' as constant modifier",
702 $this->getAttributesAt($modifierPos)));
703 }
704 }
705
706 protected function checkProperty(Property $node, $modifierPos) {
707 if ($node->flags & Class_::MODIFIER_ABSTRACT) {
708 $this->emitError(new Error('Properties cannot be declared abstract',
709 $this->getAttributesAt($modifierPos)));
710 }
711
712 if ($node->flags & Class_::MODIFIER_FINAL) {
713 $this->emitError(new Error('Properties cannot be declared final',
714 $this->getAttributesAt($modifierPos)));
715 }
716 }
717
718 protected function checkUseUse(UseUse $node, $namePos) {
719 if ('self' == strtolower($node->alias) || 'parent' == strtolower($node->alias)) {
720 $this->emitError(new Error(
721 sprintf(
722 'Cannot use %s as %s because \'%2$s\' is a special class name',
723 $node->name, $node->alias
724 ),
725 $this->getAttributesAt($namePos)
726 ));
727 }
728 }
729 }
730