ParserTest.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\ExpressionLanguage\Tests;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\ExpressionLanguage\Lexer;
  13. use Symfony\Component\ExpressionLanguage\Node;
  14. use Symfony\Component\ExpressionLanguage\Parser;
  15. class ParserTest extends TestCase
  16. {
  17. public function testParseWithInvalidName()
  18. {
  19. $this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
  20. $this->expectExceptionMessage('Variable "foo" is not valid around position 1 for expression `foo`.');
  21. $lexer = new Lexer();
  22. $parser = new Parser([]);
  23. $parser->parse($lexer->tokenize('foo'));
  24. }
  25. public function testParseWithZeroInNames()
  26. {
  27. $this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
  28. $this->expectExceptionMessage('Variable "foo" is not valid around position 1 for expression `foo`.');
  29. $lexer = new Lexer();
  30. $parser = new Parser([]);
  31. $parser->parse($lexer->tokenize('foo'), [0]);
  32. }
  33. /**
  34. * @dataProvider getParseData
  35. */
  36. public function testParse($node, $expression, $names = [])
  37. {
  38. $lexer = new Lexer();
  39. $parser = new Parser([]);
  40. $this->assertEquals($node, $parser->parse($lexer->tokenize($expression), $names));
  41. }
  42. public function getParseData()
  43. {
  44. $arguments = new Node\ArgumentsNode();
  45. $arguments->addElement(new Node\ConstantNode('arg1'));
  46. $arguments->addElement(new Node\ConstantNode(2));
  47. $arguments->addElement(new Node\ConstantNode(true));
  48. $arrayNode = new Node\ArrayNode();
  49. $arrayNode->addElement(new Node\NameNode('bar'));
  50. return [
  51. [
  52. new Node\NameNode('a'),
  53. 'a',
  54. ['a'],
  55. ],
  56. [
  57. new Node\ConstantNode('a'),
  58. '"a"',
  59. ],
  60. [
  61. new Node\ConstantNode(3),
  62. '3',
  63. ],
  64. [
  65. new Node\ConstantNode(false),
  66. 'false',
  67. ],
  68. [
  69. new Node\ConstantNode(true),
  70. 'true',
  71. ],
  72. [
  73. new Node\ConstantNode(null),
  74. 'null',
  75. ],
  76. [
  77. new Node\UnaryNode('-', new Node\ConstantNode(3)),
  78. '-3',
  79. ],
  80. [
  81. new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)),
  82. '3 - 3',
  83. ],
  84. [
  85. new Node\BinaryNode('*',
  86. new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)),
  87. new Node\ConstantNode(2)
  88. ),
  89. '(3 - 3) * 2',
  90. ],
  91. [
  92. new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::PROPERTY_CALL),
  93. 'foo.bar',
  94. ['foo'],
  95. ],
  96. [
  97. new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
  98. 'foo.bar()',
  99. ['foo'],
  100. ],
  101. [
  102. new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('not', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
  103. 'foo.not()',
  104. ['foo'],
  105. ],
  106. [
  107. new Node\GetAttrNode(
  108. new Node\NameNode('foo'),
  109. new Node\ConstantNode('bar', true),
  110. $arguments,
  111. Node\GetAttrNode::METHOD_CALL
  112. ),
  113. 'foo.bar("arg1", 2, true)',
  114. ['foo'],
  115. ],
  116. [
  117. new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode(3), new Node\ArgumentsNode(), Node\GetAttrNode::ARRAY_CALL),
  118. 'foo[3]',
  119. ['foo'],
  120. ],
  121. [
  122. new Node\ConditionalNode(new Node\ConstantNode(true), new Node\ConstantNode(true), new Node\ConstantNode(false)),
  123. 'true ? true : false',
  124. ],
  125. [
  126. new Node\BinaryNode('matches', new Node\ConstantNode('foo'), new Node\ConstantNode('/foo/')),
  127. '"foo" matches "/foo/"',
  128. ],
  129. // chained calls
  130. [
  131. $this->createGetAttrNode(
  132. $this->createGetAttrNode(
  133. $this->createGetAttrNode(
  134. $this->createGetAttrNode(new Node\NameNode('foo'), 'bar', Node\GetAttrNode::METHOD_CALL),
  135. 'foo', Node\GetAttrNode::METHOD_CALL),
  136. 'baz', Node\GetAttrNode::PROPERTY_CALL),
  137. '3', Node\GetAttrNode::ARRAY_CALL),
  138. 'foo.bar().foo().baz[3]',
  139. ['foo'],
  140. ],
  141. [
  142. new Node\NameNode('foo'),
  143. 'bar',
  144. ['foo' => 'bar'],
  145. ],
  146. // Operators collisions
  147. [
  148. new Node\BinaryNode(
  149. 'in',
  150. new Node\GetAttrNode(
  151. new Node\NameNode('foo'),
  152. new Node\ConstantNode('not', true),
  153. new Node\ArgumentsNode(),
  154. Node\GetAttrNode::PROPERTY_CALL
  155. ),
  156. $arrayNode
  157. ),
  158. 'foo.not in [bar]',
  159. ['foo', 'bar'],
  160. ],
  161. [
  162. new Node\BinaryNode(
  163. 'or',
  164. new Node\UnaryNode('not', new Node\NameNode('foo')),
  165. new Node\GetAttrNode(
  166. new Node\NameNode('foo'),
  167. new Node\ConstantNode('not', true),
  168. new Node\ArgumentsNode(),
  169. Node\GetAttrNode::PROPERTY_CALL
  170. )
  171. ),
  172. 'not foo or foo.not',
  173. ['foo'],
  174. ],
  175. ];
  176. }
  177. private function createGetAttrNode($node, $item, $type)
  178. {
  179. return new Node\GetAttrNode($node, new Node\ConstantNode($item, Node\GetAttrNode::ARRAY_CALL !== $type), new Node\ArgumentsNode(), $type);
  180. }
  181. /**
  182. * @dataProvider getInvalidPostfixData
  183. */
  184. public function testParseWithInvalidPostfixData($expr, $names = [])
  185. {
  186. $this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
  187. $lexer = new Lexer();
  188. $parser = new Parser([]);
  189. $parser->parse($lexer->tokenize($expr), $names);
  190. }
  191. public function getInvalidPostfixData()
  192. {
  193. return [
  194. [
  195. 'foo."#"',
  196. ['foo'],
  197. ],
  198. [
  199. 'foo."bar"',
  200. ['foo'],
  201. ],
  202. [
  203. 'foo.**',
  204. ['foo'],
  205. ],
  206. [
  207. 'foo.123',
  208. ['foo'],
  209. ],
  210. ];
  211. }
  212. public function testNameProposal()
  213. {
  214. $this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
  215. $this->expectExceptionMessage('Did you mean "baz"?');
  216. $lexer = new Lexer();
  217. $parser = new Parser([]);
  218. $parser->parse($lexer->tokenize('foo > bar'), ['foo', 'baz']);
  219. }
  220. }