diff --git a/LICENSE b/LICENSE index 0083704..0138f8f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Node/GetAttrNode.php b/Node/GetAttrNode.php index e9b10c3..c348a3d 100644 --- a/Node/GetAttrNode.php +++ b/Node/GetAttrNode.php @@ -151,7 +151,7 @@ public function toArray() } /** - * Provides BC with instances serialized before v6.2 + * Provides BC with instances serialized before v6.2. */ public function __unserialize(array $data): void { diff --git a/Tests/ExpressionLanguageTest.php b/Tests/ExpressionLanguageTest.php index c5dc30c..0f8f96e 100644 --- a/Tests/ExpressionLanguageTest.php +++ b/Tests/ExpressionLanguageTest.php @@ -119,7 +119,7 @@ public function testParseThrowsInsteadOfNotice() $expressionLanguage->parse('node.', ['node']); } - public function shortCircuitProviderEvaluate() + public static function shortCircuitProviderEvaluate() { $object = new class(\Closure::fromCallable([static::class, 'fail'])) { private $fail; @@ -143,7 +143,7 @@ public function foo() ]; } - public function shortCircuitProviderCompile() + public static function shortCircuitProviderCompile() { return [ ['false and foo', ['foo' => 'foo'], false], @@ -266,7 +266,7 @@ public function testNullSafeCompile($expression, $foo) $this->assertNull(eval(sprintf('return %s;', $expressionLanguage->compile($expression, ['foo' => 'foo'])))); } - public function provideNullSafe() + public static function provideNullSafe() { $foo = new class() extends \stdClass { public function bar() @@ -315,11 +315,24 @@ public function testNullSafeCompileFails($expression, $foo) { $expressionLanguage = new ExpressionLanguage(); - $this->expectWarning(); - eval(sprintf('return %s;', $expressionLanguage->compile($expression, ['foo' => 'foo']))); + $this->expectException(\ErrorException::class); + + set_error_handler(static function (int $errno, string $errstr, string $errfile = null, int $errline = null): bool { + if ($errno & (\E_WARNING | \E_USER_WARNING) && (str_contains($errstr, 'Attempt to read property') || str_contains($errstr, 'Trying to access'))) { + throw new \ErrorException($errstr, 0, $errno, $errfile, $errline); + } + + return false; + }); + + try { + eval(sprintf('return %s;', $expressionLanguage->compile($expression, ['foo' => 'foo']))); + } finally { + restore_error_handler(); + } } - public function provideInvalidNullSafe() + public static function provideInvalidNullSafe() { yield ['foo?.bar.baz', (object) ['bar' => null], 'Unable to get property "baz" of non-object "foo.bar".']; yield ['foo?.bar["baz"]', (object) ['bar' => null], 'Unable to get an item of non-array "foo.bar".']; @@ -344,7 +357,7 @@ public function testNullCoalescingCompile($expression, $foo) $this->assertSame(eval(sprintf('return %s;', $expressionLanguage->compile($expression, ['foo' => 'foo']))), 'default'); } - public function provideNullCoalescing() + public static function provideNullCoalescing() { $foo = new class() extends \stdClass { public function bar() @@ -375,7 +388,7 @@ public function testRegisterAfterCompile($registerCallback) $registerCallback($el); } - public function getRegisterCallbacks() + public static function getRegisterCallbacks() { return [ [ diff --git a/Tests/LexerTest.php b/Tests/LexerTest.php index b0efa2b..64f060d 100644 --- a/Tests/LexerTest.php +++ b/Tests/LexerTest.php @@ -54,7 +54,7 @@ public function testTokenizeThrowsErrorOnUnclosedBrace() $this->lexer->tokenize($expression); } - public function getTokenizeData() + public static function getTokenizeData() { return [ [ diff --git a/Tests/Node/AbstractNodeTest.php b/Tests/Node/AbstractNodeTestCase.php similarity index 83% rename from Tests/Node/AbstractNodeTest.php rename to Tests/Node/AbstractNodeTestCase.php index 2975037..7521788 100644 --- a/Tests/Node/AbstractNodeTest.php +++ b/Tests/Node/AbstractNodeTestCase.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\ExpressionLanguage\Compiler; -abstract class AbstractNodeTest extends TestCase +abstract class AbstractNodeTestCase extends TestCase { /** * @dataProvider getEvaluateData @@ -24,7 +24,7 @@ public function testEvaluate($expected, $node, $variables = [], $functions = []) $this->assertSame($expected, $node->evaluate($functions, $variables)); } - abstract public function getEvaluateData(); + abstract public static function getEvaluateData(); /** * @dataProvider getCompileData @@ -36,7 +36,7 @@ public function testCompile($expected, $node, $functions = []) $this->assertSame($expected, $compiler->getSource()); } - abstract public function getCompileData(); + abstract public static function getCompileData(); /** * @dataProvider getDumpData @@ -46,5 +46,5 @@ public function testDump($expected, $node) $this->assertSame($expected, $node->dump()); } - abstract public function getDumpData(); + abstract public static function getDumpData(); } diff --git a/Tests/Node/ArgumentsNodeTest.php b/Tests/Node/ArgumentsNodeTest.php index 9d88b4a..2b25073 100644 --- a/Tests/Node/ArgumentsNodeTest.php +++ b/Tests/Node/ArgumentsNodeTest.php @@ -15,21 +15,21 @@ class ArgumentsNodeTest extends ArrayNodeTest { - public function getCompileData() + public static function getCompileData(): array { return [ - ['"a", "b"', $this->getArrayNode()], + ['"a", "b"', static::getArrayNode()], ]; } - public function getDumpData() + public static function getDumpData(): \Generator { - return [ - ['"a", "b"', $this->getArrayNode()], + yield from [ + ['"a", "b"', static::getArrayNode()], ]; } - protected function createArrayNode() + protected static function createArrayNode(): \Symfony\Component\ExpressionLanguage\Node\ArrayNode { return new ArgumentsNode(); } diff --git a/Tests/Node/ArrayNodeTest.php b/Tests/Node/ArrayNodeTest.php index 3d03d83..fcd7636 100644 --- a/Tests/Node/ArrayNodeTest.php +++ b/Tests/Node/ArrayNodeTest.php @@ -14,7 +14,7 @@ use Symfony\Component\ExpressionLanguage\Node\ArrayNode; use Symfony\Component\ExpressionLanguage\Node\ConstantNode; -class ArrayNodeTest extends AbstractNodeTest +class ArrayNodeTest extends AbstractNodeTestCase { public function testSerialization() { @@ -28,45 +28,45 @@ public function testSerialization() $this->assertNotEquals($this->createArrayNode(), $unserializedNode); } - public function getEvaluateData() + public static function getEvaluateData(): array { return [ - [['b' => 'a', 'b'], $this->getArrayNode()], + [['b' => 'a', 'b'], static::getArrayNode()], ]; } - public function getCompileData() + public static function getCompileData(): array { return [ - ['["b" => "a", 0 => "b"]', $this->getArrayNode()], + ['["b" => "a", 0 => "b"]', static::getArrayNode()], ]; } - public function getDumpData() + public static function getDumpData(): \Generator { - yield ['{"b": "a", 0: "b"}', $this->getArrayNode()]; + yield ['{"b": "a", 0: "b"}', static::getArrayNode()]; - $array = $this->createArrayNode(); + $array = static::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 = static::createArrayNode(); $array->addElement(new ConstantNode('c')); $array->addElement(new ConstantNode('d')); yield ['["c", "d"]', $array]; } - protected function getArrayNode() + protected static function getArrayNode(): ArrayNode { - $array = $this->createArrayNode(); + $array = static::createArrayNode(); $array->addElement(new ConstantNode('a'), new ConstantNode('b')); $array->addElement(new ConstantNode('b')); return $array; } - protected function createArrayNode() + protected static function createArrayNode(): ArrayNode { return new ArrayNode(); } diff --git a/Tests/Node/BinaryNodeTest.php b/Tests/Node/BinaryNodeTest.php index 9e7ec6e..365f07a 100644 --- a/Tests/Node/BinaryNodeTest.php +++ b/Tests/Node/BinaryNodeTest.php @@ -18,9 +18,9 @@ use Symfony\Component\ExpressionLanguage\Node\NameNode; use Symfony\Component\ExpressionLanguage\SyntaxError; -class BinaryNodeTest extends AbstractNodeTest +class BinaryNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { $array = new ArrayNode(); $array->addElement(new ConstantNode('a')); @@ -75,7 +75,7 @@ public function getEvaluateData() ]; } - public function getCompileData() + public static function getCompileData(): array { $array = new ArrayNode(); $array->addElement(new ConstantNode('a')); @@ -127,7 +127,7 @@ public function getCompileData() ]; } - public function getDumpData() + public static function getDumpData(): array { $array = new ArrayNode(); $array->addElement(new ConstantNode('a')); diff --git a/Tests/Node/ConditionalNodeTest.php b/Tests/Node/ConditionalNodeTest.php index 13b7cbd..4555429 100644 --- a/Tests/Node/ConditionalNodeTest.php +++ b/Tests/Node/ConditionalNodeTest.php @@ -14,9 +14,9 @@ use Symfony\Component\ExpressionLanguage\Node\ConditionalNode; use Symfony\Component\ExpressionLanguage\Node\ConstantNode; -class ConditionalNodeTest extends AbstractNodeTest +class ConditionalNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { return [ [1, new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))], @@ -24,7 +24,7 @@ public function getEvaluateData() ]; } - public function getCompileData() + public static function getCompileData(): array { return [ ['((true) ? (1) : (2))', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))], @@ -32,7 +32,7 @@ public function getCompileData() ]; } - public function getDumpData() + public static function getDumpData(): array { return [ ['(true ? 1 : 2)', new ConditionalNode(new ConstantNode(true), new ConstantNode(1), new ConstantNode(2))], diff --git a/Tests/Node/ConstantNodeTest.php b/Tests/Node/ConstantNodeTest.php index fee9f5b..f1b1004 100644 --- a/Tests/Node/ConstantNodeTest.php +++ b/Tests/Node/ConstantNodeTest.php @@ -13,9 +13,9 @@ use Symfony\Component\ExpressionLanguage\Node\ConstantNode; -class ConstantNodeTest extends AbstractNodeTest +class ConstantNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { return [ [false, new ConstantNode(false)], @@ -28,7 +28,7 @@ public function getEvaluateData() ]; } - public function getCompileData() + public static function getCompileData(): array { return [ ['false', new ConstantNode(false)], @@ -41,7 +41,7 @@ public function getCompileData() ]; } - public function getDumpData() + public static function getDumpData(): array { return [ ['false', new ConstantNode(false)], diff --git a/Tests/Node/FunctionNodeTest.php b/Tests/Node/FunctionNodeTest.php index c6cb02c..2ae6f95 100644 --- a/Tests/Node/FunctionNodeTest.php +++ b/Tests/Node/FunctionNodeTest.php @@ -15,30 +15,30 @@ use Symfony\Component\ExpressionLanguage\Node\FunctionNode; use Symfony\Component\ExpressionLanguage\Node\Node; -class FunctionNodeTest extends AbstractNodeTest +class FunctionNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { return [ - ['bar', new FunctionNode('foo', new Node([new ConstantNode('bar')])), [], ['foo' => $this->getCallables()]], + ['bar', new FunctionNode('foo', new Node([new ConstantNode('bar')])), [], ['foo' => static::getCallables()]], ]; } - public function getCompileData() + public static function getCompileData(): array { return [ - ['foo("bar")', new FunctionNode('foo', new Node([new ConstantNode('bar')])), ['foo' => $this->getCallables()]], + ['foo("bar")', new FunctionNode('foo', new Node([new ConstantNode('bar')])), ['foo' => static::getCallables()]], ]; } - public function getDumpData() + public static function getDumpData(): array { return [ - ['foo("bar")', new FunctionNode('foo', new Node([new ConstantNode('bar')])), ['foo' => $this->getCallables()]], + ['foo("bar")', new FunctionNode('foo', new Node([new ConstantNode('bar')])), ['foo' => static::getCallables()]], ]; } - protected function getCallables() + protected static function getCallables(): array { return [ 'compiler' => function ($arg) { diff --git a/Tests/Node/GetAttrNodeTest.php b/Tests/Node/GetAttrNodeTest.php index c790f33..6d81a2b 100644 --- a/Tests/Node/GetAttrNodeTest.php +++ b/Tests/Node/GetAttrNodeTest.php @@ -16,48 +16,48 @@ use Symfony\Component\ExpressionLanguage\Node\GetAttrNode; use Symfony\Component\ExpressionLanguage\Node\NameNode; -class GetAttrNodeTest extends AbstractNodeTest +class GetAttrNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { 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']]], + ['b', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), self::getArrayNode(), GetAttrNode::ARRAY_CALL), ['foo' => ['b' => 'a', 'b']]], + ['a', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), self::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()]], + ['bar', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), self::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']], + ['baz', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), self::getArrayNode(), GetAttrNode::METHOD_CALL), ['foo' => new Obj()]], + ['a', new GetAttrNode(new NameNode('foo'), new NameNode('index'), self::getArrayNode(), GetAttrNode::ARRAY_CALL), ['foo' => ['b' => 'a', 'b'], 'index' => 'b']], ]; } - public function getCompileData() + public static function getCompileData(): array { 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[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), self::getArrayNode(), GetAttrNode::ARRAY_CALL)], + ['$foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), self::getArrayNode(), GetAttrNode::ARRAY_CALL)], - ['$foo->foo', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), ['foo' => new Obj()]], + ['$foo->foo', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), self::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)], + ['$foo->foo(["b" => "a", 0 => "b"])', new GetAttrNode(new NameNode('foo'), new ConstantNode('foo'), self::getArrayNode(), GetAttrNode::METHOD_CALL), ['foo' => new Obj()]], + ['$foo[$index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), self::getArrayNode(), GetAttrNode::ARRAY_CALL)], ]; } - public function getDumpData() + public static function getDumpData(): array { 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[0]', new GetAttrNode(new NameNode('foo'), new ConstantNode(0), self::getArrayNode(), GetAttrNode::ARRAY_CALL)], + ['foo["b"]', new GetAttrNode(new NameNode('foo'), new ConstantNode('b'), self::getArrayNode(), GetAttrNode::ARRAY_CALL)], - ['foo.foo', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), $this->getArrayNode(), GetAttrNode::PROPERTY_CALL), ['foo' => new Obj()]], + ['foo.foo', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), self::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)], + ['foo.foo({"b": "a", 0: "b"})', new GetAttrNode(new NameNode('foo'), new NameNode('foo'), self::getArrayNode(), GetAttrNode::METHOD_CALL), ['foo' => new Obj()]], + ['foo[index]', new GetAttrNode(new NameNode('foo'), new NameNode('index'), self::getArrayNode(), GetAttrNode::ARRAY_CALL)], ]; } - protected function getArrayNode() + protected static function getArrayNode(): ArrayNode { $array = new ArrayNode(); $array->addElement(new ConstantNode('a'), new ConstantNode('b')); diff --git a/Tests/Node/NameNodeTest.php b/Tests/Node/NameNodeTest.php index a30c27b..661466f 100644 --- a/Tests/Node/NameNodeTest.php +++ b/Tests/Node/NameNodeTest.php @@ -13,23 +13,23 @@ use Symfony\Component\ExpressionLanguage\Node\NameNode; -class NameNodeTest extends AbstractNodeTest +class NameNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { return [ ['bar', new NameNode('foo'), ['foo' => 'bar']], ]; } - public function getCompileData() + public static function getCompileData(): array { return [ ['$foo', new NameNode('foo')], ]; } - public function getDumpData() + public static function getDumpData(): array { return [ ['foo', new NameNode('foo')], diff --git a/Tests/Node/UnaryNodeTest.php b/Tests/Node/UnaryNodeTest.php index bac75d2..7da4be7 100644 --- a/Tests/Node/UnaryNodeTest.php +++ b/Tests/Node/UnaryNodeTest.php @@ -14,9 +14,9 @@ use Symfony\Component\ExpressionLanguage\Node\ConstantNode; use Symfony\Component\ExpressionLanguage\Node\UnaryNode; -class UnaryNodeTest extends AbstractNodeTest +class UnaryNodeTest extends AbstractNodeTestCase { - public function getEvaluateData() + public static function getEvaluateData(): array { return [ [-1, new UnaryNode('-', new ConstantNode(1))], @@ -26,7 +26,7 @@ public function getEvaluateData() ]; } - public function getCompileData() + public static function getCompileData(): array { return [ ['(-1)', new UnaryNode('-', new ConstantNode(1))], @@ -36,7 +36,7 @@ public function getCompileData() ]; } - public function getDumpData() + public static function getDumpData(): array { return [ ['(- 1)', new UnaryNode('-', new ConstantNode(1))], diff --git a/Tests/ParserTest.php b/Tests/ParserTest.php index b34711f..58a232e 100644 --- a/Tests/ParserTest.php +++ b/Tests/ParserTest.php @@ -47,7 +47,7 @@ public function testParse($node, $expression, $names = []) $this->assertEquals($node, $parser->parse($lexer->tokenize($expression), $names)); } - public function getParseData() + public static function getParseData() { $arguments = new Node\ArgumentsNode(); $arguments->addElement(new Node\ConstantNode('arg1')); @@ -176,10 +176,10 @@ public function getParseData() // chained calls [ - $this->createGetAttrNode( - $this->createGetAttrNode( - $this->createGetAttrNode( - $this->createGetAttrNode(new Node\NameNode('foo'), 'bar', Node\GetAttrNode::METHOD_CALL), + self::createGetAttrNode( + self::createGetAttrNode( + self::createGetAttrNode( + self::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), @@ -233,7 +233,7 @@ public function getParseData() ]; } - private function createGetAttrNode($node, $item, $type) + private static function createGetAttrNode($node, $item, $type) { return new Node\GetAttrNode($node, new Node\ConstantNode($item, Node\GetAttrNode::ARRAY_CALL !== $type), new Node\ArgumentsNode(), $type); } @@ -249,7 +249,7 @@ public function testParseWithInvalidPostfixData($expr, $names = []) $parser->parse($lexer->tokenize($expr), $names); } - public function getInvalidPostfixData() + public static function getInvalidPostfixData() { return [ [ @@ -299,7 +299,7 @@ public function testLint($expression, $names, string $exception = null) $this->expectNotToPerformAssertions(); } - public function getLintData(): array + public static function getLintData(): array { return [ 'valid expression' => [ diff --git a/Token.php b/Token.php index 40f5cda..c59fc73 100644 --- a/Token.php +++ b/Token.php @@ -30,8 +30,8 @@ class Token public const PUNCTUATION_TYPE = 'punctuation'; /** - * @param string $type The type of the token (self::*_TYPE) - * @param int $cursor The cursor position in the source + * @param string $type The type of the token (self::*_TYPE) + * @param int|null $cursor The cursor position in the source */ public function __construct(string $type, string|int|float|null $value, ?int $cursor) {