Opened 16 months ago
Last modified 16 months ago
#19986 new defect (bug)
Issue with add_filter() and add_action() when using anonymous functions.
| Reported by: |
|
Owned by: | |
|---|---|---|---|
| Priority: | normal | Milestone: | Awaiting Review |
| Component: | Plugins | Version: | 3.3.1 |
| Severity: | minor | Keywords: | has-patch |
| Cc: | pagesimplify, kpayne@… |
Description
Referencing File: /wp-includes/plugin.php :: function add_filter()
function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
global $wp_filter, $merged_filters;
$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
unset( $merged_filters[ $tag ] );
return true;
}
When registering a function with add_filter() and add_action(), a unique id ($idx) is generated to be used as an array key in:
$wp_filter[$tag][$priority][$idx]
The function that creates this unique key ($idx) returns the function name passed into the $function_to_add parameter.
$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
If the $function_to_add parameter is a string, the function _wp_filter_build_unique_id() returns the original function name.
function _wp_filter_build_unique_id($tag, $function, $priority) {
global $wp_filter;
static $filter_id_count = 0;
if ( is_string($function) )
return $function;
...
}
Returning a string value for the unique id is a problem when the function is an anonymous function generated using create_function(). The reason is anonymous functions are named as "special character + lambda_XX" or as "ºlambda_XX" where the first character cannot be displayed.
See attached file: anonymous_function_name.jpg
Since array keys can only be integers or strings, the following array is invalid and causes inconsistent behaviors.
$wp_filter[$tag][$priority]['ºlambda_XX']
Although I've seen instances where this will still work, I've also observed strange issues with inconsistent results where plugins should work and sometimes do not. One example is with Otto's Simple Facebook Connect plugin.
See file: sfc-widgets.php
add_action('widgets_init', create_function('', 'return register_widget("SFC_Fan_Box_Widget");'));
I have not been able to isolate the conditions that allow this to work in one WordPress installation and not in others. However, I have been able to apply a patch to my installation with consistent and successful results.
See attached patch: plugin.php.diff
While this is an obscure and possibly very isolated issue, it could resolve unexplained issues related to using anonymous functions with add_filter() elsewhere.
Attachments (3)
Change History (7)
pagesimplify — 16 months ago
- Severity changed from normal to minor
While this looks harmless enough, I don't think we should commit it until we at least isolate the conditions under which it appears (PHP version + OS combination perhaps)?
comment:2
pagesimplify — 16 months ago
- Cc pagesimplify added
Unfortunately, I noticed this in January 2011 and applied this patch to my WordPress installation then. So it's been a long while since I have observed the various conditions where I noticed this being an issue. I can say that I recall the issues were noticeable more between plugin/widget configurations for certain pages than between different system environments.
If I stretch my mental muscles just a bit more, I recall suspecting the issue was related to the combination of several properly named functions registered with an anonymous function, or perhaps multiple anonymous functions for a specific filter tag. I discovered the issue when attempting to step through code in a plugin that wasn't running properly. As I moved the break points up the possible callstack to the apply_filters function, I noticed I could never step past a certain number of function calls in the apply filters loop. This was because the apply_filter function could not retrieve the array item stored with an invalid array key name. I can't recall if this would work if no other functions were registered or if it was the last item in the array.
In this specific case, I do recall that by removing some widgets from a page or by deactivating other plugins, I could get the problematic filter to run. However, this is still anecdotal in that I never tested to isolate all conditions for this issue to occur. Once I isolated the issue while troubleshooting Otto's SFC plugin, I simply applied the fix and the very random issues I previously experienced were no longer an issue.
It may also be helpful to know I mostly run multisite installations and will manage many blogs running in the same network. So, I was encountering random issues with many different sites on the same network using different themes, activated plugins, and widget configurations. Again, problems went away after applying the trim() function to the string return value.
Hopefully, this can resolve a lot of random issues people experience with no real explanation.
comment:3
follow-up:
↓ 4
SergeyBiryukov — 16 months ago
Anonymous function names indeed start with a null character, in order to ensure they don't clash with user-defined functions:
That said, null is also a valid array key (will evaluate to an empty string):
http://www.php.net/manual/en/language.types.array.php#30132
Both of these examples seem to work:
$foo = array( null => 'bar' ); echo $foo['']; $foo = array( chr(0) . 'key' => 'bar' ); echo $foo[ chr(0) . 'key' ];
- Cc kpayne@… added
Replying to SergeyBiryukov:
Anonymous function names indeed start with a null character, in order to ensure they don't clash with user-defined functions
You beat me to it!
Replying to pagesimplify:
Hopefully, this can resolve a lot of random issues people experience with no real explanation.
It doesn't look like the patch shouldn't cause a problem. But a patch like this has a lot of implications. It would probably be best to understand exactly what broke to begin with.

Anonymous Function Name viewed during XDebug