-
-
Notifications
You must be signed in to change notification settings - Fork 488
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
Warn against lambda callbacks in add_filter() and add_action() #1486
Comments
Not true: |
Well, I agree with @azaozz that closures can not easily be removed from the With that in mind, I would support a sniff which would warn - not error - when a closure is used as the callback parameter when hooking into WP. Having said that, I totally disagree with prohibiting or even discouraging closures in general. Restricting the use of constructs like The only thing I could imagine doing in that space would be to, again, warn, when a closure is more than five statements with the suggestion of making it a proper function instead. The number |
I think this paragraph from the second link provided by @GaryJones is relevant (although of course it's beyond the scope of this discussion):
If a sniff is still going to be added, then I agree with @jrfnl that the sniff should only be a warning and not an error. I also agree that the number of statements in an anonymous function should be configurable. |
@GaryJones hmmmm, this is a quote from one of the links you referenced:
I.e. you can "blindly" remove lambda callbacks in a very hacky way. Don't think that's an acceptable practice, not even close to "best practices", etc. :) The fact is that using anonymous callbacks in I agree with @jrfnl that a warning would be sufficient there. Also thinking this particular case can perhaps be handled in the code ("Doing it wrong", etc.). The general problems I'd like to address with this ticket are that:
As I've said above and as @jrfnl points out, the purpose here is not to "ban" all lambda functions, but to set "expectations" and "best practices". As they are not yet officially supported in core, now would be a good time to do so. |
"Doing it wrong" seems excessive. Isn't that prohibitively difficult to disable for private projects, where all @azaozz listed lambda function related concerns can be handled perfectly fine? Coding Standards is nicely configurable via XML, as long I can turn these off, whatever above sounds good to me for core guidelines. |
I agree - it's not clean, and it's not intuitive, but it is possible, which is exactly the opposite of what you first claimed. Please use accurate statements and avoid absolutes.
Also agreed. There may be cases when a developer adds a filter with the intent that at least one callback is "always on"(for whatever reason), and using an anonymous function / closure would intentionally make it harder for consuming developers to remove that initial callback.
Disagree. Using a closure as a hook callback isn't doing things wrong - it's just potentially against the coding standard, and shouldn't result in a change of behaviour at run-time.
Be clear here - anonymous functions can be assigned to a variable, so what you're really wanting to target is where the second arguments of |
I think you mean the |
@GaryJones uh, ok, lets try this again, hopefully without misunderstandings this time :) The problem with using lambda functions as callbacks in
Whether it is warranted to throw "doing it wrong" when we detect such use is another question (for a core ticket). There have been some discussions about it but no clear conclusion for now. I personally would prefer if this is handled in the coding standards, however there is a possibility to handle that in the core code as well.
Yes, it is possible (in a hacky, unsupported way) to remove a single lambda callback, however it is not possible to remove a callback when there are two or more lambda callbacks with the same priority. This type of removal will also be "doing it wrong" as it messes with the underlying (private) API data. |
There are some valid use-cases for anonymous functions in Take for example something like this: $sidebars = apply_filters( 'my_custom_filter_1', array(
'sidebar_1' => true,
'sidebar_2' => true,
'sidebar_3' => false,
) );
add_filter( 'my_custom_filter_2', function( $args ) use ( $sidebars ) {
foreach ( $sidebars as $key => $val ) {
// Do something.
}
return $args;
} ); Though the above example is pretty simple and can be written without an anonymous function, depending on the data we have and operations we want to perform sometimes closures is the best option. |
@aristath right, I think so too. Also, could you add an example of how you can use The whole point here is that anonymous callbacks break that part of the API. |
There is no way that I know of. The point I wanted to make is that I have encountered instances where using anonymous functions is the best (if not only) way to handle some complex situations. I'm not against discouraging the use of anonymous methods in Also in case this is implemented, it would be really useful to have a whitelisting tag like |
Just so you know: The |
I think that the name of the issue should be renamed to Just to avoid the confusion, as I think that this is what @azaozz had in mind in the first place. |
I think it's worth pointing out that this is a limitation of the API itself. Anonymous functions are an essential part of the PHP language. Like anything else, they could certainly be used improperly. But they have a lot of advantages that shouldn't be set aside simply because the current WordPress API doesn't know how to deal with them. The fact that the API isn't currently designed with these in mind means that the API needs to be updated to take these into account. As @aristath mentioned, there are instances where using anonymous functions is the best, and sometimes only, way to handle complex situations. Rather than reinforcing the limitations of the API itself, the API should be updated. This is especially relevant when the move away from PHP 5.2 is taken into account. If anything, this should be a temporary warning until the API can be made to handle anonymous functions properly. |
As long as WordPress is PHP 5.2 backward compatible, I doubt that is going to change... |
How is this different than a class instance doing this (where the instance is not exposed)? class SomeClass {
public function __construct() {
add_action( 'some_hook', array( $this, 'some_method' ) );
}
public function some_method() {
echo 'Hello';
}
} I agree with this statement by @JPry
|
Hmmm, I'm quite surprised at how many people are so eager to use a coding pattern that breaks an existing API. IMHO it would be helpful if everybody here mentioned what their involvement with coding standards is and why they think it is a good idea to do so :) Theoretical example:
How many plugins to you think will break? Maybe enough to "bring down the internet"? :) |
That class can be extended, and the methods overloaded, right? :) But yes, when it comes to OOP the "management" of what runs when is shifted to classes and sub-classes. |
Absolutely agree.
Absolutely agree again. All commenters here are very welcome to add any ideas for updates/fixes to the plugins API that will allow anonymous callbacks to be removed easily, by using Also agree that until this is patched in core we need to warn against using anonymous callbacks there. |
Yes, but that doesn't help with removing a filter using that instance. |
Right, but the decision to keep that instance "private" makes that non-removable (actually it can still be removed by doing |
I very rarely go against suggestions in this project, but for this one I feel its worth speaking up. I would like to see this discussion going back to #core-php for more input and perhaps a make post. This is quite a drastic measure, even as a warning. There are times where you might like to extend your own plugin and perhaps you'd like to implement a "reverse hook" mechanism in a class method (or constructor) to perhaps do something like... public function __construct( $namespace ) {
add_action( 'the_thing_i_trigger_elsewhere', function( $arg1, $arg2 ) use ( $namespace ) {
// Do something with $arg1, $arg2 and $namespace.
} )
} I'm not saying the above is a valid use case per se, you could assign Some more discussion would be welcomed. If the community says, "ban it!" then that's fine. This particular sniff just worries me a bit, especially with seasoned developers who may have a very legit reason for doing this. I guess that warrants the argument for a |
I personally would be against this (adding But I'm definitely for discussing this on a larger scale since it touches the subject of rewriting the important API in the WordPress, and would definitely like to hear what some experienced developers have to say about this :) |
@dingo-d I agree. Was using it as an example...
|
Yes in general it is a bad idea. I was just illustrating the point that what is being warned against here doesn't seem much different than a similar pattern I've seen elsewhere. This points to the larger issue of a flaw in the plugin action/filter api (granted not a trivial thing to fix for back-compat etc). |
Vehemently against discouraging closures. WP is very hook centric and sometimes complex use cases require not just using an existing callback, but manufacturing a very custom callback with scope on the fly. Closures are the most convenient and succinct mechanism for it at the moment. Alternatives like object factory or anonymous class would be just as hard to remove without access to instance. If removing is the problem then removing should be improved. There are abundant existing materials on removing complex callbacks, as well as battle–tested APIs to do it conveniently such as identifying them by a string representation. For example Brain Monkey testing lib allows to easily identify closures in hooks like this: |
I've opened a trac ticket with suggestion to improve the API: Improve identifying of non–trivial callbacks in hooks |
Sorry but I think the discussion here is not going in the right direction :) The facts are that closures in WP actions and filters break the current API. Ideally the coding standards should warn the developers that they are doing something wrong. Whether closures are allowed in WP hooks or not is not something the "Coding Standards" should decide. However the "Coding Standards" shouldn't fail to warn the developer that an API is used incorrectly. IMHO this is one of the good things about having coding standards in the first place :) |
So do objects by that logic. Thought the plan was to move WP development forward, not backwards. |
@Rarst I think you are still talking about how WordPress works and not about "coding standards". The ticket you opened on trac would probably be a better place to decide what to do with plugins and themes that break the existing API? :) |
Related: PHPCSStandards/PHPCSExtra#192 |
I've opened a PR to address the concern about long closures - PR #2311. While it doesn't directly address the use of closures for hooks, it does address other remarks which were made in this thread. Please have a look at the PR and leave an opinion if you have one. |
(Follow-up from #1485).
Currently (because of PHP 5.2) lambda/anonymous functions cannot be used in core. However once we move on to requiring a more recent PHP version, this will become possible.
Generally using lambda functions should be (strongly) discouraged as they usually miss documentation and context. There are some exceptions: for example very short callbacks in calls to
array_filter()
,array_udiff()
,usort()
, etc. may be better to be "inline lambda" rather than defined separately.However in case of
add_filter()
andadd_action()
using lambda functions is "harmful". The anonymous callbacks cannot be removed and "break"remove_filter()
andremove_action()
.In that terms thinking we need a sniff to warn when a lambda callback is used in general (in case the code can be written better), and perhaps another sniff (or another configuration of the same sniff) specifically targeting
add_filter()
andadd_action()
and throwing an error when a lambda callback is detected there.The text was updated successfully, but these errors were encountered: