2018-04-17 02:11:51 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file is part of the Symfony package.
|
|
|
|
*
|
|
|
|
* (c) Fabien Potencier <fabien@symfony.com>
|
|
|
|
*
|
|
|
|
* For the full copyright and license information, please view the LICENSE
|
|
|
|
* file that was distributed with this source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace Symfony\Component\ExpressionLanguage\Tests;
|
|
|
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
use Symfony\Component\ExpressionLanguage\Lexer;
|
|
|
|
use Symfony\Component\ExpressionLanguage\Node;
|
2024-01-12 05:08:24 +00:00
|
|
|
use Symfony\Component\ExpressionLanguage\Parser;
|
2018-04-17 02:11:51 +00:00
|
|
|
|
|
|
|
class ParserTest extends TestCase
|
|
|
|
{
|
|
|
|
public function testParseWithInvalidName()
|
|
|
|
{
|
2024-01-12 05:08:24 +00:00
|
|
|
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
|
|
|
|
$this->expectExceptionMessage('Variable "foo" is not valid around position 1 for expression `foo`.');
|
2018-04-17 02:11:51 +00:00
|
|
|
$lexer = new Lexer();
|
2024-01-12 05:08:24 +00:00
|
|
|
$parser = new Parser([]);
|
2018-04-17 02:11:51 +00:00
|
|
|
$parser->parse($lexer->tokenize('foo'));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function testParseWithZeroInNames()
|
|
|
|
{
|
2024-01-12 05:08:24 +00:00
|
|
|
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
|
|
|
|
$this->expectExceptionMessage('Variable "foo" is not valid around position 1 for expression `foo`.');
|
2018-04-17 02:11:51 +00:00
|
|
|
$lexer = new Lexer();
|
2024-01-12 05:08:24 +00:00
|
|
|
$parser = new Parser([]);
|
|
|
|
$parser->parse($lexer->tokenize('foo'), [0]);
|
2018-04-17 02:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider getParseData
|
|
|
|
*/
|
2024-01-12 05:08:24 +00:00
|
|
|
public function testParse($node, $expression, $names = [])
|
2018-04-17 02:11:51 +00:00
|
|
|
{
|
|
|
|
$lexer = new Lexer();
|
2024-01-12 05:08:24 +00:00
|
|
|
$parser = new Parser([]);
|
2018-04-17 02:11:51 +00:00
|
|
|
$this->assertEquals($node, $parser->parse($lexer->tokenize($expression), $names));
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getParseData()
|
|
|
|
{
|
|
|
|
$arguments = new Node\ArgumentsNode();
|
|
|
|
$arguments->addElement(new Node\ConstantNode('arg1'));
|
|
|
|
$arguments->addElement(new Node\ConstantNode(2));
|
|
|
|
$arguments->addElement(new Node\ConstantNode(true));
|
|
|
|
|
2024-01-12 05:08:24 +00:00
|
|
|
$arrayNode = new Node\ArrayNode();
|
|
|
|
$arrayNode->addElement(new Node\NameNode('bar'));
|
|
|
|
|
|
|
|
return [
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\NameNode('a'),
|
|
|
|
'a',
|
2024-01-12 05:08:24 +00:00
|
|
|
['a'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\ConstantNode('a'),
|
|
|
|
'"a"',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\ConstantNode(3),
|
|
|
|
'3',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\ConstantNode(false),
|
|
|
|
'false',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\ConstantNode(true),
|
|
|
|
'true',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\ConstantNode(null),
|
|
|
|
'null',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\UnaryNode('-', new Node\ConstantNode(3)),
|
|
|
|
'-3',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)),
|
|
|
|
'3 - 3',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\BinaryNode('*',
|
|
|
|
new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)),
|
|
|
|
new Node\ConstantNode(2)
|
|
|
|
),
|
|
|
|
'(3 - 3) * 2',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::PROPERTY_CALL),
|
|
|
|
'foo.bar',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
|
|
|
|
'foo.bar()',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('not', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
|
|
|
|
'foo.not()',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\GetAttrNode(
|
|
|
|
new Node\NameNode('foo'),
|
|
|
|
new Node\ConstantNode('bar', true),
|
|
|
|
$arguments,
|
|
|
|
Node\GetAttrNode::METHOD_CALL
|
|
|
|
),
|
|
|
|
'foo.bar("arg1", 2, true)',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode(3), new Node\ArgumentsNode(), Node\GetAttrNode::ARRAY_CALL),
|
|
|
|
'foo[3]',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\ConditionalNode(new Node\ConstantNode(true), new Node\ConstantNode(true), new Node\ConstantNode(false)),
|
|
|
|
'true ? true : false',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\BinaryNode('matches', new Node\ConstantNode('foo'), new Node\ConstantNode('/foo/')),
|
|
|
|
'"foo" matches "/foo/"',
|
2024-01-12 05:08:24 +00:00
|
|
|
],
|
2018-04-17 02:11:51 +00:00
|
|
|
|
|
|
|
// chained calls
|
2024-01-12 05:08:24 +00:00
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
$this->createGetAttrNode(
|
|
|
|
$this->createGetAttrNode(
|
|
|
|
$this->createGetAttrNode(
|
|
|
|
$this->createGetAttrNode(new Node\NameNode('foo'), 'bar', Node\GetAttrNode::METHOD_CALL),
|
|
|
|
'foo', Node\GetAttrNode::METHOD_CALL),
|
|
|
|
'baz', Node\GetAttrNode::PROPERTY_CALL),
|
|
|
|
'3', Node\GetAttrNode::ARRAY_CALL),
|
|
|
|
'foo.bar().foo().baz[3]',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
2018-04-17 02:11:51 +00:00
|
|
|
|
2024-01-12 05:08:24 +00:00
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
new Node\NameNode('foo'),
|
|
|
|
'bar',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo' => 'bar'],
|
|
|
|
],
|
|
|
|
|
|
|
|
// Operators collisions
|
|
|
|
[
|
|
|
|
new Node\BinaryNode(
|
|
|
|
'in',
|
|
|
|
new Node\GetAttrNode(
|
|
|
|
new Node\NameNode('foo'),
|
|
|
|
new Node\ConstantNode('not', true),
|
|
|
|
new Node\ArgumentsNode(),
|
|
|
|
Node\GetAttrNode::PROPERTY_CALL
|
|
|
|
),
|
|
|
|
$arrayNode
|
|
|
|
),
|
|
|
|
'foo.not in [bar]',
|
|
|
|
['foo', 'bar'],
|
|
|
|
],
|
|
|
|
[
|
|
|
|
new Node\BinaryNode(
|
|
|
|
'or',
|
|
|
|
new Node\UnaryNode('not', new Node\NameNode('foo')),
|
|
|
|
new Node\GetAttrNode(
|
|
|
|
new Node\NameNode('foo'),
|
|
|
|
new Node\ConstantNode('not', true),
|
|
|
|
new Node\ArgumentsNode(),
|
|
|
|
Node\GetAttrNode::PROPERTY_CALL
|
|
|
|
)
|
|
|
|
),
|
|
|
|
'not foo or foo.not',
|
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
];
|
2018-04-17 02:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private function createGetAttrNode($node, $item, $type)
|
|
|
|
{
|
|
|
|
return new Node\GetAttrNode($node, new Node\ConstantNode($item, Node\GetAttrNode::ARRAY_CALL !== $type), new Node\ArgumentsNode(), $type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @dataProvider getInvalidPostfixData
|
|
|
|
*/
|
2024-01-12 05:08:24 +00:00
|
|
|
public function testParseWithInvalidPostfixData($expr, $names = [])
|
2018-04-17 02:11:51 +00:00
|
|
|
{
|
2024-01-12 05:08:24 +00:00
|
|
|
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
|
2018-04-17 02:11:51 +00:00
|
|
|
$lexer = new Lexer();
|
2024-01-12 05:08:24 +00:00
|
|
|
$parser = new Parser([]);
|
2018-04-17 02:11:51 +00:00
|
|
|
$parser->parse($lexer->tokenize($expr), $names);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getInvalidPostfixData()
|
|
|
|
{
|
2024-01-12 05:08:24 +00:00
|
|
|
return [
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
'foo."#"',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
'foo."bar"',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
'foo.**',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
[
|
2018-04-17 02:11:51 +00:00
|
|
|
'foo.123',
|
2024-01-12 05:08:24 +00:00
|
|
|
['foo'],
|
|
|
|
],
|
|
|
|
];
|
2018-04-17 02:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public function testNameProposal()
|
|
|
|
{
|
2024-01-12 05:08:24 +00:00
|
|
|
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
|
|
|
|
$this->expectExceptionMessage('Did you mean "baz"?');
|
2018-04-17 02:11:51 +00:00
|
|
|
$lexer = new Lexer();
|
2024-01-12 05:08:24 +00:00
|
|
|
$parser = new Parser([]);
|
2018-04-17 02:11:51 +00:00
|
|
|
|
2024-01-12 05:08:24 +00:00
|
|
|
$parser->parse($lexer->tokenize('foo > bar'), ['foo', 'baz']);
|
2018-04-17 02:11:51 +00:00
|
|
|
}
|
|
|
|
}
|