From 60b9f856fdbe796acaeac19801712afdd544c082 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 20 Sep 2013 15:35:54 +0200 Subject: [PATCH 1/3] [ExpressionLanguage] made ExpressionLanguage::parse return an ParsedExpression instance --- .../DependencyInjection/SecurityExtension.php | 2 +- .../ExpressionLanguage/ExpressionLanguage.php | 23 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php index 3047136283..aaa3a73152 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/SecurityExtension.php @@ -615,7 +615,7 @@ class SecurityExtension extends Extension ->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression') ->setPublic(false) ->addArgument($expression) - ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request')))) + ->addArgument(serialize($this->getExpressionLanguage()->parse($expression, array('token', 'user', 'object', 'roles', 'request'))->getNodes())) ; return $this->expressions[$id] = new Reference($id); diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index 0a45ca14dd..1db479cd87 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -36,14 +36,14 @@ class ExpressionLanguage /** * Compiles an expression source code. * - * @param string $expression The expression to compile - * @param array $names An array of valid names + * @param Expression|string $expression The expression to compile + * @param array $names An array of valid names * * @return string The compiled PHP source code */ public function compile($expression, $names = array()) { - return $this->getCompiler()->compile($this->parse($expression, $names))->getSource(); + return $this->getCompiler()->compile($this->parse($expression, $names)->getNodes())->getSource(); } /** @@ -56,13 +56,7 @@ class ExpressionLanguage */ public function evaluate($expression, $values = array()) { - if ($expression instanceof ParsedExpression) { - $expression = $expression->getNodes(); - } else { - $expression = $this->parse($expression, array_keys($values)); - } - - return $expression->evaluate($this->functions, $values); + return $this->parse($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values); } /** @@ -70,14 +64,19 @@ class ExpressionLanguage * * @param Expression|string $expression The expression to parse * - * @return Node A Node tree + * @return ParsedExpression A ParsedExpression instance */ public function parse($expression, $names) { + if ($expression instanceof ParsedExpression) { + return $expression; + } + $key = $expression.'//'.implode('-', $names); if (!isset($this->cache[$key])) { - $this->cache[$key] = $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names); + $nodes = $this->getParser()->parse($this->getLexer()->tokenize((string) $expression), $names); + $this->cache[$key] = new ParsedExpression((string) $expression, $nodes); } return $this->cache[$key]; From 5076ec7e90d46609684c618cfe9e5a04aca59061 Mon Sep 17 00:00:00 2001 From: Adrien Brault Date: Thu, 19 Sep 2013 22:14:47 -0700 Subject: [PATCH 2/3] [ExpressionLanguage] optimized serialization of nodes and expressions --- .../ExpressionLanguage/Node/BinaryNode.php | 16 ++++++++-------- .../ExpressionLanguage/Node/UnaryNode.php | 4 ++-- .../Tests/ExpressionTest.php | 18 ++++++++++++++++++ .../Tests/Node/ArrayNodeTest.php | 12 ++++++++++++ .../Tests/Node/NodeTest.php | 10 ++++++++++ .../Tests/ParsedExpressionTest.php | 19 +++++++++++++++++++ 6 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 src/Symfony/Component/ExpressionLanguage/Tests/ExpressionTest.php create mode 100644 src/Symfony/Component/ExpressionLanguage/Tests/ParsedExpressionTest.php diff --git a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php index c2bd58df73..54e1f88107 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/BinaryNode.php @@ -15,13 +15,13 @@ use Symfony\Component\ExpressionLanguage\Compiler; class BinaryNode extends Node { - private $operators = array( + private static $operators = array( '~' => '.', 'and' => '&&', 'or' => '||', ); - private $functions = array( + private static $functions = array( '**' => 'pow', '..' => 'range', 'in' => 'in_array', @@ -50,9 +50,9 @@ class BinaryNode extends Node return; } - if (isset($this->functions[$operator])) { + if (isset(self::$functions[$operator])) { $compiler - ->raw(sprintf('%s(', $this->functions[$operator])) + ->raw(sprintf('%s(', self::$functions[$operator])) ->compile($this->nodes['left']) ->raw(', ') ->compile($this->nodes['right']) @@ -62,8 +62,8 @@ class BinaryNode extends Node return; } - if (isset($this->operators[$operator])) { - $operator = $this->operators[$operator]; + if (isset(self::$operators[$operator])) { + $operator = self::$operators[$operator]; } $compiler @@ -83,12 +83,12 @@ class BinaryNode extends Node $left = $this->nodes['left']->evaluate($functions, $values); $right = $this->nodes['right']->evaluate($functions, $values); - if (isset($this->functions[$operator])) { + if (isset(self::$functions[$operator])) { if ('not in' == $operator) { return !call_user_func('in_array', $left, $right); } - return call_user_func($this->functions[$operator], $left, $right); + return call_user_func(self::$functions[$operator], $left, $right); } switch ($operator) { diff --git a/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php b/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php index 3ec88d1179..73aef144af 100644 --- a/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php +++ b/src/Symfony/Component/ExpressionLanguage/Node/UnaryNode.php @@ -15,7 +15,7 @@ use Symfony\Component\ExpressionLanguage\Compiler; class UnaryNode extends Node { - private $operators = array( + private static $operators = array( '!' => '!', 'not' => '!', '+' => '+', @@ -32,7 +32,7 @@ class UnaryNode extends Node { $compiler ->raw('(') - ->raw($this->operators[$this->attributes['operator']]) + ->raw(self::$operators[$this->attributes['operator']]) ->compile($this->nodes['node']) ->raw(')') ; diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionTest.php new file mode 100644 index 0000000000..be87321fd3 --- /dev/null +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionTest.php @@ -0,0 +1,18 @@ +assertEquals($expression, $unserializedExpression); + } +} diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Node/ArrayNodeTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/Node/ArrayNodeTest.php index 24d1bb06fb..f2342f2850 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Node/ArrayNodeTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Node/ArrayNodeTest.php @@ -16,6 +16,18 @@ 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 array( diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php index 4365e8a1f6..6063c27ba4 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Node/NodeTest.php @@ -27,4 +27,14 @@ Node( EOF , (string) $node); } + + public function testSerialization() + { + $node = new Node(array('foo' => 'bar'), array('bar' => 'foo')); + + $serializedNode = serialize($node); + $unserializedNode = unserialize($serializedNode); + + $this->assertEquals($node, $unserializedNode); + } } diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ParsedExpressionTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ParsedExpressionTest.php new file mode 100644 index 0000000000..c91b9ef666 --- /dev/null +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ParsedExpressionTest.php @@ -0,0 +1,19 @@ +assertEquals($expression, $unserializedExpression); + } +} From e8691366ce87db4705ec7c809cd963c981b909e7 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Fri, 20 Sep 2013 16:16:44 +0200 Subject: [PATCH 3/3] [ExpressionLanguage] renamed addFunction() to register() --- .../DependencyInjection/ExpressionLanguage.php | 4 ++-- .../ExpressionLanguage/ExpressionLanguage.php | 11 +++++++++-- src/Symfony/Component/ExpressionLanguage/README.md | 2 +- .../Core/Authorization/ExpressionLanguage.php | 10 +++++----- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php b/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php index 5b97b202b7..2e7edcd32f 100644 --- a/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php +++ b/src/Symfony/Component/DependencyInjection/ExpressionLanguage.php @@ -27,13 +27,13 @@ class ExpressionLanguage extends BaseExpressionLanguage { parent::registerFunctions(); - $this->addFunction('service', function ($arg) { + $this->register('service', function ($arg) { return sprintf('$this->get(%s)', $arg); }, function (array $variables, $value) { return $variables['container']->get($value); }); - $this->addFunction('parameter', function ($arg) { + $this->register('parameter', function ($arg) { return sprintf('$this->getParameter(%s)', $arg); }, function (array $variables, $value) { return $variables['container']->getParameter($value); diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index 1db479cd87..b5cd15e0de 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -82,14 +82,21 @@ class ExpressionLanguage return $this->cache[$key]; } - public function addFunction($name, $compiler, $evaluator) + /** + * Registers a function. + * + * @param string $name The function name + * @param callable $compiler A callable able to compile the function + * @param callable $evaluator A callable able to evaluate the function + */ + public function register($name, $compiler, $evaluator) { $this->functions[$name] = array('compiler' => $compiler, 'evaluator' => $evaluator); } protected function registerFunctions() { - $this->addFunction('constant', function ($constant) { + $this->register('constant', function ($constant) { return sprintf('constant(%s)', $constant); }, function (array $values, $constant) { return constant($constant); diff --git a/src/Symfony/Component/ExpressionLanguage/README.md b/src/Symfony/Component/ExpressionLanguage/README.md index 648dedcbcf..3d53d2ac1b 100644 --- a/src/Symfony/Component/ExpressionLanguage/README.md +++ b/src/Symfony/Component/ExpressionLanguage/README.md @@ -25,7 +25,7 @@ You can extend your DSL with functions: $evaluator = function (array $variables, $value) { return strtoupper($value); }; - $language->addFunction('upper', $compiler, $evaluator); + $language->register('upper', $compiler, $evaluator); echo $language->evaluate('"foo" ~ upper(foo)', array('foo' => 'bar')); // would output fooBAR diff --git a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php index bdb3371c01..524342ef6c 100644 --- a/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php +++ b/src/Symfony/Component/Security/Core/Authorization/ExpressionLanguage.php @@ -24,31 +24,31 @@ class ExpressionLanguage extends BaseExpressionLanguage { parent::registerFunctions(); - $this->addFunction('is_anonymous', function () { + $this->register('is_anonymous', function () { return '$trust_resolver->isAnonymous($token)'; }, function (array $variables) { return $variables['trust_resolver']->isAnonymous($variables['token']); }); - $this->addFunction('is_authenticated', function () { + $this->register('is_authenticated', function () { return '!$trust_resolver->isAnonymous($token)'; }, function (array $variables) { return !$variables['trust_resolver']->isAnonymous($variables['token']); }); - $this->addFunction('is_fully_authenticated', function () { + $this->register('is_fully_authenticated', function () { return '!$trust_resolver->isFullFledge($token)'; }, function (array $variables) { return !$variables['trust_resolver']->isFullFledge($variables['token']); }); - $this->addFunction('is_remember_me', function () { + $this->register('is_remember_me', function () { return '!$trust_resolver->isRememberMe($token)'; }, function (array $variables) { return !$variables['trust_resolver']->isRememberMe($variables['token']); }); - $this->addFunction('has_role', function ($role) { + $this->register('has_role', function ($role) { return sprintf('in_array(%s, $roles)', $role); }, function (array $variables, $role) { return in_array($role, $variables['roles']);