Understanding the fundamentals of WordPress hooks

A practical account on how we can utilise WordPress hooks within our theme and plugin development.

By Luke Whitehouse on

PHPWordPress

Article

WordPress hooks are predefined checkpoints built directly into the Content Management System (CMS). They allow you to tap into WordPress' core functions without altering the source itself, providing Theme and Plugin developers with all the tools they need to build standalone components for WordPress.

Within my last post on 'How to create WordPress dashboard widgets' I touched upon the idea of using WordPress hooks to modify it's Dashboard. This post will serve as follow-up in order to demystify the Plugins API from which these hooks are built upon and subsequently provide examples on how you can utilise the system for your own needs.

What are WordPress hooks?

... and what exactly do I mean by "predefined checkpoints"? Well, when we're playing a video game we often think of a checkpoint as a save point, a point within a game where your progress is saved so that if you die, you can start again from that point, which is not too dissimilar from WordPress hooks.

Whilst WordPress is running it has a series of events that run at different times, these could be anything from the initial load of the system or something that only occurs when an user interacts with the website, such as activating a plugin. Whatever the task, WordPress will have a hook for it, allowing you to execute a piece of code whenever that task is flagged up.

Hooks are split up into two types: Filters and Actions. Let's take a look at them in more detail.

Filters

Filters allow you to modify the value of whatever is 'hooked'. For example, we may want to modify every post's content area to wrap it within some HTML before it's printed to the screen using functions like get_the_content. This can be achieved by using a filter aptly named the_content.

To create a filter within WordPress we must use the add_filter function which takes four arguments. For now, lets take a look at the first two (more on the other two later):

  • $tag - The name of the hook you'd like to apply a filter to.
  • $function_to_add - The name of the function you'd like to run to modify the hook's value.

Using our example of modifying the HTML of any get_the_content function we need to create a new filter using the add_filter function, where we'll pass in the name of the hook we want to filter (in this case it's also called the_content) and the name of the function we will be using to modify post content's value, which we'll name asrt_wrap_post_content.

add_filter('the_content', 'asrt_wrap_post_content');

Now that we've defined our filter, we need to create the function underneath.

add_filter('the_content', 'asrt_wrap_post_content');

function asrt_wrap_post_content() {
  // Modifications to go here
}

As each hook in WordPress is built for a single purpose, their implementation is ever so slightly different from it's peers. This is most notable when looking at arguments the attached function requires (which we've named asrt_wrap_post_content) as the number and type of arguments depends on the hook itself.

For example, one filter may expose two arguments for you to work with in your new function, whereas another may provide you with four, it really just depends. These can all be found on the filter's Codex Reference, however in this case the hook we're using (the_content) expects one argument named $content, which will give us access to the post's content to interact with and modify.

add_filter('the_content', 'asrt_wrap_post_content');

function asrt_wrap_post_content($content) {
  // Modifications to go here
}

Using this $content variable we can create our HTML wrapper and send it back on it's way to be used within a theme through get_the_content.

add_filter('the_content', 'asrt_wrap_post_content');

function asrt_wrap_post_content($content) {
  // Wrap post content in .wrapper
  $content = '<div class="wrapper">' . $content . '</div>';

  // Return modified $content for theme to output
  return $content;
}

Now that we've looked at an example, we can go a step further to understand how this affects the flow of WordPress itself. As we now know, a filter can be used to modify the core values of WordPress, so by default we have these core values in WordPress which are fed through to our templates.

The WordPress operations flow before a filter is introduced

Fig 1: The WordPress operations flow before a filter is introduced

We can then use our add_filter function in combination with the the_content hook to modify the value before it is passed to the templates.

The WordPress operations flow after a filter is introduced

Fig 2: The WordPress operations flow after a filter is introduced

Actions

On the flip side, we have actions, a more grown up filter in many ways. With filter's we hook into an existing function and provide a middle man for the data to pass through before it can go on its way. However, with an Action you could create a function to be executed in parallel to that one, or rather, with an Action we're defining a function to be executed at that point, not necerssarily always interacting with any core WordPress values and passing them through to the templates. Yes, that hook may provide us with data to interact with if we so wish, but we're definitely not made to do so, whereas with a filter you are required to return the data you were given.

I appreciate that's a little wordy, so let's break that down with some cool little flow charts.

Let's imagine an ordered list of operations WordPress needs to complete before the website and admin area are loaded. Unfortunately, real life is never this easy but it'll be a good way to get our heads around Actions.

  1. WordPress loaded
  2. Plugins loaded
  3. Theme loaded
  4. CMS content loaded
  5. ... and so on

Okay, so rather than hooking into a value like filters, we're instead hooking into the points when these operations are being executed.

