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 cabb0afdfc..aa74fe8463 100644 --- a/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php +++ b/src/Symfony/Component/ExpressionLanguage/Tests/ExpressionLanguageTest.php @@ -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()')); } /** 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; +}