LexerTest.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <?php
  2. namespace PhpParser;
  3. use PhpParser\Parser\Tokens;
  4. class LexerTest extends \PHPUnit_Framework_TestCase
  5. {
  6. /* To allow overwriting in parent class */
  7. protected function getLexer(array $options = array()) {
  8. return new Lexer($options);
  9. }
  10. /**
  11. * @dataProvider provideTestError
  12. */
  13. public function testError($code, $message) {
  14. if (defined('HHVM_VERSION')) {
  15. $this->markTestSkipped('HHVM does not throw warnings from token_get_all()');
  16. }
  17. $lexer = $this->getLexer();
  18. try {
  19. $lexer->startLexing($code);
  20. } catch (Error $e) {
  21. $this->assertSame($message, $e->getMessage());
  22. return;
  23. }
  24. $this->fail('Expected PhpParser\Error');
  25. }
  26. public function provideTestError() {
  27. return array(
  28. array('<?php /*', 'Unterminated comment on line 1'),
  29. array('<?php ' . "\1", 'Unexpected character "' . "\1" . '" (ASCII 1) on unknown line'),
  30. array('<?php ' . "\0", 'Unexpected null byte on unknown line'),
  31. );
  32. }
  33. /**
  34. * @dataProvider provideTestLex
  35. */
  36. public function testLex($code, $options, $tokens) {
  37. $lexer = $this->getLexer($options);
  38. $lexer->startLexing($code);
  39. while ($id = $lexer->getNextToken($value, $startAttributes, $endAttributes)) {
  40. $token = array_shift($tokens);
  41. $this->assertSame($token[0], $id);
  42. $this->assertSame($token[1], $value);
  43. $this->assertEquals($token[2], $startAttributes);
  44. $this->assertEquals($token[3], $endAttributes);
  45. }
  46. }
  47. public function provideTestLex() {
  48. return array(
  49. // tests conversion of closing PHP tag and drop of whitespace and opening tags
  50. array(
  51. '<?php tokens ?>plaintext',
  52. array(),
  53. array(
  54. array(
  55. Tokens::T_STRING, 'tokens',
  56. array('startLine' => 1), array('endLine' => 1)
  57. ),
  58. array(
  59. ord(';'), '?>',
  60. array('startLine' => 1), array('endLine' => 1)
  61. ),
  62. array(
  63. Tokens::T_INLINE_HTML, 'plaintext',
  64. array('startLine' => 1), array('endLine' => 1)
  65. ),
  66. )
  67. ),
  68. // tests line numbers
  69. array(
  70. '<?php' . "\n" . '$ token /** doc' . "\n" . 'comment */ $',
  71. array(),
  72. array(
  73. array(
  74. ord('$'), '$',
  75. array('startLine' => 2), array('endLine' => 2)
  76. ),
  77. array(
  78. Tokens::T_STRING, 'token',
  79. array('startLine' => 2), array('endLine' => 2)
  80. ),
  81. array(
  82. ord('$'), '$',
  83. array(
  84. 'startLine' => 3,
  85. 'comments' => array(
  86. new Comment\Doc('/** doc' . "\n" . 'comment */', 2, 14),
  87. )
  88. ),
  89. array('endLine' => 3)
  90. ),
  91. )
  92. ),
  93. // tests comment extraction
  94. array(
  95. '<?php /* comment */ // comment' . "\n" . '/** docComment 1 *//** docComment 2 */ token',
  96. array(),
  97. array(
  98. array(
  99. Tokens::T_STRING, 'token',
  100. array(
  101. 'startLine' => 2,
  102. 'comments' => array(
  103. new Comment('/* comment */', 1, 6),
  104. new Comment('// comment' . "\n", 1, 20),
  105. new Comment\Doc('/** docComment 1 */', 2, 31),
  106. new Comment\Doc('/** docComment 2 */', 2, 50),
  107. ),
  108. ),
  109. array('endLine' => 2)
  110. ),
  111. )
  112. ),
  113. // tests differing start and end line
  114. array(
  115. '<?php "foo' . "\n" . 'bar"',
  116. array(),
  117. array(
  118. array(
  119. Tokens::T_CONSTANT_ENCAPSED_STRING, '"foo' . "\n" . 'bar"',
  120. array('startLine' => 1), array('endLine' => 2)
  121. ),
  122. )
  123. ),
  124. // tests exact file offsets
  125. array(
  126. '<?php "a";' . "\n" . '// foo' . "\n" . '"b";',
  127. array('usedAttributes' => array('startFilePos', 'endFilePos')),
  128. array(
  129. array(
  130. Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"',
  131. array('startFilePos' => 6), array('endFilePos' => 8)
  132. ),
  133. array(
  134. ord(';'), ';',
  135. array('startFilePos' => 9), array('endFilePos' => 9)
  136. ),
  137. array(
  138. Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"',
  139. array('startFilePos' => 18), array('endFilePos' => 20)
  140. ),
  141. array(
  142. ord(';'), ';',
  143. array('startFilePos' => 21), array('endFilePos' => 21)
  144. ),
  145. )
  146. ),
  147. // tests token offsets
  148. array(
  149. '<?php "a";' . "\n" . '// foo' . "\n" . '"b";',
  150. array('usedAttributes' => array('startTokenPos', 'endTokenPos')),
  151. array(
  152. array(
  153. Tokens::T_CONSTANT_ENCAPSED_STRING, '"a"',
  154. array('startTokenPos' => 1), array('endTokenPos' => 1)
  155. ),
  156. array(
  157. ord(';'), ';',
  158. array('startTokenPos' => 2), array('endTokenPos' => 2)
  159. ),
  160. array(
  161. Tokens::T_CONSTANT_ENCAPSED_STRING, '"b"',
  162. array('startTokenPos' => 5), array('endTokenPos' => 5)
  163. ),
  164. array(
  165. ord(';'), ';',
  166. array('startTokenPos' => 6), array('endTokenPos' => 6)
  167. ),
  168. )
  169. ),
  170. // tests all attributes being disabled
  171. array(
  172. '<?php /* foo */ $bar;',
  173. array('usedAttributes' => array()),
  174. array(
  175. array(
  176. Tokens::T_VARIABLE, '$bar',
  177. array(), array()
  178. ),
  179. array(
  180. ord(';'), ';',
  181. array(), array()
  182. )
  183. )
  184. )
  185. );
  186. }
  187. /**
  188. * @dataProvider provideTestHaltCompiler
  189. */
  190. public function testHandleHaltCompiler($code, $remaining) {
  191. $lexer = $this->getLexer();
  192. $lexer->startLexing($code);
  193. while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken());
  194. $this->assertSame($remaining, $lexer->handleHaltCompiler());
  195. $this->assertSame(0, $lexer->getNextToken());
  196. }
  197. public function provideTestHaltCompiler() {
  198. return array(
  199. array('<?php ... __halt_compiler();Remaining Text', 'Remaining Text'),
  200. array('<?php ... __halt_compiler ( ) ;Remaining Text', 'Remaining Text'),
  201. array('<?php ... __halt_compiler() ?>Remaining Text', 'Remaining Text'),
  202. //array('<?php ... __halt_compiler();' . "\0", "\0"),
  203. //array('<?php ... __halt_compiler /* */ ( ) ;Remaining Text', 'Remaining Text'),
  204. );
  205. }
  206. /**
  207. * @expectedException \PhpParser\Error
  208. * @expectedExceptionMessage __HALT_COMPILER must be followed by "();"
  209. */
  210. public function testHandleHaltCompilerError() {
  211. $lexer = $this->getLexer();
  212. $lexer->startLexing('<?php ... __halt_compiler invalid ();');
  213. while (Tokens::T_HALT_COMPILER !== $lexer->getNextToken());
  214. $lexer->handleHaltCompiler();
  215. }
  216. public function testGetTokens() {
  217. $code = '<?php "a";' . "\n" . '// foo' . "\n" . '"b";';
  218. $expectedTokens = array(
  219. array(T_OPEN_TAG, '<?php ', 1),
  220. array(T_CONSTANT_ENCAPSED_STRING, '"a"', 1),
  221. ';',
  222. array(T_WHITESPACE, "\n", 1),
  223. array(T_COMMENT, '// foo' . "\n", 2),
  224. array(T_CONSTANT_ENCAPSED_STRING, '"b"', 3),
  225. ';',
  226. );
  227. $lexer = $this->getLexer();
  228. $lexer->startLexing($code);
  229. $this->assertSame($expectedTokens, $lexer->getTokens());
  230. }
  231. }