This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
<?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\ExpressionFunction;
/**
* Tests ExpressionFunction.
*
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class ExpressionFunctionTest extends TestCase
{
public function testFunctionDoesNotExist()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('PHP function "fn_does_not_exist" does not exist.');
ExpressionFunction::fromPhp('fn_does_not_exist');
}
public function testFunctionNamespaced()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('An expression function name must be defined when PHP function "Symfony\Component\ExpressionLanguage\Tests\fn_namespaced" is namespaced.');
ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\fn_namespaced');
}
}
function fn_namespaced()
{
}

View File

@@ -0,0 +1,308 @@
<?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\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
use Symfony\Component\ExpressionLanguage\Tests\Fixtures\TestProvider;
class ExpressionLanguageTest extends TestCase
{
public function testCachedParse()
{
$cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemPoolInterface')->getMock();
$cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock();
$savedParsedExpression = null;
$expressionLanguage = new ExpressionLanguage($cacheMock);
$cacheMock
->expects($this->exactly(2))
->method('getItem')
->with('1%20%2B%201%2F%2F')
->willReturn($cacheItemMock)
;
$cacheItemMock
->expects($this->exactly(2))
->method('get')
->willReturnCallback(function () use (&$savedParsedExpression) {
return $savedParsedExpression;
})
;
$cacheItemMock
->expects($this->exactly(1))
->method('set')
->with($this->isInstanceOf(ParsedExpression::class))
->willReturnCallback(function ($parsedExpression) use (&$savedParsedExpression) {
$savedParsedExpression = $parsedExpression;
})
;
$cacheMock
->expects($this->exactly(1))
->method('save')
->with($cacheItemMock)
;
$parsedExpression = $expressionLanguage->parse('1 + 1', []);
$this->assertSame($savedParsedExpression, $parsedExpression);
$parsedExpression = $expressionLanguage->parse('1 + 1', []);
$this->assertSame($savedParsedExpression, $parsedExpression);
}
/**
* @group legacy
*/
public function testCachedParseWithDeprecatedParserCacheInterface()
{
$cacheMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$savedParsedExpression = null;
$expressionLanguage = new ExpressionLanguage($cacheMock);
$cacheMock
->expects($this->exactly(1))
->method('fetch')
->with('1%20%2B%201%2F%2F')
->willReturn($savedParsedExpression)
;
$cacheMock
->expects($this->exactly(1))
->method('save')
->with('1%20%2B%201%2F%2F', $this->isInstanceOf(ParsedExpression::class))
->willReturnCallback(function ($key, $expression) use (&$savedParsedExpression) {
$savedParsedExpression = $expression;
})
;
$parsedExpression = $expressionLanguage->parse('1 + 1', []);
$this->assertSame($savedParsedExpression, $parsedExpression);
}
public function testWrongCacheImplementation()
{
$this->expectException('InvalidArgumentException');
$this->expectExceptionMessage('Cache argument has to implement "Psr\Cache\CacheItemPoolInterface".');
$cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemSpoolInterface')->getMock();
new ExpressionLanguage($cacheMock);
}
public function testConstantFunction()
{
$expressionLanguage = new ExpressionLanguage();
$this->assertEquals(\PHP_VERSION, $expressionLanguage->evaluate('constant("PHP_VERSION")'));
$expressionLanguage = new ExpressionLanguage();
$this->assertEquals('\constant("PHP_VERSION")', $expressionLanguage->compile('constant("PHP_VERSION")'));
}
public function testProviders()
{
$expressionLanguage = new ExpressionLanguage(null, [new TestProvider()]);
$this->assertEquals('foo', $expressionLanguage->evaluate('identity("foo")'));
$this->assertEquals('"foo"', $expressionLanguage->compile('identity("foo")'));
$this->assertEquals('FOO', $expressionLanguage->evaluate('strtoupper("foo")'));
$this->assertEquals('\strtoupper("foo")', $expressionLanguage->compile('strtoupper("foo")'));
$this->assertEquals('foo', $expressionLanguage->evaluate('strtolower("FOO")'));
$this->assertEquals('\strtolower("FOO")', $expressionLanguage->compile('strtolower("FOO")'));
$this->assertTrue($expressionLanguage->evaluate('fn_namespaced()'));
$this->assertEquals('\Symfony\Component\ExpressionLanguage\Tests\Fixtures\fn_namespaced()', $expressionLanguage->compile('fn_namespaced()'));
}
/**
* @dataProvider shortCircuitProviderEvaluate
*/
public function testShortCircuitOperatorsEvaluate($expression, array $values, $expected)
{
$expressionLanguage = new ExpressionLanguage();
$this->assertEquals($expected, $expressionLanguage->evaluate($expression, $values));
}
/**
* @dataProvider shortCircuitProviderCompile
*/
public function testShortCircuitOperatorsCompile($expression, array $names, $expected)
{
$result = null;
$expressionLanguage = new ExpressionLanguage();
eval(sprintf('$result = %s;', $expressionLanguage->compile($expression, $names)));
$this->assertSame($expected, $result);
}
public function testParseThrowsInsteadOfNotice()
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$this->expectExceptionMessage('Unexpected end of expression around position 6 for expression `node.`.');
$expressionLanguage = new ExpressionLanguage();
$expressionLanguage->parse('node.', ['node']);
}
public function shortCircuitProviderEvaluate()
{
$object = $this->getMockBuilder('stdClass')->setMethods(['foo'])->getMock();
$object->expects($this->never())->method('foo');
return [
['false and object.foo()', ['object' => $object], false],
['false && object.foo()', ['object' => $object], false],
['true || object.foo()', ['object' => $object], true],
['true or object.foo()', ['object' => $object], true],
];
}
public function shortCircuitProviderCompile()
{
return [
['false and foo', ['foo' => 'foo'], false],
['false && foo', ['foo' => 'foo'], false],
['true || foo', ['foo' => 'foo'], true],
['true or foo', ['foo' => 'foo'], true],
];
}
public function testCachingForOverriddenVariableNames()
{
$expressionLanguage = new ExpressionLanguage();
$expression = 'a + b';
$expressionLanguage->evaluate($expression, ['a' => 1, 'b' => 1]);
$result = $expressionLanguage->compile($expression, ['a', 'B' => 'b']);
$this->assertSame('($a + $B)', $result);
}
public function testStrictEquality()
{
$expressionLanguage = new ExpressionLanguage();
$expression = '123 === a';
$result = $expressionLanguage->compile($expression, ['a']);
$this->assertSame('(123 === $a)', $result);
}
public function testCachingWithDifferentNamesOrder()
{
$cacheMock = $this->getMockBuilder('Psr\Cache\CacheItemPoolInterface')->getMock();
$cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock();
$expressionLanguage = new ExpressionLanguage($cacheMock);
$savedParsedExpression = null;
$cacheMock
->expects($this->exactly(2))
->method('getItem')
->with('a%20%2B%20b%2F%2Fa%7CB%3Ab')
->willReturn($cacheItemMock)
;
$cacheItemMock
->expects($this->exactly(2))
->method('get')
->willReturnCallback(function () use (&$savedParsedExpression) {
return $savedParsedExpression;
})
;
$cacheItemMock
->expects($this->exactly(1))
->method('set')
->with($this->isInstanceOf(ParsedExpression::class))
->willReturnCallback(function ($parsedExpression) use (&$savedParsedExpression) {
$savedParsedExpression = $parsedExpression;
})
;
$cacheMock
->expects($this->exactly(1))
->method('save')
->with($cacheItemMock)
;
$expression = 'a + b';
$expressionLanguage->compile($expression, ['a', 'B' => 'b']);
$expressionLanguage->compile($expression, ['B' => 'b', 'a']);
}
public function testOperatorCollisions()
{
$expressionLanguage = new ExpressionLanguage();
$expression = 'foo.not in [bar]';
$compiled = $expressionLanguage->compile($expression, ['foo', 'bar']);
$this->assertSame('in_array($foo->not, [0 => $bar])', $compiled);
$result = $expressionLanguage->evaluate($expression, ['foo' => (object) ['not' => 'test'], 'bar' => 'test']);
$this->assertTrue($result);
}
/**
* @dataProvider getRegisterCallbacks
*/
public function testRegisterAfterParse($registerCallback)
{
$this->expectException('LogicException');
$el = new ExpressionLanguage();
$el->parse('1 + 1', []);
$registerCallback($el);
}
/**
* @dataProvider getRegisterCallbacks
*/
public function testRegisterAfterEval($registerCallback)
{
$this->expectException('LogicException');
$el = new ExpressionLanguage();
$el->evaluate('1 + 1');
$registerCallback($el);
}
public function testCallBadCallable()
{
$this->expectException('RuntimeException');
$this->expectExceptionMessageMatches('/Unable to call method "\w+" of object "\w+"./');
$el = new ExpressionLanguage();
$el->evaluate('foo.myfunction()', ['foo' => new \stdClass()]);
}
/**
* @dataProvider getRegisterCallbacks
*/
public function testRegisterAfterCompile($registerCallback)
{
$this->expectException('LogicException');
$el = new ExpressionLanguage();
$el->compile('1 + 1');
$registerCallback($el);
}
public function getRegisterCallbacks()
{
return [
[
function (ExpressionLanguage $el) {
$el->register('fn', function () {}, function () {});
},
],
[
function (ExpressionLanguage $el) {
$el->addFunction(new ExpressionFunction('fn', function () {}, function () {}));
},
],
[
function (ExpressionLanguage $el) {
$el->registerProvider(new TestProvider());
},
],
];
}
}

