WordPress hooks, actions and filters explained

Written by Mark Wilkinson on September 9, 2018

Read this definitive guide to how WordPress actions and filters, known as hooks work.


Introduction

I have been using WordPress for many years – all the way back to version 2.0 in 2005 and I am now co-owner of a WordPress agency, here at Highrise Digital. A key breakthrough for me, in terms of being a WordPress developer, was when I finally fully understood how to edit the behaviour of WordPress and plugins and themes using hooks.

I hope by reading this post that you will come away with a better understanding of what actions are, how they are used and how you can use them to extend WordPress and of course your own plugins and themes.

What are WordPress hooks and how do they work?

Hooks are what WordPress uses to allow one piece of code to interact with and/or modify another piece of code. For example, a plugin could modify another piece of code which WordPress runs. WordPress also makes extensive use of hooks to modify its own code.

Hooks are a way for one piece of code to interact/modify another piece of code.

WordPress.org

Hooks are named events (such as save_post) to which developers can attach functions. The name is the reference you use to attach (or hook!) your function to. When the hook is run, at a certain time in the WordPress load process, the functions added to the hook are executed.

A little bit about terminology here. Functions that we attach to a hook are known as ‘callbacks’, in that the process of running a hook, calls your function to run.

Multiple callbacks can be added to a hook, therefore we need a way to order the callbacks so we can determine which runs first, last and in-between. WordPress hooks enable us to assign each callback with a priority number (the default if you don’t add a priority is 10). Therefore, callbacks added to a hook with a priority of 20 will be run after those added with a priority of 10.

What are the different types of hooks?

There are two types of hooks in WordPress, actions and filters. They are very similar, but they behave in a slightly different way and therefore it is worth pointing out the difference. Let’s take a look at each.

Action hooks

Actions in WordPress allow us to add data or change the way in which WordPress or third party extensible code operates. Any callbacks (remember these are just functions) added to an action are run at a specific point when WordPress loads.

Callbacks added to actions could potentially do anything, for example, you could echo out some content at a certain point, insert something into the WordPress database or even interact with an external service.

You will see action hooks in WordPress called using the do_action() function. Callbacks are attached to actions with the add_action() function. Callbacks can also be removed using the remove_action() function.

Filter hooks

Filters in WordPress are all about changing data during the execution of WordPress. A callback attached to a filter will accept data in the form of a variable, make some changes to that data, and then return the variable back (this is so other callbacks attached to the filter can also use it).

Callbacks added to filters will modify the data that the hook provides such as modify the arguments in a query or changing the value of a string.

You will see filter hooks in WordPress called using the apply_filters() function. Callbacks are attached to filters with the add_filter() function. Callbacks can also be removed using the remove_filter() function.

The difference between ‘action’ and ‘filter’ in WordPress

The biggest difference between actions and filters, and the important thing to remember as a developer, is that with filters, callbacks must always return the filterable data as a variable. Callbacks attached to an action don’t need to return anything.

Always remember, when using filters, your callback must always return the original data so it can be passed on to the next callback or to WordPress iself.Mark Wilkinson

Adding callback functions to an action or filter hook

Let’s take a look at how we can hook into WordPress using the hooks system to add our own functionality or modify existing functionality, by adding callbacks using actions and filters. Below is an example of each.

An action example

To help understand how action hooks work, let’s take a look at a practical example. The normal WordPress login page looks something like this.

A screenshot showing the default WordPress login screen.
Standard WordPress login screen

We have often found that clients want to include more information on the login page, for example, to remind users which email address they should use for logging in. Adding this below the login form is ideal. WordPress allows us to do this thanks to the use of an action. When WordPress outputs the login form content it includes the following line of code:

do_action( 'login_footer' );

This means that we can attach a callback function to this action hook in order to output some content on the page. For example, we could add a simple instructional sentence with the following code:

<?php
/**
 * Adds a message to the footer area under the login form.
 */
function hd_login_footer_message() {
	
	// output our message in the login footer area.
	?>
	<p style="border-top: 1px solid #0085ba; margin: 0 auto; width: 320px; padding-top: 10px;">Use your @highrise.digital email address to login here.</p>
	<?php

}

add_action( 'login_footer', 'hd_login_footer_message' );

Now our login page looks like this, with some footer content.

A screenshot of the WordPress login screen, modified with a WordPress hook, outputting some content below the form.

A filter example

To help understand how action filters work, let’s take a look at a practical example. On the WordPress post edit screens, you will notice in the input where you add the post title, there is some placeholder text which reads “Enter title here”.

Screenshot showing the older WordPress editor.
The WordPress post edit screen with post title placeholder text.

When WordPress outputs this (in the code) it does so with this code:

apply_filters( ‘enter_title_here’, __( ‘Enter title here’ ), $post );

Because the string “Enter title here” is run through a filter (we can tell this because it is wrapped in apply_filters()), it means that we can change it, by attaching a callback function to the filter. This would look something like this.

<?php
/**
 * Alter the enter title here placeholder text.
 * This is on the WordPress post edit screens.
 *
 * @param  string $title The is the current placeholder string.
 * @return string        The modified placeholder string.
 */
function hd_title_here( $title ) {

	// set a new string for the placeholder text.
	$title = __( 'Enter article title here.', 'text-domain' );

	// return the title back to the hook.
	return $title;

}
add_action( 'enter_title_here', 'hd_title_here', 10 );

In this code snippet you can see that we are assigning a new value (a string of what we want the placeholder to be in this case) to the variable $title, and then returning back the variable.

With this running we can now see the post edit screen reflects the change in the post title placeholder text.

Screenshot of the WordPress editor with the placeholder for the post title changed to read "Enter article title here".
The WordPress post edit screen with post title placeholder text edited.

Removing callback functions from an action or filter hook

Let’s take a look at how we can hook into WordPress using the hooks system to remove functionality added by others or WordPress itself, by removing callbacks using actions and filters.

This is the beauty of the hooks system in WordPress because when a developer uses a hook to add functionality, another developer can remove that functionality in a safe way, without directly changing the original code.

When removing filters or actions from WordPress, you must add to your remove function the same arguments with which the callback was added, including the priority and number of arguments being passed to the callback. If they don’t match it means the remove will not be successful.

Below are examples of removing callbacks added by filters and actions.

An example of removing an action

If you have ever examined the source code of a WordPress page you will notice that it has a ‘generator’ meta element in the head of the page which indicates that WordPress generated the page, including the version of WordPress being used.

Since this is added using a hook, it means that developers can remove this using the hook system in WordPress. To remove this we could use the following code

remove_action( 'wp_head', 'wp_generator' );

This could be placed into a plugin or even into your themes functions.php file. This code removes the wp_generator callback function that is added to the wp_head hook in WordPress. The remove_filter() function works in the same way.

A note about coding style

One thing that a lot of WordPress developers have said to me which they are confused by is that it seems as though other developers use hooks in different ways. When I look at the examples they give, it is purely a different way of writing the code, rather than using them differently in terms of functionality.

When hooking into an action in WordPress (adding your callback) there are two parts to the code.

  1. Declaring your function which you want to add to the hook
  2. Calling the add_action or apply_filters function, which declares the name of your function you want to hook in

Many developers write them in the order above and this is my preferred way. You declare the function first and then directly below that declaration you attach it to the action.

However, you may often see code written with the function attached to the action first and then declared underneath. This makes no difference and there is no right or wrong way to do it – just personal preference.

Some developers even declare all the hooked functions (the callbacks attached to hooks) in one or multiple files and then have all the add_action or apply_filter calls in another file. Again this is just developer preference, but I prefer to have the function declaration and it being hooked into to the action or filter together in the same place.

The final thing you may see is a style of writing that looks something like this:

add_action( 'init', function() {
    // Do something.
});

In this example, you can see that the function attached to the init hook in WordPress, does not have a name. It is being declared right within the call to add_action rather than being declared above or below and then referenced. These are called anonymous functions. I would not recommend using them with WordPress hooks because it makes callback functions hard to unhook, as they don’t have a name you can reference.

How do hooks work?

