ParserTest.php 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <?php
  2. namespace PhpParser;
  3. use PhpParser\Comment;
  4. use PhpParser\Node\Expr;
  5. use PhpParser\Node\Scalar;
  6. use PhpParser\Node\Scalar\String_;
  7. abstract class ParserTest extends \PHPUnit_Framework_TestCase
  8. {
  9. /** @returns Parser */
  10. abstract protected function getParser(Lexer $lexer);
  11. /**
  12. * @expectedException \PhpParser\Error
  13. * @expectedExceptionMessage Syntax error, unexpected EOF on line 1
  14. */
  15. public function testParserThrowsSyntaxError() {
  16. $parser = $this->getParser(new Lexer());
  17. $parser->parse('<?php foo');
  18. }
  19. /**
  20. * @expectedException \PhpParser\Error
  21. * @expectedExceptionMessage Cannot use foo as self because 'self' is a special class name on line 1
  22. */
  23. public function testParserThrowsSpecialError() {
  24. $parser = $this->getParser(new Lexer());
  25. $parser->parse('<?php use foo as self;');
  26. }
  27. /**
  28. * @expectedException \PhpParser\Error
  29. * @expectedExceptionMessage Unterminated comment on line 1
  30. */
  31. public function testParserThrowsLexerError() {
  32. $parser = $this->getParser(new Lexer());
  33. $parser->parse('<?php /*');
  34. }
  35. public function testAttributeAssignment() {
  36. $lexer = new Lexer(array(
  37. 'usedAttributes' => array(
  38. 'comments', 'startLine', 'endLine',
  39. 'startTokenPos', 'endTokenPos',
  40. )
  41. ));
  42. $code = <<<'EOC'
  43. <?php
  44. /** Doc comment */
  45. function test($a) {
  46. // Line
  47. // Comments
  48. echo $a;
  49. }
  50. EOC;
  51. $code = canonicalize($code);
  52. $parser = $this->getParser($lexer);
  53. $stmts = $parser->parse($code);
  54. /** @var \PhpParser\Node\Stmt\Function_ $fn */
  55. $fn = $stmts[0];
  56. $this->assertInstanceOf('PhpParser\Node\Stmt\Function_', $fn);
  57. $this->assertEquals(array(
  58. 'comments' => array(
  59. new Comment\Doc('/** Doc comment */', 2, 6),
  60. ),
  61. 'startLine' => 3,
  62. 'endLine' => 7,
  63. 'startTokenPos' => 3,
  64. 'endTokenPos' => 21,
  65. ), $fn->getAttributes());
  66. $param = $fn->params[0];
  67. $this->assertInstanceOf('PhpParser\Node\Param', $param);
  68. $this->assertEquals(array(
  69. 'startLine' => 3,
  70. 'endLine' => 3,
  71. 'startTokenPos' => 7,
  72. 'endTokenPos' => 7,
  73. ), $param->getAttributes());
  74. /** @var \PhpParser\Node\Stmt\Echo_ $echo */
  75. $echo = $fn->stmts[0];
  76. $this->assertInstanceOf('PhpParser\Node\Stmt\Echo_', $echo);
  77. $this->assertEquals(array(
  78. 'comments' => array(
  79. new Comment("// Line\n", 4, 49),
  80. new Comment("// Comments\n", 5, 61),
  81. ),
  82. 'startLine' => 6,
  83. 'endLine' => 6,
  84. 'startTokenPos' => 16,
  85. 'endTokenPos' => 19,
  86. ), $echo->getAttributes());
  87. /** @var \PhpParser\Node\Expr\Variable $var */
  88. $var = $echo->exprs[0];
  89. $this->assertInstanceOf('PhpParser\Node\Expr\Variable', $var);
  90. $this->assertEquals(array(
  91. 'startLine' => 6,
  92. 'endLine' => 6,
  93. 'startTokenPos' => 18,
  94. 'endTokenPos' => 18,
  95. ), $var->getAttributes());
  96. }
  97. /**
  98. * @expectedException \RangeException
  99. * @expectedExceptionMessage The lexer returned an invalid token (id=999, value=foobar)
  100. */
  101. public function testInvalidToken() {
  102. $lexer = new InvalidTokenLexer;
  103. $parser = $this->getParser($lexer);
  104. $parser->parse('dummy');
  105. }
  106. /**
  107. * @dataProvider provideTestExtraAttributes
  108. */
  109. public function testExtraAttributes($code, $expectedAttributes) {
  110. $parser = $this->getParser(new Lexer);
  111. $stmts = $parser->parse("<?php $code;");
  112. $attributes = $stmts[0]->getAttributes();
  113. foreach ($expectedAttributes as $name => $value) {
  114. $this->assertSame($value, $attributes[$name]);
  115. }
  116. }
  117. public function provideTestExtraAttributes() {
  118. return array(
  119. array('0', ['kind' => Scalar\LNumber::KIND_DEC]),
  120. array('9', ['kind' => Scalar\LNumber::KIND_DEC]),
  121. array('07', ['kind' => Scalar\LNumber::KIND_OCT]),
  122. array('0xf', ['kind' => Scalar\LNumber::KIND_HEX]),
  123. array('0XF', ['kind' => Scalar\LNumber::KIND_HEX]),
  124. array('0b1', ['kind' => Scalar\LNumber::KIND_BIN]),
  125. array('0B1', ['kind' => Scalar\LNumber::KIND_BIN]),
  126. array('[]', ['kind' => Expr\Array_::KIND_SHORT]),
  127. array('array()', ['kind' => Expr\Array_::KIND_LONG]),
  128. array("'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
  129. array("b'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
  130. array("B'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
  131. array('"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
  132. array('b"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
  133. array('B"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
  134. array('"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
  135. array('b"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
  136. array('B"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
  137. array("<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
  138. array("<<<STR\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
  139. array("<<<\"STR\"\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
  140. array("b<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
  141. array("B<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
  142. array("<<< \t 'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
  143. // HHVM doesn't support this due to a lexer bug
  144. // (https://github.com/facebook/hhvm/issues/6970)
  145. // array("<<<'\xff'\n\xff\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => "\xff"]),
  146. array("<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
  147. array("b<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
  148. array("B<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
  149. array("<<< \t \"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
  150. array("die", ['kind' => Expr\Exit_::KIND_DIE]),
  151. array("die('done')", ['kind' => Expr\Exit_::KIND_DIE]),
  152. array("exit", ['kind' => Expr\Exit_::KIND_EXIT]),
  153. array("exit(1)", ['kind' => Expr\Exit_::KIND_EXIT]),
  154. array("?>Foo", ['hasLeadingNewline' => false]),
  155. array("?>\nFoo", ['hasLeadingNewline' => true]),
  156. );
  157. }
  158. }
  159. class InvalidTokenLexer extends Lexer {
  160. public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) {
  161. $value = 'foobar';
  162. return 999;
  163. }
  164. }