merged branch fabpot/expression-language-tweaks (PR #9091)

This PR was merged into the master branch.

Discussion
----------

[ExpressionLanguage] made minor tweaks

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #9087
| License       | MIT
| Doc PR        | n/a

 * made ExpressionLanguage::parse return an ParsedExpression instance
 * optimized the size of the serialization of expressions

Commits
-------

e869136 [ExpressionLanguage] renamed addFunction() to register()
5076ec7 [ExpressionLanguage] optimized serialization of nodes and expressions
60b9f85 [ExpressionLanguage] made ExpressionLanguage::parse return an ParsedExpression instance
This commit is contained in:
Fabien Potencier 2013-09-21 20:31:30 +02:00
commit 8aa685bbe9
11 changed files with 98 additions and 33 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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,27 +64,39 @@ 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];
}
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);

View File

@ -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) {

View File

@ -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(')')
;

View File

@ -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

View File

@ -0,0 +1,18 @@
<?php
namespace Symfony\Component\ExpressionLanguage\Tests;
use Symfony\Component\ExpressionLanguage\Expression;
class ExpressionTest extends \PHPUnit_Framework_TestCase
{
public function testSerialization()
{
$expression = new Expression('kernel.boot()');
$serializedExpression = serialize($expression);
$unserializedExpression = unserialize($serializedExpression);
$this->assertEquals($expression, $unserializedExpression);
}
}

View File

@ -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(

View File

@ -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);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace Symfony\Component\ExpressionLanguage\Tests;
use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
use Symfony\Component\ExpressionLanguage\ParsedExpression;
class ParsedExpressionTest extends \PHPUnit_Framework_TestCase
{
public function testSerialization()
{
$expression = new ParsedExpression('25', new ConstantNode('25'));
$serializedExpression = serialize($expression);
$unserializedExpression = unserialize($serializedExpression);
$this->assertEquals($expression, $unserializedExpression);
}
}

View File

@ -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']);