feature #21122 [ExpressionLanguage] Create an ExpressionFunction from a PHP function name (maidmaid)
This PR was squashed before being merged into the 3.3-dev branch (closes #21122).
Discussion
----------
[ExpressionLanguage] Create an ExpressionFunction from a PHP function name
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | n/a
| License | MIT
| Doc PR | n/a
When we [extend Expression Language](http://symfony.com/doc/current/components/expression_language/extending.html), we often need to add PHP functions whose code is repetitive and redundant at the compiler/evaluator level. This PR proposes a new way more generic which allows to add a PHP function.
currently:
```php
$el = new ExpressionLanguage();
$compiler = function ($str) {
return sprintf('strtoupper(%s)', $str);
};
$evaluator = function ($arguments, $str) {
return strtoupper($str);
};
$el->addFunction(new ExpressionFunction('strtoupper', $compiler, $evaluator));
$el->evaluate('strtoupper("hello")'); // return "HELLO"
```
with this PR:
```php
$el->addFunction(ExpressionFunction::fromPhp('strtoupper'));
$el->evaluate('strtoupper("hello")'); // return "HELLO"
```
It includes PHP namespaced function:
```php
$el->addFunction(ExpressionFunction::fromPhp('My\strtoupper', 'my_strtoupper'));
$el->evaluate('my_strtoupper("hello")'); // return "HELLO"
```
Commits
-------
44d67ed5f5
[ExpressionLanguage] Create an ExpressionFunction from a PHP function name
This commit is contained in:
commit
3e9b8f3ba5
@ -62,4 +62,41 @@ class ExpressionFunction
|
||||
{
|
||||
return $this->evaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ExpressionFunction from a PHP function name.
|
||||
*
|
||||
* @param string $phpFunctionName The PHP function name
|
||||
* @param string|null $expressionFunctionName The expression function name (default: same than the PHP function name)
|
||||
*
|
||||
* @return self
|
||||
*
|
||||
* @throws \InvalidArgumentException if given PHP function name does not exist
|
||||
* @throws \InvalidArgumentException if given PHP function name is in namespace
|
||||
* and expression function name is not defined
|
||||
*/
|
||||
public static function fromPhp($phpFunctionName, $expressionFunctionName = null)
|
||||
{
|
||||
$phpFunctionName = ltrim($phpFunctionName, '\\');
|
||||
if (!function_exists($phpFunctionName)) {
|
||||
throw new \InvalidArgumentException(sprintf('PHP function "%s" does not exist.', $phpFunctionName));
|
||||
}
|
||||
|
||||
$parts = explode('\\', $phpFunctionName);
|
||||
if (!$expressionFunctionName && count($parts) > 1) {
|
||||
throw new \InvalidArgumentException(sprintf('An expression function name must be defined when PHP function "%s" is namespaced.', $phpFunctionName));
|
||||
}
|
||||
|
||||
$compiler = function () use ($phpFunctionName) {
|
||||
return sprintf('\%s(%s)', $phpFunctionName, implode(', ', func_get_args()));
|
||||
};
|
||||
|
||||
$evaluator = function () use ($phpFunctionName) {
|
||||
$args = func_get_args();
|
||||
|
||||
return call_user_func_array($phpFunctionName, array_splice($args, 1));
|
||||
};
|
||||
|
||||
return new self($expressionFunctionName ?: end($parts), $compiler, $evaluator);
|
||||
}
|
||||
}
|
||||
|
@ -143,11 +143,7 @@ class ExpressionLanguage
|
||||
|
||||
protected function registerFunctions()
|
||||
{
|
||||
$this->register('constant', function ($constant) {
|
||||
return sprintf('constant(%s)', $constant);
|
||||
}, function (array $values, $constant) {
|
||||
return constant($constant);
|
||||
});
|
||||
$this->addFunction(ExpressionFunction::fromPhp('constant'));
|
||||
}
|
||||
|
||||
private function getLexer()
|
||||
|
@ -0,0 +1,44 @@
|
||||
<?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 Symfony\Component\ExpressionLanguage\ExpressionFunction;
|
||||
|
||||
/**
|
||||
* Tests ExpressionFunction.
|
||||
*
|
||||
* @author Dany Maillard <danymaillard93b@gmail.com>
|
||||
*/
|
||||
class ExpressionFunctionTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage PHP function "fn_does_not_exist" does not exist.
|
||||
*/
|
||||
public function testFunctionDoesNotExist()
|
||||
{
|
||||
ExpressionFunction::fromPhp('fn_does_not_exist');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage An expression function name must be defined when PHP function "Symfony\Component\ExpressionLanguage\Tests\fn_namespaced" is namespaced.
|
||||
*/
|
||||
public function testFunctionNamespaced()
|
||||
{
|
||||
ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\fn_namespaced');
|
||||
}
|
||||
}
|
||||
|
||||
function fn_namespaced()
|
||||
{
|
||||
}
|
@ -109,7 +109,7 @@ class ExpressionLanguageTest extends TestCase
|
||||
$this->assertEquals(PHP_VERSION, $expressionLanguage->evaluate('constant("PHP_VERSION")'));
|
||||
|
||||
$expressionLanguage = new ExpressionLanguage();
|
||||
$this->assertEquals('constant("PHP_VERSION")', $expressionLanguage->compile('constant("PHP_VERSION")'));
|
||||
$this->assertEquals('\constant("PHP_VERSION")', $expressionLanguage->compile('constant("PHP_VERSION")'));
|
||||
}
|
||||
|
||||
public function testProviders()
|
||||
@ -117,6 +117,12 @@ class ExpressionLanguageTest extends TestCase
|
||||
$expressionLanguage = new ExpressionLanguage(null, array(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()'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,7 @@ 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
|
||||
{
|
||||
@ -24,6 +25,17 @@ class TestProvider implements ExpressionFunctionProviderInterface
|
||||
}, 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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user