At first, it can be hard to get your head around how hooks work, and therefore to try and help understand this we are going outline in simple steps what happens when a WordPress hook is fired (this seems to be a developer term for running the functions do_action and apply_filters).

  1. WordPress looks for all the callback functions attached to this particular hook. Think about it as adding them to an invisible list.
  2. WordPress checks for any calls to remove actions or filters from this hook, and if there are calls to remove_action or remove_filter for this hook, it removes these callbacks from the list.
  3. The priority of the added callbacks is then referenced and the callbacks are placed in the list in order of their priority parameter, the callback with the lowest priority goes first on the list and the callback with the highest priority goes last and all the rest in between in order – low to high.
  4. WordPress now has a list of callback functions to run at this point in the load process and it does so, executing each callback function in turn after the previous one.

As you can see from the list above, using WordPress hooks is a very efficient system because no callback functions are actually executed until the very final step. This means that adding and removing the callbacks from the invisible list is efficient as the actual callbacks are not executed until we have a definitive list of what we actually need to run.

Passing variables to callback functions

Hooks are great at allowing developers to run code at certain points in the WordPress load process. However, they can be even more useful because certain hooks provide variables to the callbacks.

These variables allow the callbacks to do a lot more, sometimes targeting specific things and sometimes changing data in WordPress. Let’s take a look at how this works.

Let’s go back and take a look at our previous filter example, where we looked at changing the default post title placeholder text in the admin screens.

<?php
/**
 * Alter the enter title here placeholder text.
 * This is on the WordPress post edit screens.
 *
 * @param  string $title The is the current placeholder string.
 * @return string        The modified placeholder string.
 */
function hd_title_here( $title ) {

	// set a new string for the placeholder text.
	$title = __( 'Enter article title here.', 'text-domain' );

	// return the title back to the hook.
	return $title;

}
add_action( 'enter_title_here', 'hd_title_here', 10 );

This is great, but of course, it would change that placeholder text for all post types in WordPress – posts, pages and any other custom post types too. What if we wanted to only change the title for posts, and not other post types? Well, thanks to the ability for hooks to pass variables to their callback functions we can!

Take a look below at a modified version of this code:

<?php
/**
 * Alter the enter title here placeholder text.
 * This is on the WordPress post edit screens.
 *
 * @param  string $title The is the current placeholder string.
 * @return string        The modified placeholder string.
 */
function hd_title_here( $title, $post ) {
	
	// if this post is not the post type of 'post'.
	if ( 'post' !== get_post_type( $post ) ) {
		
		// return the unmodified title placeholder.
		return $title'
	
	}
	// set a new string for the placeholder text as the post type must be a 'post'.
	$title = __( 'Enter article title here.', 'text-domain' );

	// return the title back to the hook.
	return $title;

}

add_action( 'enter_title_here', 'hd_title_here', 10, 2 );

In the code above we have made some small changes.

Firstly, the enter_title_here filter passes the current post object to any callback as well as the title string as a second arg. Therefore, when I called add_action I left the priority at 10, but added another arg after that, a number 2. This indicates that 2 parameters will be passed to the callback attached, the second being the post object as $post.

Then I check for the post type of the current post using get_post_type and pass the current post object to that function. If the current post type is not a post I simply return the original, unmodified placeholder string and if the post type is a post we make our change to the placeholder string before returning it.

Important: The number of variables you are passing to your callback function must be declared as the final argument in either add_filter or add_action.

You can find what variables are available to your callbacks, either by viewing the wordpress.org developer page about the hook, or you can view where the hook is called in the source code and see which variables are made available.

Looking at the tag cloud widget example in the video at the start, we can see that in WordPress core, the filter is defined as:

<?php
apply_filters(
	'widget_tag_cloud_args',
	array(
		'taxonomy'   => $current_taxonomy,
		'echo'       => false,
		'show_count' => $show_count,
	),
	$instance
);

This filter makes 2 arguments available to callbacks. These are the array of args (including taxonomy, echo and show count) and the instance variable. With a filter, it is always the first variable passed to our callback which must be returned.

Using hooks when writing your own code

Hooks are not something that are only used in WordPress core or other people’s plugins. You can, and I would argue that you should, be using hooks in your own code. When I say using hooks I mean making your own code extensible by providing hooks for other developers (which includes yourself of course!) to modify your code in an extensible and safe way.