View File

@@ -0,0 +1,28 @@
<?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\Expression;
class ExpressionTest extends TestCase
{
public function testSerialization()
{
$expression = new Expression('kernel.boot()');
$serializedExpression = serialize($expression);
$unserializedExpression = unserialize($serializedExpression);
$this->assertEquals($expression, $unserializedExpression);
}
}

View File

@@ -0,0 +1,41 @@
<?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\Fixtures;
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
use Symfony\Component\ExpressionLanguage\ExpressionPhpFunction;
class TestProvider implements ExpressionFunctionProviderInterface
{
public function getFunctions()
{
return [
new ExpressionFunction('identity', function ($input) {
return $input;
}, function (array $values, $input) {
return $input;
}),
ExpressionFunction::fromPhp('strtoupper'),
ExpressionFunction::fromPhp('\strtolower'),
ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\Fixtures\fn_namespaced', 'fn_namespaced'),
];
}
}
function fn_namespaced()
{
return true;
}

View File

@@ -0,0 +1,129 @@
<?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\Token;
use Symfony\Component\ExpressionLanguage\TokenStream;
class LexerTest extends TestCase
{
/**
* @var Lexer
*/
private $lexer;
protected function setUp()
{
$this->lexer = new Lexer();
}
/**
* @dataProvider getTokenizeData
*/
public function testTokenize($tokens, $expression)
{
$tokens[] = new Token('end of expression', null, \strlen($expression) + 1);
$this->assertEquals(new TokenStream($tokens, $expression), $this->lexer->tokenize($expression));
}
public function testTokenizeThrowsErrorWithMessage()
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$this->expectExceptionMessage('Unexpected character "\'" around position 33 for expression `service(faulty.expression.example\').dummyMethod()`.');
$expression = "service(faulty.expression.example').dummyMethod()";
$this->lexer->tokenize($expression);
}
public function testTokenizeThrowsErrorOnUnclosedBrace()
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$this->expectExceptionMessage('Unclosed "(" around position 7 for expression `service(unclosed.expression.dummyMethod()`.');
$expression = 'service(unclosed.expression.dummyMethod()';
$this->lexer->tokenize($expression);
}
public function getTokenizeData()
{
return [
[
[new Token('name', 'a', 3)],
' a ',
],
[
[new Token('name', 'a', 1)],
'a',
],
[
[new Token('string', 'foo', 1)],
'"foo"',
],
[
[new Token('number', '3', 1)],
'3',
],
[
[new Token('operator', '+', 1)],
'+',
],
[
[new Token('punctuation', '.', 1)],
'.',
],
[
[
new Token('punctuation', '(', 1),
new Token('number', '3', 2),
new Token('operator', '+', 4),
new Token('number', '5', 6),
new Token('punctuation', ')', 7),
new Token('operator', '~', 9),
new Token('name', 'foo', 11),
new Token('punctuation', '(', 14),
new Token('string', 'bar', 15),
new Token('punctuation', ')', 20),
new Token('punctuation', '.', 21),
new Token('name', 'baz', 22),
new Token('punctuation', '[', 25),
new Token('number', '4', 26),
new Token('punctuation', ']', 27),
],
'(3 + 5) ~ foo("bar").baz[4]',
],
[
[new Token('operator', '..', 1)],
'..',
],
[
[new Token('string', '#foo', 1)],
"'#foo'",
],
[
[new Token('string', '#foo', 1)],
'"#foo"',
],
[
[
new Token('name', 'foo', 1),
new Token('punctuation', '.', 4),
new Token('name', 'not', 5),
new Token('operator', 'in', 9),
new Token('punctuation', '[', 12),
new Token('name', 'bar', 13),
new Token('punctuation', ']', 16),
],
'foo.not in [bar]',
],
];
}
}

View File

@@ -0,0 +1,50 @@
<?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\Node;
use PHPUnit\Framework\TestCase;
use Symfony\Component\ExpressionLanguage\Compiler;
abstract class AbstractNodeTest extends TestCase
{
/**
* @dataProvider getEvaluateData
*/
public function testEvaluate($expected, $node, $variables = [], $functions = [])
{
$this->assertSame($expected, $node->evaluate($functions, $variables));
}
abstract public function getEvaluateData();
/**
* @dataProvider getCompileData
*/
public function testCompile($expected, $node, $functions = [])
{
$compiler = new Compiler($functions);
$node->compile($compiler);
$this->assertSame($expected, $compiler->getSource());
}
abstract public function getCompileData();
/**
* @dataProvider getDumpData
*/
public function testDump($expected, $node)
{
$this->assertSame($expected, $node->dump());
}
abstract public function getDumpData();
}

View File

@@ -0,0 +1,36 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ArgumentsNode;
class ArgumentsNodeTest extends ArrayNodeTest
{
public function getCompileData()
{
return [
['"a", "b"', $this->getArrayNode()],
];
}
public function getDumpData()
{
return [
['"a", "b"', $this->getArrayNode()],
];
}
protected function createArrayNode()
{
return new ArgumentsNode();
}
}

View File

@@ -0,0 +1,73 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ArrayNode;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
class ArrayNodeTest extends AbstractNodeTest
{
public function testSerialization()
{
$node = $this->createArrayNode();
$node->addElement(new ConstantNode('foo'));
$serializedNode = serialize($node);
$unserializedNode = unserialize($serializedNode);
$this->assertEquals($node, $unserializedNode);
$this->assertNotEquals($this->createArrayNode(), $unserializedNode);
}
public function getEvaluateData()
{
return [
[['b' => 'a', 'b'], $this->getArrayNode()],
];
}
public function getCompileData()
{
return [
['["b" => "a", 0 => "b"]', $this->getArrayNode()],
];
}
public function getDumpData()
{
yield ['{"b": "a", 0: "b"}', $this->getArrayNode()];
$array = $this->createArrayNode();
$array->addElement(new ConstantNode('c'), new ConstantNode('a"b'));
$array->addElement(new ConstantNode('d'), new ConstantNode('a\b'));
yield ['{"a\\"b": "c", "a\\\\b": "d"}', $array];
$array = $this->createArrayNode();
$array->addElement(new ConstantNode('c'));
$array->addElement(new ConstantNode('d'));
yield ['["c", "d"]', $array];
}
protected function getArrayNode()
{
$array = $this->createArrayNode();
$array->addElement(new ConstantNode('a'), new ConstantNode('b'));
$array->addElement(new ConstantNode('b'));
return $array;
}
protected function createArrayNode()
{
return new ArrayNode();
}
}

View File

@@ -0,0 +1,166 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ArrayNode;
use Symfony\Component\ExpressionLanguage\Node\BinaryNode;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
class BinaryNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
$array = new ArrayNode();
$array->addElement(new ConstantNode('a'));
$array->addElement(new ConstantNode('b'));
return [
[true, new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))],
[true, new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))],
[false, new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))],
[false, new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))],
[0, new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
[6, new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
[6, new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
[true, new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
[true, new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
[true, new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))],
[false, new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))],
[false, new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))],
[true, new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))],
[true, new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))],
[false, new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))],
[false, new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))],
[true, new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))],
[-1, new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))],
[3, new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))],
[4, new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))],
[1, new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))],
[1, new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))],
[25, new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))],
['ab', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))],
[true, new BinaryNode('in', new ConstantNode('a'), $array)],
[false, new BinaryNode('in', new ConstantNode('c'), $array)],
[true, new BinaryNode('not in', new ConstantNode('c'), $array)],
[false, new BinaryNode('not in', new ConstantNode('a'), $array)],
[[1, 2, 3], new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))],
[1, new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+$/'))],
];
}
public function getCompileData()
{
$array = new ArrayNode();
$array->addElement(new ConstantNode('a'));
$array->addElement(new ConstantNode('b'));
return [
['(true || false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))],
['(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))],
['(true && false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))],
['(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))],
['(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
['(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
['(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
['(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
['(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
['(1 <= 1)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))],
['(1 > 2)', new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))],
['(1 >= 2)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))],
['(1 >= 1)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))],
['(true === true)', new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))],
['(true !== true)', new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))],
['(2 == 1)', new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))],
['(2 != 1)', new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))],
['(1 - 2)', new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))],
['(1 + 2)', new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))],
['(2 * 2)', new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))],
['(2 / 2)', new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))],
['(5 % 2)', new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))],
['pow(5, 2)', new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))],
['("a" . "b")', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))],
['in_array("a", [0 => "a", 1 => "b"])', new BinaryNode('in', new ConstantNode('a'), $array)],
['in_array("c", [0 => "a", 1 => "b"])', new BinaryNode('in', new ConstantNode('c'), $array)],
['!in_array("c", [0 => "a", 1 => "b"])', new BinaryNode('not in', new ConstantNode('c'), $array)],
['!in_array("a", [0 => "a", 1 => "b"])', new BinaryNode('not in', new ConstantNode('a'), $array)],
['range(1, 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))],
['preg_match("/^[a-z]+/i\$/", "abc")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))],
];
}
public function getDumpData()
{
$array = new ArrayNode();
$array->addElement(new ConstantNode('a'));
$array->addElement(new ConstantNode('b'));
return [
['(true or false)', new BinaryNode('or', new ConstantNode(true), new ConstantNode(false))],
['(true || false)', new BinaryNode('||', new ConstantNode(true), new ConstantNode(false))],
['(true and false)', new BinaryNode('and', new ConstantNode(true), new ConstantNode(false))],
['(true && false)', new BinaryNode('&&', new ConstantNode(true), new ConstantNode(false))],
['(2 & 4)', new BinaryNode('&', new ConstantNode(2), new ConstantNode(4))],
['(2 | 4)', new BinaryNode('|', new ConstantNode(2), new ConstantNode(4))],
['(2 ^ 4)', new BinaryNode('^', new ConstantNode(2), new ConstantNode(4))],
['(1 < 2)', new BinaryNode('<', new ConstantNode(1), new ConstantNode(2))],
['(1 <= 2)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(2))],
['(1 <= 1)', new BinaryNode('<=', new ConstantNode(1), new ConstantNode(1))],
['(1 > 2)', new BinaryNode('>', new ConstantNode(1), new ConstantNode(2))],
['(1 >= 2)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(2))],
['(1 >= 1)', new BinaryNode('>=', new ConstantNode(1), new ConstantNode(1))],
['(true === true)', new BinaryNode('===', new ConstantNode(true), new ConstantNode(true))],
['(true !== true)', new BinaryNode('!==', new ConstantNode(true), new ConstantNode(true))],
['(2 == 1)', new BinaryNode('==', new ConstantNode(2), new ConstantNode(1))],
['(2 != 1)', new BinaryNode('!=', new ConstantNode(2), new ConstantNode(1))],
['(1 - 2)', new BinaryNode('-', new ConstantNode(1), new ConstantNode(2))],
['(1 + 2)', new BinaryNode('+', new ConstantNode(1), new ConstantNode(2))],
['(2 * 2)', new BinaryNode('*', new ConstantNode(2), new ConstantNode(2))],
['(2 / 2)', new BinaryNode('/', new ConstantNode(2), new ConstantNode(2))],
['(5 % 2)', new BinaryNode('%', new ConstantNode(5), new ConstantNode(2))],
['(5 ** 2)', new BinaryNode('**', new ConstantNode(5), new ConstantNode(2))],
['("a" ~ "b")', new BinaryNode('~', new ConstantNode('a'), new ConstantNode('b'))],
['("a" in ["a", "b"])', new BinaryNode('in', new ConstantNode('a'), $array)],
['("c" in ["a", "b"])', new BinaryNode('in', new ConstantNode('c'), $array)],
['("c" not in ["a", "b"])', new BinaryNode('not in', new ConstantNode('c'), $array)],
['("a" not in ["a", "b"])', new BinaryNode('not in', new ConstantNode('a'), $array)],
['(1 .. 3)', new BinaryNode('..', new ConstantNode(1), new ConstantNode(3))],
['("abc" matches "/^[a-z]+/i$/")', new BinaryNode('matches', new ConstantNode('abc'), new ConstantNode('/^[a-z]+/i$/'))],
];
}
}

View File

@@ -0,0 +1,42 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ConditionalNode;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
class ConditionalNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
return [
[1, new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))],
[2, new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))],
];
}
public function getCompileData()
{
return [
['((true) ? (1) : (2))', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))],
['((false) ? (1) : (2))', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))],
];
}
public function getDumpData()
{
return [
['(true ? 1 : 2)', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))],
['(false ? 1 : 2)', new ConditionalNode(new ConstantNode(false), new ConstantNode(1), new ConstantNode(2))],
];
}
}