The WordPress operations flow before an action is introduced

Fig 3: The WordPress operations flow before an action is introduced

Then, we create an action to execute a new function just after that Event, in this case 'WordPress event 1' or 'WordPress loaded'.

The WordPress operations flow after an action is introduced

Fig 4: The WordPress operations flow after an action is introduced

Noticed the new function? Just like with the filters, to create an action within WordPress you must use the add_action function. This function takes the same parameters as add_filter, so nothing new here but we're just focusing on those first two parameters again: $tag and $functions_to_add.

As shown in the last image, we're going to register some WordPress menus, by utilising the init WordPress hook along with the add_action function.

add_action('init', 'asrt_register_nav_menus');

Once defined, we can then create the new function underneath.

add_action('init', 'asrt_register_nav_menus');

function asrt_register_nav_menus() {
  // Modifications to go here
}

Based on the flow chart images above we can see that no data needs to be passed through to the next checkpoint like with filters, so all we need to do now is create our menus using the register_nav_menus function.

add_action('init', 'asrt_register_nav_menus');

function asrt_register_nav_menus() {
  // Define menu arguments
  $args = array(
    'secondary' => __( 'Secondary' ),
    'tertiary'  => __( 'Tertiary' )
  );

  // Register new menus
  register_nav_menus( $args );
}

Ordering hooks

Going back a step, the third parameter for both add_action and add_filter functions is $priority. Imagine you've created two functions to both be executed on a hook but you need to make sure one is always executed before the other, this is where $priority comes into play.

With a default of 10, $priority allows you to change the ordering of when a function is executed on a hook, the lower the integer the higher up the pecking list the function is.

Assuming we've got a few functions all trying to manipulate the init hook.

add_action('init', 'asrt_register_nav_menus');
add_action('init', 'asrt_update_post_type_features');
add_action('init', 'asrt_another_function');
add_action('init', 'asrt_even_more_functions');

In this case we can pass in a 3rd parameter to add_action to dictate when each of them are executed.

add_action('init', 'asrt_register_nav_menus', 2);
add_action('init', 'asrt_update_post_type_features', 3);
add_action('init', 'asrt_another_function', 1);
add_action('init', 'asrt_even_more_functions');

Here's the order in which they'll be executed:

  1. asrt_another_function - Ordering starts at 1
  2. asrt_register_nav_menus - The order in which they're wrote is only taken into account when you have two functions with the same $priority value.
  3. asrt_update_post_type_features
  4. asrt_even_more_functions - Default value is 10.

This parameter is especially useful when building plugins for a wider audience that just yourself, as the plugin may be needed to integrate with a theme which also manipulates the same hook, so providing a difinite priority (be that higher or lower) can help alleviate any conflicts.

Number of arguments

Following on from ordering our hooks, the fouth parameter for both add_filter and add_action is $accepted_args and this defines the number of arguments the function you're attaching to a hook will have.

// Remember, this won't work until you add in your $priority arguement

add_action('wp_insert_comment', 'asrt_validate_comment', $priority, 2);

Unfortunately there isn't much documentation on why this parameter is actually required, so I'm lead to believe its purely as a best practice so anyone looking at your code will know how many parameters the attached function should have. In addition, in order to know how many parameters the function will require, you'll need to go to the WordPress documentation.

In this case the wp_insert_comment hook provides 2 parameters: $comment_id and $comment_object so that's the number we'll use.

Note: If anyone knows why this argument exists I'd love to hear about it, send me a quick message.

Take a look at the Action Reference and Filter Reference for a full listing of hooks.

Reality of actions vs filters

Taken all that in? Great, because I've sort of just lied to you. In an ideal world what I've said throughout this post would be true, but we're not in an ideal world.

Sadly the differences between Actions and Filters in WordPress are more of a concept or best practice approach to working with WordPress, rather than a fact. Don't believe me? Take a look at the source code for WordPress (4.6 in this case), you'll notice the add_action function is really just a glorified wrapper for add_filter, and doesn't do anything special, so you could technically use the add_filter for everything if you wanted.

That being said, I'd still recommend approaching WordPress hooks in this way, as does the whole development community, otherwise you'll run into a host of conflicts with Plugins and Themes when they don't expect a filter or hook to do something because you've used the wrong function. You can read more into the 'why' over on this blog post from 2011.

Further reading

Now that you've got a handle on the basics of WordPress hooks, it's time to use them in the real world. Practice using common hooks like init and admin_init to modify WordPress to your needs, you'll find that you can achieve some pretty robust features with little to no code. The possibilities are endless.

Until next time :)

Follow us on Twitter, Facebook or Github.
© Copyright 2021 Assortment.
Created and maintained by Luke Whitehouse.