Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
maidmaid wants to merge14 commits intosymfony:masterfrommaidmaid:el-phpfn

Conversation

@maidmaid
Copy link
Contributor

@maidmaidmaidmaid commentedJan 2, 2017
edited
Loading

QA
Branch?master
Bug fix?no
New feature?yes
BC breaks?no
Deprecations?no
Tests pass?yes
Fixed ticketsn/a
LicenseMIT
Doc PRn/a

When weextend 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:

$el =newExpressionLanguage();$compiler =function ($str) {returnsprintf('strtoupper(%s)',$str);};$evaluator =function ($arguments,$str) {returnstrtoupper($str);};$el->addFunction(newExpressionFunction('strtoupper',$compiler,$evaluator));$el->evaluate('strtoupper("hello")');// return "HELLO"

with this PR:

$el->addFunction(ExpressionFunction::fromPhp('strtoupper'));$el->evaluate('strtoupper("hello")');// return "HELLO"

It includes PHP namespaced function:

$el->addFunction(ExpressionFunction::fromPhp('My\strtoupper','my_strtoupper'));$el->evaluate('my_strtoupper("hello")');// return "HELLO"

sstok reacted with thumbs up emoji
@maidmaidmaidmaid changed the titleEl phpfn[ExpressionLanguage] Add a new class ExpressionPhpFunctionJan 2, 2017
@nicolas-grekasnicolas-grekas added this to the3.x milestoneJan 2, 2017
*
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class ExpressionPhpFunctionextends ExpressionFunction
Copy link
Contributor

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 inExpressionFunction rather than utilizing inheritance:

ExpressionFunction::php('strtoupper')

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

sstok and andersonamuller reacted with thumbs up emoji
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Done

@stof
Copy link
Member

stof commentedJan 6, 2017

Does it work for namespaced functions ? I think it does not in case they don't have the leading\ in the name (well, it works only if the compiled code is placed in the global namespace)

*
* @throws \InvalidArgumentException if given function name does not exist
*/
publicstaticfunctionphp($name)
Copy link
Member

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 likefromPhp?

Copy link
ContributorAuthor

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.

@maidmaid
Copy link
ContributorAuthor

maidmaid commentedJan 7, 2017
edited
Loading

@stof Indeed, for namespaced functions, it's doesn't work; aSyntaxError is thrown that saidUnexpected character "\" around position 0.. To solve this, I throw an exception if given function is not in global namespace. WDYT?

@maidmaid
Copy link
ContributorAuthor

@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.

@stof
Copy link
Member

stof commentedJan 9, 2017

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.
this tells me that the factory would need to allow 2 arguments: the PHP function name, and the EL function name used to expose it.
The EL name could be optional in case the PHP function is not namespaced, reusing the PHP function name. And the factory would throw an exception if the PHP function is namespaced and the EL name is omitted.

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\ in their name.

maidmaid reacted with thumbs up emoji

@fabpot
Copy link
Member

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.

maidmaid reacted with thumbs up emoji

@maidmaid
Copy link
ContributorAuthor

I done the changes that you suggested@fabpot and@stof.

thrownew \InvalidArgumentException(sprintf(
'An expression function name must be defined if PHP function "%s" is in namespace.',
$phpFunction
));
Copy link
Member

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

Copy link
ContributorAuthor

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) {
Copy link
Member

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?

Copy link
Member

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.

Copy link
ContributorAuthor

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.

Copy link
ContributorAuthor

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.

@maidmaidmaidmaid changed the title[ExpressionLanguage] Add a new class ExpressionPhpFunction[ExpressionLanguage] Create an ExpressionFunction from a PHP function nameJan 16, 2017
* @throws \InvalidArgumentException if given PHP function name is in namespace
* and expression function name is not defined
*/
publicstaticfunctionfromPhp($phpFunction,$expressionFunction =null)
Copy link
Contributor

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.

Copy link
Member

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

Copy link
Member

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 toexpressionFunctionName or something else.

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

For clarity, I renamedexpressionFunction toexpressionFunctionName andphpFunction tophpFunctionName.

@lyrixx
Copy link
Member

@maidmaid could you update the PR description to match the new API?

@maidmaid
Copy link
ContributorAuthor

@lyrixx Yes, I updated the PR description now.


$reflection =new \ReflectionFunction($phpFunctionName);
if (!$expressionFunctionName &&$reflection->inNamespace()) {
thrownew \InvalidArgumentException(sprintf('An expression function name must be defined if PHP function "%s" is in namespace.',$phpFunctionName));

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.?

maidmaid reacted with thumbs up emoji
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Done

Copy link
Member

@nicolas-grekasnicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

👍 with minor comment

@maidmaid
Copy link
ContributorAuthor

It's done@nicolas-grekas :)

@fabpot
Copy link
Member

Apparently, tests are broken when applying these changes.

@maidmaid
Copy link
ContributorAuthor

I fixed tests@fabpot.

@fabpot
Copy link
Member

@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.phpindex 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?

@fabpot
Copy link
Member

@maidmaid Can you submit a PR on the docs to document this new feature?

@fabpot
Copy link
Member

Thank you@maidmaid.

@maidmaid
Copy link
ContributorAuthor

Thank you to you@fabpot and others reviewers. I created doc PR.

xabbuh added a commit to symfony/symfony-docs that referenced this pull requestFeb 28, 2017
…hp (maidmaid, javiereguiluz)This PR was merged into the master branch.Discussion----------[ExpressionLanguage] Document ExpressionFunction::fromPhpcfsymfony/symfony#21122Commits-------415c4ec Update extending.rst478400a Minor reword059155f Add versionadded6c9e09d Fix typoc70ea7f Fix typo6d9af37 Add fromPhp tip
@nicolas-grekasnicolas-grekas modified the milestones:3.x,3.3Mar 24, 2017
@fabpotfabpot mentioned this pull requestMay 1, 2017
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@fabpotfabpotfabpot left review comments

@lyrixxlyrixxlyrixx left review comments

@nicolas-grekasnicolas-grekasnicolas-grekas approved these changes

+1 more reviewer

@unkindunkindunkind left review comments

Reviewers whose approvals may not affect merge requirements

Assignees

No one assigned

Projects

None yet

Milestone

3.3

Development

Successfully merging this pull request may close these issues.

7 participants

@maidmaid@stof@fabpot@lyrixx@nicolas-grekas@unkind@carsonbot

[8]ページ先頭

©2009-2025 Movatter.jp