How does filters and hooks actually work in WordPress?
I’m asking about something advanced. How is it implemented in PHP? E.g how does it collect all the hooks from the different plugins and “attach” them to the core hooks etc.
Overview
Basically the “Plugin API,” which summons Filters and Hooks, consists out of the following functions:
-
apply_filters()
– execute -
do_action
– execute -
apply_filters_ref_array()
– execute -
do_action_ref_array()
– execute -
add_filter()
– add to stack -
add_action()
– add to stack
Basic Internals
Overall there’re a couple of globals (what else in WordPress world) involved:
global $wp_filter, $wp_actions, $wp_current_filter, $merged_filters;
The first one $wp_filter
is a global Array
that holds all filter names as subarrays. Each of those subarrays then holds even more subarrays that are callbacks summoned under a priority array.
Brief in-depth
So when an execute function is called, WordPress searches those globals arrays for the keys with that name. Then the attached callbacks get executed priority after priority. The only thing that happens up front are callbacks attached to the all
filter.
When you add a callback using add_action
or add_filter
, then WordPress first calculates a “unique” ID to not overwrite already attached callbacks.
$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
Then it adds your callback to the global $wp_filter
stack:
$wp_filter[ $tag ][ $priority ][ $idx ] = array(
'function' => $function_to_add,
'accepted_args' => $accepted_args
);
As you can see the main sub array is the $tag
(or action/filter name), then everything is summoned under a specific priority, and then the “unique” callback/ID string gets used as key.
Later, when a filter gets called – happening with the $tag
/action-/filter-name – the array gets searched and the callbacks get called. As it is using call_user_func_array
it doesn’t really matter how many arguments are attached. WordPress resolves that by itself.
foreach ( (array) current( $wp_filter[ $tag ] ) as $the_ )
{
call_user_func_array(
$the_['function'],
array_slice(
$args,
0,
(int) $the_['accepted_args']
)
);
}