ParserTest.php 6.5 KB

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