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 expressions60b9f85
[ExpressionLanguage] made ExpressionLanguage::parse return an ParsedExpression instance
This commit is contained in:
commit
8aa685bbe9
@ -615,7 +615,7 @@ class SecurityExtension extends Extension
|
|||||||
->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression')
|
->register($id, 'Symfony\Component\ExpressionLanguage\SerializedParsedExpression')
|
||||||
->setPublic(false)
|
->setPublic(false)
|
||||||
->addArgument($expression)
|
->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);
|
return $this->expressions[$id] = new Reference($id);
|
||||||
|
@ -27,13 +27,13 @@ class ExpressionLanguage extends BaseExpressionLanguage
|
|||||||
{
|
{
|
||||||
parent::registerFunctions();
|
parent::registerFunctions();
|
||||||
|
|
||||||
$this->addFunction('service', function ($arg) {
|
$this->register('service', function ($arg) {
|
||||||
return sprintf('$this->get(%s)', $arg);
|
return sprintf('$this->get(%s)', $arg);
|
||||||
}, function (array $variables, $value) {
|
}, function (array $variables, $value) {
|
||||||
return $variables['container']->get($value);
|
return $variables['container']->get($value);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->addFunction('parameter', function ($arg) {
|
$this->register('parameter', function ($arg) {
|
||||||
return sprintf('$this->getParameter(%s)', $arg);
|
return sprintf('$this->getParameter(%s)', $arg);
|
||||||
}, function (array $variables, $value) {
|
}, function (array $variables, $value) {
|
||||||
return $variables['container']->getParameter($value);
|
return $variables['container']->getParameter($value);
|
||||||
|
@ -36,14 +36,14 @@ class ExpressionLanguage
|
|||||||
/**
|
/**
|
||||||
* Compiles an expression source code.
|
* Compiles an expression source code.
|
||||||
*
|
*
|
||||||
* @param string $expression The expression to compile
|
* @param Expression|string $expression The expression to compile
|
||||||
* @param array $names An array of valid names
|
* @param array $names An array of valid names
|
||||||
*
|
*
|
||||||
* @return string The compiled PHP source code
|
* @return string The compiled PHP source code
|
||||||
*/
|
*/
|
||||||
public function compile($expression, $names = array())
|
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())
|
public function evaluate($expression, $values = array())
|
||||||
{
|
{
|
||||||
if ($expression instanceof ParsedExpression) {
|
return $this->parse($expression, array_keys($values))->getNodes()->evaluate($this->functions, $values);
|
||||||
$expression = $expression->getNodes();
|
|
||||||
} else {
|
|
||||||
$expression = $this->parse($expression, array_keys($values));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $expression->evaluate($this->functions, $values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,27 +64,39 @@ class ExpressionLanguage
|
|||||||
*
|
*
|
||||||
* @param Expression|string $expression The expression to parse
|
* @param Expression|string $expression The expression to parse
|
||||||
*
|
*
|
||||||
* @return Node A Node tree
|
* @return ParsedExpression A ParsedExpression instance
|
||||||
*/
|
*/
|
||||||
public function parse($expression, $names)
|
public function parse($expression, $names)
|
||||||
{
|
{
|
||||||
|
if ($expression instanceof ParsedExpression) {
|
||||||
|
return $expression;
|
||||||
|
}
|
||||||
|
|
||||||
$key = $expression.'//'.implode('-', $names);
|
$key = $expression.'//'.implode('-', $names);
|
||||||
|
|
||||||
if (!isset($this->cache[$key])) {
|
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];
|
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);
|
$this->functions[$name] = array('compiler' => $compiler, 'evaluator' => $evaluator);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function registerFunctions()
|
protected function registerFunctions()
|
||||||
{
|
{
|
||||||
$this->addFunction('constant', function ($constant) {
|
$this->register('constant', function ($constant) {
|
||||||
return sprintf('constant(%s)', $constant);
|
return sprintf('constant(%s)', $constant);
|
||||||
}, function (array $values, $constant) {
|
}, function (array $values, $constant) {
|
||||||
return constant($constant);
|
return constant($constant);
|
||||||
|
@ -15,13 +15,13 @@ use Symfony\Component\ExpressionLanguage\Compiler;
|
|||||||
|
|
||||||
class BinaryNode extends Node
|
class BinaryNode extends Node
|
||||||
{
|
{
|
||||||
private $operators = array(
|
private static $operators = array(
|
||||||
'~' => '.',
|
'~' => '.',
|
||||||
'and' => '&&',
|
'and' => '&&',
|
||||||
'or' => '||',
|
'or' => '||',
|
||||||
);
|
);
|
||||||
|
|
||||||
private $functions = array(
|
private static $functions = array(
|
||||||
'**' => 'pow',
|
'**' => 'pow',
|
||||||
'..' => 'range',
|
'..' => 'range',
|
||||||
'in' => 'in_array',
|
'in' => 'in_array',
|
||||||
@ -50,9 +50,9 @@ class BinaryNode extends Node
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->functions[$operator])) {
|
if (isset(self::$functions[$operator])) {
|
||||||
$compiler
|
$compiler
|
||||||
->raw(sprintf('%s(', $this->functions[$operator]))
|
->raw(sprintf('%s(', self::$functions[$operator]))
|
||||||
->compile($this->nodes['left'])
|
->compile($this->nodes['left'])
|
||||||
->raw(', ')
|
->raw(', ')
|
||||||
->compile($this->nodes['right'])
|
->compile($this->nodes['right'])
|
||||||
@ -62,8 +62,8 @@ class BinaryNode extends Node
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($this->operators[$operator])) {
|
if (isset(self::$operators[$operator])) {
|
||||||
$operator = $this->operators[$operator];
|
$operator = self::$operators[$operator];
|
||||||
}
|
}
|
||||||
|
|
||||||
$compiler
|
$compiler
|
||||||
@ -83,12 +83,12 @@ class BinaryNode extends Node
|
|||||||
$left = $this->nodes['left']->evaluate($functions, $values);
|
$left = $this->nodes['left']->evaluate($functions, $values);
|
||||||
$right = $this->nodes['right']->evaluate($functions, $values);
|
$right = $this->nodes['right']->evaluate($functions, $values);
|
||||||
|
|
||||||
if (isset($this->functions[$operator])) {
|
if (isset(self::$functions[$operator])) {
|
||||||
if ('not in' == $operator) {
|
if ('not in' == $operator) {
|
||||||
return !call_user_func('in_array', $left, $right);
|
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) {
|
switch ($operator) {
|
||||||
|
@ -15,7 +15,7 @@ use Symfony\Component\ExpressionLanguage\Compiler;
|
|||||||
|
|
||||||
class UnaryNode extends Node
|
class UnaryNode extends Node
|
||||||
{
|
{
|
||||||
private $operators = array(
|
private static $operators = array(
|
||||||
'!' => '!',
|
'!' => '!',
|
||||||
'not' => '!',
|
'not' => '!',
|
||||||
'+' => '+',
|
'+' => '+',
|
||||||
@ -32,7 +32,7 @@ class UnaryNode extends Node
|
|||||||
{
|
{
|
||||||
$compiler
|
$compiler
|
||||||
->raw('(')
|
->raw('(')
|
||||||
->raw($this->operators[$this->attributes['operator']])
|
->raw(self::$operators[$this->attributes['operator']])
|
||||||
->compile($this->nodes['node'])
|
->compile($this->nodes['node'])
|
||||||
->raw(')')
|
->raw(')')
|
||||||
;
|
;
|
||||||
|
@ -25,7 +25,7 @@ You can extend your DSL with functions:
|
|||||||
$evaluator = function (array $variables, $value) {
|
$evaluator = function (array $variables, $value) {
|
||||||
return strtoupper($value);
|
return strtoupper($value);
|
||||||
};
|
};
|
||||||
$language->addFunction('upper', $compiler, $evaluator);
|
$language->register('upper', $compiler, $evaluator);
|
||||||
|
|
||||||
echo $language->evaluate('"foo" ~ upper(foo)', array('foo' => 'bar'));
|
echo $language->evaluate('"foo" ~ upper(foo)', array('foo' => 'bar'));
|
||||||
// would output fooBAR
|
// would output fooBAR
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,18 @@ use Symfony\Component\ExpressionLanguage\Node\ConstantNode;
|
|||||||
|
|
||||||
class ArrayNodeTest extends AbstractNodeTest
|
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()
|
public function getEvaluateData()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
|
@ -27,4 +27,14 @@ Node(
|
|||||||
EOF
|
EOF
|
||||||
, (string) $node);
|
, (string) $node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSerialization()
|
||||||
|
{
|
||||||
|
$node = new Node(array('foo' => 'bar'), array('bar' => 'foo'));
|
||||||
|
|
||||||
|
$serializedNode = serialize($node);
|
||||||
|
$unserializedNode = unserialize($serializedNode);
|
||||||
|
|
||||||
|
$this->assertEquals($node, $unserializedNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -24,31 +24,31 @@ class ExpressionLanguage extends BaseExpressionLanguage
|
|||||||
{
|
{
|
||||||
parent::registerFunctions();
|
parent::registerFunctions();
|
||||||
|
|
||||||
$this->addFunction('is_anonymous', function () {
|
$this->register('is_anonymous', function () {
|
||||||
return '$trust_resolver->isAnonymous($token)';
|
return '$trust_resolver->isAnonymous($token)';
|
||||||
}, function (array $variables) {
|
}, function (array $variables) {
|
||||||
return $variables['trust_resolver']->isAnonymous($variables['token']);
|
return $variables['trust_resolver']->isAnonymous($variables['token']);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->addFunction('is_authenticated', function () {
|
$this->register('is_authenticated', function () {
|
||||||
return '!$trust_resolver->isAnonymous($token)';
|
return '!$trust_resolver->isAnonymous($token)';
|
||||||
}, function (array $variables) {
|
}, function (array $variables) {
|
||||||
return !$variables['trust_resolver']->isAnonymous($variables['token']);
|
return !$variables['trust_resolver']->isAnonymous($variables['token']);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->addFunction('is_fully_authenticated', function () {
|
$this->register('is_fully_authenticated', function () {
|
||||||
return '!$trust_resolver->isFullFledge($token)';
|
return '!$trust_resolver->isFullFledge($token)';
|
||||||
}, function (array $variables) {
|
}, function (array $variables) {
|
||||||
return !$variables['trust_resolver']->isFullFledge($variables['token']);
|
return !$variables['trust_resolver']->isFullFledge($variables['token']);
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->addFunction('is_remember_me', function () {
|
$this->register('is_remember_me', function () {
|
||||||
return '!$trust_resolver->isRememberMe($token)';
|
return '!$trust_resolver->isRememberMe($token)';
|
||||||
}, function (array $variables) {
|
}, function (array $variables) {
|
||||||
return !$variables['trust_resolver']->isRememberMe($variables['token']);
|
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);
|
return sprintf('in_array(%s, $roles)', $role);
|
||||||
}, function (array $variables, $role) {
|
}, function (array $variables, $role) {
|
||||||
return in_array($role, $variables['roles']);
|
return in_array($role, $variables['roles']);
|
||||||
|
Reference in New Issue
Block a user