From 44d67ed5f514cd5cc050ed94287c1e1eae7b2c8a Mon Sep 17 00:00:00 2001 From: Dany Maillard Date: Mon, 2 Jan 2017 02:49:09 +0100 Subject: [PATCH] [ExpressionLanguage] Create an ExpressionFunction from a PHP function name --- .../ExpressionLanguage/ExpressionFunction.php | 37 ++++++++++++++++ .../ExpressionLanguage/ExpressionLanguage.php | 6 +-- .../Tests/ExpressionFunctionTest.php | 44 +++++++++++++++++++ .../Tests/ExpressionLanguageTest.php | 8 +++- .../Tests/Fixtures/TestProvider.php | 12 +++++ 5 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 src/Symfony/Component/ExpressionLanguage/Tests/ExpressionFunctionTest.php diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php index c42f29f609..749b8c7ced 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php @@ -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); + } } diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php index ec56890d2e..a0552567d3 100644 --- a/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php +++ b/src/Symfony/Component/ExpressionLanguage/ExpressionLanguage.php @@ -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() diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionFunctionTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionFunctionTest.php new file mode 100644 index 0000000000..8faf980977 --- /dev/null +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionFunctionTest.php @@ -0,0 +1,44 @@ + + * + * 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 + */ +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() +{ +} diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php index 82b27454b0..eba54ec7cc 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -108,7 +108,7 @@ class ExpressionLanguageTest extends \PHPUnit_Framework_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() @@ -116,6 +116,12 @@ class ExpressionLanguageTest extends \PHPUnit_Framework_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()')); } /** diff --git a/src/Symfony/Component/ExpressionLanguage/Tests/Fixtures/TestProvider.php b/src/Symfony/Component/ExpressionLanguage/Tests/Fixtures/TestProvider.php index 8b7d81910c..20c5182bba 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/Fixtures/TestProvider.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/Fixtures/TestProvider.php @@ -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; +}