-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[ExpressionLanguage] Create an ExpressionFunction from a PHP function name #21122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
* | ||
* @author Dany Maillard <danymaillard93b@gmail.com> | ||
*/ | ||
class ExpressionPhpFunction extends ExpressionFunction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can make simple named constructor in ExpressionFunction
rather than utilizing inheritance:
ExpressionFunction::php('strtoupper')
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this suggestion of using a factory instead of a new class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Does it work for namespaced functions ? I think it does not in case they don't have the leading |
* | ||
* @throws \InvalidArgumentException if given function name does not exist | ||
*/ | ||
public static function php($name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about a more expressive name like fromPhp
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's better. I done.
@stof Indeed, for namespaced functions, it's doesn't work; a |
@stof An another solution would be don't throw exception, to evaluate with short name of namespaced function and to compile with full name of namespaced function. |
A syntax error ? Which error are you talking about ? Are you trying to put the namespace in the ExpressionLanguage function name too ? This is not supported, as EL does not have namespaced functions. The alternative is to decide than namespaced PHP functions are unsupported, but I would throw an exception early in this case, saying that EL function names cannot use |
I think supporting namespaced functions is better and having a second argument to change the function name in EL sounds like a good idea anyway, even for regular functions. |
throw new \InvalidArgumentException(sprintf( | ||
'An expression function name must be defined if PHP function "%s" is in namespace.', | ||
$phpFunction | ||
)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be on one line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
} | ||
|
||
$reflection = new \ReflectionFunction($phpFunction); | ||
if ($reflection->inNamespace() && !$expressionFunction) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using reflection, what about just checking for \\
in the function name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I will move the second part !$expressionFunction
first to make it faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reflection is more expressive that \\
and is anyway used further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved !$expressionFunction
at first.
* @throws \InvalidArgumentException if given PHP function name is in namespace | ||
* and expression function name is not defined | ||
*/ | ||
public static function fromPhp($phpFunction, $expressionFunction = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't Symfony have some convention to put factories/named constructors before the real constructor? I'm not sure, just asking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, __construct()
is always first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had to read the PHP doc to know what is expressionFunction
. I would rename it to expressionFunctionName
or something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarity, I renamed expressionFunction
to expressionFunctionName
and phpFunction
to phpFunctionName
.
@maidmaid could you update the PR description to match the new API? |
@lyrixx Yes, I updated the PR description now. |
|
||
$reflection = new \ReflectionFunction($phpFunctionName); | ||
if (!$expressionFunctionName && $reflection->inNamespace()) { | ||
throw new \InvalidArgumentException(sprintf('An expression function name must be defined if PHP function "%s" is in namespace.', $phpFunctionName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An expression function name must be defined when PHP function "%s" is namespaced.
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 with minor comment
It's done @nicolas-grekas :) |
Apparently, tests are broken when applying these changes. |
I fixed tests @fabpot. |
@maidmaid I'm still not convinced that using reflection is needed. Here is my patch to get rid of it: diff --git a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php
index d011854804..87a494f82d 100644
--- a/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php
+++ b/src/Symfony/Component/ExpressionLanguage/ExpressionFunction.php
@@ -77,18 +77,16 @@ class ExpressionFunction
*/
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));
}
- $reflection = new \ReflectionFunction($phpFunctionName);
- if (!$expressionFunctionName && $reflection->inNamespace()) {
+ $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));
}
- $phpFunctionName = $reflection->getName();
- $expressionFunctionName = $expressionFunctionName ?: $reflection->getShortName();
-
$compiler = function () use ($phpFunctionName) {
return sprintf('\%s(%s)', $phpFunctionName, implode(', ', func_get_args()));
};
@@ -99,6 +97,6 @@ class ExpressionFunction
return call_user_func_array($phpFunctionName, array_splice($args, 1));
};
- return new self($expressionFunctionName, $compiler, $evaluator);
+ return new self($expressionFunctionName ?: $parts[count($parts) - 1], $compiler, $evaluator);
}
} Any downsides? |
@maidmaid Can you submit a PR on the docs to document this new feature? |
Thank you @maidmaid. |
Thank you to you @fabpot and others reviewers. I created doc PR. |
…hp (maidmaid, javiereguiluz) This PR was merged into the master branch. Discussion ---------- [ExpressionLanguage] Document ExpressionFunction::fromPhp cf symfony/symfony#21122 Commits ------- 415c4ec Update extending.rst 478400a Minor reword 059155f Add versionadded 6c9e09d Fix typo c70ea7f Fix typo 6d9af37 Add fromPhp tip
When we extend Expression Language, 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:
with this PR:
It includes PHP namespaced function: