ParserAbstract.php 29 KB

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