View File

@@ -0,0 +1,60 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
class ConstantNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
return [
[false, new ConstantNode(false)],
[true, new ConstantNode(true)],
[null, new ConstantNode(null)],
[3, new ConstantNode(3)],
[3.3, new ConstantNode(3.3)],
['foo', new ConstantNode('foo')],
[[1, 'b' => 'a'], new ConstantNode([1, 'b' => 'a'])],
];
}
public function getCompileData()
{
return [
['false', new ConstantNode(false)],
['true', new ConstantNode(true)],
['null', new ConstantNode(null)],
['3', new ConstantNode(3)],
['3.3', new ConstantNode(3.3)],
['"foo"', new ConstantNode('foo')],
['[0 => 1, "b" => "a"]', new ConstantNode([1, 'b' => 'a'])],
];
}
public function getDumpData()
{
return [
['false', new ConstantNode(false)],
['true', new ConstantNode(true)],
['null', new ConstantNode(null)],
['3', new ConstantNode(3)],
['3.3', new ConstantNode(3.3)],
['"foo"', new ConstantNode('foo')],
['foo', new ConstantNode('foo', true)],
['{0: 1, "b": "a", 1: true}', new ConstantNode([1, 'b' => 'a', true])],
['{"a\\"b": "c", "a\\\\b": "d"}', new ConstantNode(['a"b' => 'c', 'a\\b' => 'd'])],
['["c", "d"]', new ConstantNode(['c', 'd'])],
['{"a": ["b"]}', new ConstantNode(['a' => ['b']])],
];
}
}

View File

@@ -0,0 +1,52 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
use Symfony\Component\ExpressionLanguage\Node\FunctionNode;
use Symfony\Component\ExpressionLanguage\Node\Node;
class FunctionNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
return [
['bar', new FunctionNode('foo', new Node([new ConstantNode('bar')])), [], ['foo' => $this->getCallables()]],
];
}
public function getCompileData()
{
return [
['foo("bar")', new FunctionNode('foo', new Node([new ConstantNode('bar')])), ['foo' => $this->getCallables()]],
];
}
public function getDumpData()
{
return [
['foo("bar")', new FunctionNode('foo', new Node([new ConstantNode('bar')])), ['foo' => $this->getCallables()]],
];
}
protected function getCallables()
{
return [
'compiler' => function ($arg) {
return sprintf('foo(%s)', $arg);
},
'evaluator' => function ($variables, $arg) {
return $arg;
},
];
}
}

View File

@@ -0,0 +1,78 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ArrayNode;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
use Symfony\Component\ExpressionLanguage\Node\GetAttrNode;
use Symfony\Component\ExpressionLanguage\Node\NameNode;
class GetAttrNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
return [
['b', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), ['foo' => ['b' => 'a', 'b']]],
['a', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), ['foo' => ['b' => 'a', 'b']]],
['bar', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), ['foo' => new Obj()]],
['baz', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), ['foo' => new Obj()]],
['a', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL), ['foo' => ['b' => 'a', 'b'], 'index' => 'b']],
];
}
public function getCompileData()
{
return [
['$foo[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)],
['$foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)],
['$foo->foo', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), ['foo' => new Obj()]],
['$foo->foo(["b" => "a", 0 => "b"])', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), ['foo' => new Obj()]],
['$foo[$index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)],
];
}
public function getDumpData()
{
return [
['foo[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)],
['foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)],
['foo.foo', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), ['foo' => new Obj()]],
['foo.foo({"b": "a", 0: "b"})', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::METHOD_CALL), ['foo' => new Obj()]],
['foo[index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), $this->getArrayNode(), GetAttrNode::ARRAY_CALL)],
];
}
protected function getArrayNode()
{
$array = new ArrayNode();
$array->addElement(new ConstantNode('a'), new ConstantNode('b'));
$array->addElement(new ConstantNode('b'));
return $array;
}
}
class Obj
{
public $foo = 'bar';
public function foo()
{
return 'baz';
}
}

View File

@@ -0,0 +1,38 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\NameNode;
class NameNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
return [
['bar', new NameNode('foo'), ['foo' => 'bar']],
];
}
public function getCompileData()
{
return [
['$foo', new NameNode('foo')],
];
}
public function getDumpData()
{
return [
['foo', new NameNode('foo')],
];
}
}

View File

@@ -0,0 +1,41 @@
<?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\Node;
use PHPUnit\Framework\TestCase;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
use Symfony\Component\ExpressionLanguage\Node\Node;
class NodeTest extends TestCase
{
public function testToString()
{
$node = new Node([new ConstantNode('foo')]);
$this->assertEquals(<<<'EOF'
Node(
ConstantNode(value: 'foo')
)
EOF
, (string) $node);
}
public function testSerialization()
{
$node = new Node(['foo' => 'bar'], ['bar' => 'foo']);
$serializedNode = serialize($node);
$unserializedNode = unserialize($serializedNode);
$this->assertEquals($node, $unserializedNode);
}
}

View File

@@ -0,0 +1,48 @@
<?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\Node;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
use Symfony\Component\ExpressionLanguage\Node\UnaryNode;
class UnaryNodeTest extends AbstractNodeTest
{
public function getEvaluateData()
{
return [
[-1, new UnaryNode('-', new ConstantNode(1))],
[3, new UnaryNode('+', new ConstantNode(3))],
[false, new UnaryNode('!', new ConstantNode(true))],
[false, new UnaryNode('not', new ConstantNode(true))],
];
}
public function getCompileData()
{
return [
['(-1)', new UnaryNode('-', new ConstantNode(1))],
['(+3)', new UnaryNode('+', new ConstantNode(3))],
['(!true)', new UnaryNode('!', new ConstantNode(true))],
['(!true)', new UnaryNode('not', new ConstantNode(true))],
];
}
public function getDumpData()
{
return [
['(- 1)', new UnaryNode('-', new ConstantNode(1))],
['(+ 3)', new UnaryNode('+', new ConstantNode(3))],
['(! true)', new UnaryNode('!', new ConstantNode(true))],
['(not true)', new UnaryNode('not', new ConstantNode(true))],
];
}
}

View File

@@ -0,0 +1,29 @@
<?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\Node\ConstantNode;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
class ParsedExpressionTest extends TestCase
{
public function testSerialization()
{
$expression = new ParsedExpression('25', new ConstantNode('25'));
$serializedExpression = serialize($expression);
$unserializedExpression = unserialize($serializedExpression);
$this->assertEquals($expression, $unserializedExpression);
}
}

View File

@@ -0,0 +1,140 @@
<?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\Node\Node;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
use Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheAdapter;
/**
* @group legacy
*/
class ParserCacheAdapterTest extends TestCase
{
public function testGetItem()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$key = 'key';
$value = 'value';
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$poolMock
->expects($this->once())
->method('fetch')
->with($key)
->willReturn($value)
;
$cacheItem = $parserCacheAdapter->getItem($key);
$this->assertEquals($value, $cacheItem->get());
$this->assertTrue($cacheItem->isHit());
}
public function testSave()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock();
$key = 'key';
$value = new ParsedExpression('1 + 1', new Node([], []));
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$poolMock
->expects($this->once())
->method('save')
->with($key, $value)
;
$cacheItemMock
->expects($this->once())
->method('getKey')
->willReturn($key)
;
$cacheItemMock
->expects($this->once())
->method('get')
->willReturn($value)
;
$parserCacheAdapter->save($cacheItemMock);
}
public function testGetItems()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->getItems();
}
public function testHasItem()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$key = 'key';
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->hasItem($key);
}
public function testClear()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->clear();
}
public function testDeleteItem()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$key = 'key';
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->deleteItem($key);
}
public function testDeleteItems()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$keys = ['key'];
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->deleteItems($keys);
}
public function testSaveDeferred()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$cacheItemMock = $this->getMockBuilder('Psr\Cache\CacheItemInterface')->getMock();
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->saveDeferred($cacheItemMock);
}
public function testCommit()
{
$poolMock = $this->getMockBuilder('Symfony\Component\ExpressionLanguage\ParserCache\ParserCacheInterface')->getMock();
$parserCacheAdapter = new ParserCacheAdapter($poolMock);
$this->expectException(\BadMethodCallException::class);
$parserCacheAdapter->commit();
}
}

