Mercurial > hg > isophonics-drupal-site
comparison vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 5fb285c0d0e3 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace 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 PhpParser\Node\Name; | |
10 use PhpParser\Node\Param; | |
11 use PhpParser\Node\Scalar\LNumber; | |
12 use PhpParser\Node\Scalar\String_; | |
13 use PhpParser\Node\Stmt\Class_; | |
14 use PhpParser\Node\Stmt\ClassConst; | |
15 use PhpParser\Node\Stmt\ClassMethod; | |
16 use PhpParser\Node\Stmt\Interface_; | |
17 use PhpParser\Node\Stmt\Namespace_; | |
18 use PhpParser\Node\Stmt\Property; | |
19 use PhpParser\Node\Stmt\TryCatch; | |
20 use 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 } |