Where should I insert hooks?

Every plugin or theme is different and therefore there are no hard and fast rules to define this. However, these are some of the scenarios that I think are good candidates for using adding hooks.

Setting up arrays

Often in our code, we use arrays to pass data to functions. A really simple example of this is when we are using WP_Query class to query some data in WordPress. We pass an array into the class of the query parameters. One potential change you could make to your code is to pass this array through a filter. This allows other developers to change those query arguments.

For example, we may often be writing something like this:

<?php
// run a query for the latest 10 jobs.
$jobs = new WP_Query(
	array(
		'posts_per_page' => 10,
		'post_type       => 'wpmark_job',
	)
);

We could make this so much better using a filter, like this:

<?php
// run a query for the latest 10 jobs.
$jobs = new WP_Query(
	apply_filters(
		'wpmark_job_query_args',
		array(
			'posts_per_page' => 10,
			'post_type       => 'wpmark_job',
		)
	)
);

A developer can now change those query args easily. Maybe they have a burning desire to only show the latest 5 posts which they could do using this code, placed for example in their theme functions.php file or in a plugin:

<?php
/**
 * Changes the job query to show only 5 posts per page.
 *
 * @param  array $args The current query args.
 * @return array       The modified query args.
 */
function wpmark_alter_job_query_args( $args ) {
	
	// change the number of posts per page to 5.
	$args['posts_per_page'] = 5;
	
	// return the args.
	return $args;
	
}

add_filter( 'wpmark_job_query_args', 'wpmark_alter_job_query_args' );

Function returns

Another place I like to add hooks, usually in the form of a filter, is in function returns. Instead of just returning a string, value or an array in the function, return it by running it through a filter first, even passing relevant data to the callback such as a post object so a developer can change the return value if needed. Here is an example from a plugin I built.

<?php
/**
 * Gets the username of the logicmelon feed as set on the plugins
 * settings page in the admin.
 *
 * @return string the username of the feed or empty string if not set.
 */
function hdji_get_username() {
	return apply_filters( 'hdji_get_username', get_option( 'hdji_username' ) );
}

Outputting things in a plugin or theme

By this I mean when you are perhaps outputting HMTL or content in a template file. It is easy to just do the logic in the template file and then output the necessary HTML to render the view. However instead of doing this, what is often best is that you simply add an action to your template file.

Let’s say you have a plugin that is outputting a job inside a standard WordPress loop from a WP_Query call. It could be tempting to output the post title, content etc. inside that loop. However, instead I would suggest simply adding a hook inside there like this:

do_action( 'wpmark_job_output', $post );

You can see we have passed the current post object to the hook here so a developer can access that information in any callback. You can then add your own callback function to do what you would have normally done straight in that template file. The benefit of doing it this way is that developers can remove your code and add their own.

Is there a hook for that?

We now know that we can use all the hooks in WordPress to our advantage, but how do we know whether there is a hook in existence that you can use for a specific thing? There are a few ways to find hooks that are available in WordPress.

Read the code

I think the best way of finding whether there is a hook available for the job you require is to read the source code. WordPress is open source and therefore we can all see the source code available.

I take the approach of finding where the code is running and then doing a search for apply_filters and/or do_action in that file. This was how I found the widget args filter in the video example above.

Online resources

There are a few online resources to assist with this. The first, and most obvious I guess in the WordPress codex, or what is now being the developer docs. There is also the actions API reference page and the filters API reference page which outline many but not all of the hooks available.

In addition to these, there is also The WordPress Hook and API Index which is a third party website which aims to document all the hooks in WordPress.

Summary

If there is one thing that you should learn deeply as a WordPress PHP developer I think that it is the WordPress hooks system (maybe after Javascript!) as it will really open doors for you in terms of developing more custom solutions.

I hope this article has helped you understand more about how WordPress hooks work, how you can use them to alter the behaviour of the software without actually changing any of the core code. In addition, I hope that you have or can gain the confidence to use hooks in your own themes and plugins to help out other developers who may be using them.

If you have any questions or comments on WordPress hooks then I would love to hear from you. Feel free to get in touch with via Twitter. I am @wpmark.