View File

@@ -0,0 +1,237 @@
<?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;
use Symfony\Component\ExpressionLanguage\Parser;
class ParserTest extends TestCase
{
public function testParseWithInvalidName()
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$this->expectExceptionMessage('Variable "foo" is not valid around position 1 for expression `foo`.');
$lexer = new Lexer();
$parser = new Parser([]);
$parser->parse($lexer->tokenize('foo'));
}
public function testParseWithZeroInNames()
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$this->expectExceptionMessage('Variable "foo" is not valid around position 1 for expression `foo`.');
$lexer = new Lexer();
$parser = new Parser([]);
$parser->parse($lexer->tokenize('foo'), [0]);
}
/**
* @dataProvider getParseData
*/
public function testParse($node, $expression, $names = [])
{
$lexer = new Lexer();
$parser = new Parser([]);
$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));
$arrayNode = new Node\ArrayNode();
$arrayNode->addElement(new Node\NameNode('bar'));
return [
[
new Node\NameNode('a'),
'a',
['a'],
],
[
new Node\ConstantNode('a'),
'"a"',
],
[
new Node\ConstantNode(3),
'3',
],
[
new Node\ConstantNode(false),
'false',
],
[
new Node\ConstantNode(true),
'true',
],
[
new Node\ConstantNode(null),
'null',
],
[
new Node\UnaryNode('-', new Node\ConstantNode(3)),
'-3',
],
[
new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)),
'3 - 3',
],
[
new Node\BinaryNode('*',
new Node\BinaryNode('-', new Node\ConstantNode(3), new Node\ConstantNode(3)),
new Node\ConstantNode(2)
),
'(3 - 3) * 2',
],
[
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::PROPERTY_CALL),
'foo.bar',
['foo'],
],
[
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('bar', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
'foo.bar()',
['foo'],
],
[
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode('not', true), new Node\ArgumentsNode(), Node\GetAttrNode::METHOD_CALL),
'foo.not()',
['foo'],
],
[
new Node\GetAttrNode(
new Node\NameNode('foo'),
new Node\ConstantNode('bar', true),
$arguments,
Node\GetAttrNode::METHOD_CALL
),
'foo.bar("arg1", 2, true)',
['foo'],
],
[
new Node\GetAttrNode(new Node\NameNode('foo'), new Node\ConstantNode(3), new Node\ArgumentsNode(), Node\GetAttrNode::ARRAY_CALL),
'foo[3]',
['foo'],
],
[
new Node\ConditionalNode(new Node\ConstantNode(true), new Node\ConstantNode(true), new Node\ConstantNode(false)),
'true ? true : false',
],
[
new Node\BinaryNode('matches', new Node\ConstantNode('foo'), new Node\ConstantNode('/foo/')),
'"foo" matches "/foo/"',
],
// chained calls
[
$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]',
['foo'],
],
[
new Node\NameNode('foo'),
'bar',
['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'],
],
];
}
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
*/
public function testParseWithInvalidPostfixData($expr, $names = [])
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$lexer = new Lexer();
$parser = new Parser([]);
$parser->parse($lexer->tokenize($expr), $names);
}
public function getInvalidPostfixData()
{
return [
[
'foo."#"',
['foo'],
],
[
'foo."bar"',
['foo'],
],
[
'foo.**',
['foo'],
],
[
'foo.123',
['foo'],
],
];
}
public function testNameProposal()
{
$this->expectException('Symfony\Component\ExpressionLanguage\SyntaxError');
$this->expectExceptionMessage('Did you mean "baz"?');
$lexer = new Lexer();
$parser = new Parser([]);
$parser->parse($lexer->tokenize('foo > bar'), ['foo', 'baz']);
}
}