No history yet

Action and Filter Internals

The Global Hook Registry

To understand how hooks work under the hood, you first need to meet the $wp_filter global variable. This single variable is the heart of the WordPress Plugin API. It's a large, multidimensional array that acts as a central registry for every action and filter hook.

Think of it as a meticulously organized filing cabinet. The top-level keys of the array are the names of the hooks, like the_content or wp_head. Each of these keys points to another array, where the keys are numerical priorities (10, 20, etc.).

Finally, each priority level contains an array of the actual callback functions registered to that hook at that specific priority. This structure ensures that WordPress knows exactly which functions to call, for which hook, and in what order.

// A simplified look at the $wp_filter global array
$wp_filter = [
    // The hook name, e.g., 'the_content'
    'the_content' => [
        // Priority level, e.g., 10
        10 => [
            // Unique ID for the callback
            'wpautop' => [
                'function' => 'wpautop',
                'accepted_args' => 1
            ],
            'another_callback' => [
                'function' => 'another_callback_function',
                'accepted_args' => 1
            ]
        ],
        // Another priority level
        20 => [
            'my_custom_filter' => [
                'function' => 'my_custom_filter_function',
                'accepted_args' => 1
            ]
        ]
    ],
    // Another hook name
    'wp_head' => [
        // Callbacks for the wp_head hook...
    ]
];

Registering Callbacks

When you use add_action() or add_filter(), you're not performing some magic. You are simply adding an entry into that $wp_filter global array. In fact, add_action() is just a straightforward wrapper for add_filter(). They both do the same thing: register a callback.

The function takes your callback, the hook name, the priority, and the number of accepted arguments and neatly places them in the correct spot within the $wp_filter array. If the hook or priority level doesn't exist in the array yet, WordPress creates it on the fly.

Essentially, add_filter('hook_name', 'my_function', 10, 2); is telling WordPress: "Add my_function to the $wp_filter['hook_name'][10] array, and note that it accepts 2 arguments."

Firing the Hooks

Registering hooks is only half the story. The other half is executing them. This is handled by two different functions: do_action() and apply_filters().

do_action('some_hook') is used when WordPress reaches a specific point in its execution and needs to allow plugins or themes to perform tasks, like enqueueing a script. It looks up 'some_hook' in the $wp_filter array, loops through all the registered callbacks in order of priority, and executes them one by one. It doesn't expect anything back.

apply_filters('some_filter', $value) is different. It's designed to modify data. It also loops through the callbacks for 'some_filter', but it passes a value ($value) to the first function. The return value of that function is then passed to the second function, and so on. The final, modified value is what apply_filters() returns.

Both of these functions rely on a core PHP function, call_user_func_array(). As WordPress iterates through the callbacks for a hook, it uses call_user_func_array() to execute each one, passing along the required arguments (like the value being filtered, or other contextual data).

With this understanding of the internal mechanics, you can better predict how and when your code will run, and debug issues related to hook execution order and data modification.

Quiz Questions 1/5

What is the name of the global variable in WordPress that acts as the central registry for all action and filter hooks?

Quiz Questions 2/5

Functionally, what is the main difference between add_action() and add_filter()?