Skip to content

Commit 3e9b8f3

Browse files
committed
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 ------- 44d67ed [ExpressionLanguage] Create an ExpressionFunction from a PHP function name
2 parents f4db331 + 44d67ed commit 3e9b8f3

File tree

5 files changed

+101
-6
lines changed

5 files changed

+101
-6
lines changed

src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,41 @@ public function getEvaluator()
6262
{
6363
return $this->evaluator;
6464
}
65+
66+
/**
67+
* Creates an ExpressionFunction from a PHP function name.
68+
*
69+
* @param string $phpFunctionName The PHP function name
70+
* @param string|null $expressionFunctionName The expression function name (default: same than the PHP function name)
71+
*
72+
* @return self
73+
*
74+
* @throws \InvalidArgumentException if given PHP function name does not exist
75+
* @throws \InvalidArgumentException if given PHP function name is in namespace
76+
* and expression function name is not defined
77+
*/
78+
public static function fromPhp($phpFunctionName, $expressionFunctionName = null)
79+
{
80+
$phpFunctionName = ltrim($phpFunctionName, '\\');
81+
if (!function_exists($phpFunctionName)) {
82+
throw new \InvalidArgumentException(sprintf('PHP function "%s" does not exist.', $phpFunctionName));
83+
}
84+
85+
$parts = explode('\\', $phpFunctionName);
86+
if (!$expressionFunctionName && count($parts) > 1) {
87+
throw new \InvalidArgumentException(sprintf('An expression function name must be defined when PHP function "%s" is namespaced.', $phpFunctionName));
88+
}
89+
90+
$compiler = function () use ($phpFunctionName) {
91+
return sprintf('\%s(%s)', $phpFunctionName, implode(', ', func_get_args()));
92+
};
93+
94+
$evaluator = function () use ($phpFunctionName) {
95+
$args = func_get_args();
96+
97+
return call_user_func_array($phpFunctionName, array_splice($args, 1));
98+
};
99+
100+
return new self($expressionFunctionName ?: end($parts), $compiler, $evaluator);
101+
}
65102
}

src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,7 @@ public function registerProvider(ExpressionFunctionProviderInterface $provider)
143143

144144
protected function registerFunctions()
145145
{
146-
$this->register('constant', function ($constant) {
147-
return sprintf('constant(%s)', $constant);
148-
}, function (array $values, $constant) {
149-
return constant($constant);
150-
});
146+
$this->addFunction(ExpressionFunction::fromPhp('constant'));
151147
}
152148

153149
private function getLexer()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ExpressionLanguage\Tests;
13+
14+
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
15+
16+
/**
17+
* Tests ExpressionFunction.
18+
*
19+
* @author Dany Maillard <[email protected]>
20+
*/
21+
class ExpressionFunctionTest extends \PHPUnit_Framework_TestCase
22+
{
23+
/**
24+
* @expectedException \InvalidArgumentException
25+
* @expectedExceptionMessage PHP function "fn_does_not_exist" does not exist.
26+
*/
27+
public function testFunctionDoesNotExist()
28+
{
29+
ExpressionFunction::fromPhp('fn_does_not_exist');
30+
}
31+
32+
/**
33+
* @expectedException \InvalidArgumentException
34+
* @expectedExceptionMessage An expression function name must be defined when PHP function "Symfony\Component\ExpressionLanguage\Tests\fn_namespaced" is namespaced.
35+
*/
36+
public function testFunctionNamespaced()
37+
{
38+
ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\fn_namespaced');
39+
}
40+
}
41+
42+
function fn_namespaced()
43+
{
44+
}

src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,20 @@ public function testConstantFunction()
109109
$this->assertEquals(PHP_VERSION, $expressionLanguage->evaluate('constant("PHP_VERSION")'));
110110

111111
$expressionLanguage = new ExpressionLanguage();
112-
$this->assertEquals('constant("PHP_VERSION")', $expressionLanguage->compile('constant("PHP_VERSION")'));
112+
$this->assertEquals('\constant("PHP_VERSION")', $expressionLanguage->compile('constant("PHP_VERSION")'));
113113
}
114114

115115
public function testProviders()
116116
{
117117
$expressionLanguage = new ExpressionLanguage(null, array(new TestProvider()));
118118
$this->assertEquals('foo', $expressionLanguage->evaluate('identity("foo")'));
119119
$this->assertEquals('"foo"', $expressionLanguage->compile('identity("foo")'));
120+
$this->assertEquals('FOO', $expressionLanguage->evaluate('strtoupper("foo")'));
121+
$this->assertEquals('\strtoupper("foo")', $expressionLanguage->compile('strtoupper("foo")'));
122+
$this->assertEquals('foo', $expressionLanguage->evaluate('strtolower("FOO")'));
123+
$this->assertEquals('\strtolower("FOO")', $expressionLanguage->compile('strtolower("FOO")'));
124+
$this->assertTrue($expressionLanguage->evaluate('fn_namespaced()'));
125+
$this->assertEquals('\Symfony\Component\ExpressionLanguage\Tests\Fixtures\fn_namespaced()', $expressionLanguage->compile('fn_namespaced()'));
120126
}
121127

122128
/**

src/Symfony/Component/ExpressionLanguage/Tests/Fixtures/TestProvider.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\ExpressionLanguage\ExpressionFunction;
1515
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
16+
use Symfony\Component\ExpressionLanguage\ExpressionPhpFunction;
1617

1718
class TestProvider implements ExpressionFunctionProviderInterface
1819
{
@@ -24,6 +25,17 @@ public function getFunctions()
2425
}, function (array $values, $input) {
2526
return $input;
2627
}),
28+
29+
ExpressionFunction::fromPhp('strtoupper'),
30+
31+
ExpressionFunction::fromPhp('\strtolower'),
32+
33+
ExpressionFunction::fromPhp('Symfony\Component\ExpressionLanguage\Tests\Fixtures\fn_namespaced', 'fn_namespaced'),
2734
);
2835
}
2936
}
37+
38+
function fn_namespaced()
39+
{
40+
return true;
41+
}

0 commit comments

Comments
 (0)