Understanding WordPress Action and Filter Hooks
The Most Essential Thing to Know
When I landed my first eCommerce and blog development gig over 10 years ago, I had never used WordPress. In fact prior to this job, I had only used a CMS that I had made from scratch with no framework of which to speak. Put succinctly, I was an idiot. Okay, so maybe that is a bit harsh. I wasn’t an idiot as much as I was just inexperienced. I was teaching myself many aspects of how to be a web developer, and the way I learn best is by doing. That being said, I certainly would have saved myself a lot of headaches if I had taken just a little time to learn about one of the most essential concepts in WordPress development. This is the concept of action and filter hooks.
So what exactly is a hook? The short answer is that a hook is a way for you as a developer to extend WordPress without having to modify the base code of WordPress. It protects you when WordPress updates, which is does quite frequently. Imagine this scenario with me. You download and extract WordPress onto your local machine. You fire it up, and then decide you want to change some base operation or that you want to modify the theme. How do you do this? Well, one way would be to go into the wp-includes
or wp-admin
folder (something you should never ever do) and to find what you need to change and do it. But this creates a significant problem. The next time WordPress updates, the code you modified will be deleted. Therefore we need a way to safely change elements without the modifications getting erased. And this is where the beauty of WordPress action and filter hooks come in.
These hooks are placeholder in the code base itself. It is a place where you can safely “inject” functions by adding files that WordPress will never update. This is all done inside the wp-content
folder.
Where to Begin?
The first thing you need to know about action and filter hooks is that there are tons and tons of them. And truth be told there are tons and tons more that can be added because WordPress includes two functions. One is called do_action()
, and the other is called apply_filters()
. I will go into more detail about these two functions in a little bit, but for now just know that these two functions allow you to add your own hooks into your custom code. WordPress is so extendable that even its extensions can have extensions. How cool is that?
This comes in handy when you are working with a client that wants an eCommerce site and wants to use WooCommerce but also wants to change a ton of aspects about the way WooCommerce operates (an incredibly common occurrence). The developers of WooCommerce have actually added hooks all over the place so you can tweak to your heart’s desire. This flexibility is in-part what makes developing in WordPress so fun and so attractive. You are given extremely powerful tools out-of-the-box that are constantly kept up-to-date, and you have the ability to literally do whatever you want in the platform.
Now, there are far too many hooks for me to cover them all in this article. So instead of trying to give you a huge overview, I will simply show you the difference between an action and a filter and how you may use both in a project.
Action Hooks
As I mentioned above, there are two types of hooks. Filters (which modify a variable and expect a returned value) and actions (which simply inject functionality at specific points in your code).
An action hook is simply a placeholder. It is a way of allowing you to slip in code between other functions inside a file. Let’s say that I am looking at a normal HTML markup inside of a PHP file. Perhaps it would look like this.
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main>...</main>
<footer>...</footer>
</body>
</html>
In order to edit this code, I would need to go into that file, modify it, and then save it out. I would then have to worry about what happens when the theme or plugin updates. All my work will get lost. So if I go in and add a ul
element in the main, it will be gone when the theme developer updates the theme.
But a hook allows me to edit the space between the main without the developer’s update impacting my changes (unless he were to remove the hook). So you would instead see this.
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main><?php do_action('theme_main') ?></main>
<footer>...</footer>
</body>
</html>
So what is happening here? Well it is really quite simple. In this file we are seeing that the developer has given you the option to inject code into his theme. He has added the do_action()
function, and the first parameter of that function is a tag that you will use to hook into his code. How do you do that? I’m glad you asked…
You do this by utilizing the add_action()
function. Now it is important that you realize where you should use this function. It should only be used in a plugin, a custom theme, or a child theme. To understand more about these things please visit the WordPress developers documentation site here. If you are not familiar with theme or plugin development, then I would simply encourage you to stop reading this lab and to start from the beginning. Assuming you understand the basics of plugin and theme development, then you will understand what I mean when I say this code should go in your functions.php
file or some other file within your plugin or theme that is loaded by the functions.php
file.
Let’s say I want to always have my <main>
section to contain the code <h1>Clayton is a great teacher</h1>
(nothing like a little shameless self-promotion). The way I would achieve this can found in the code snippet below:
<?php
//Inside of functions.php file
//Injection or binding
add_action('theme_main', 'ck_add_header');
//Function to be injected or bound to the hook
function ck_add_header(){
echo '<h1>Clayton is a great teacher</h1>';
}
?>
The code above would result in an HTML output of:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main><h1>Clayton is a great teacher</h1></main>
<footer>...</footer>
</body>
</html>
How does this happen? Simple really. The developer who added the action hook essentially added a listener into his code. That code loads after your functions.php
file has loaded. So, you define your function (in this case ck_add_header()
) and tell that function to do something. In our current example we are simply echoing out the <h1>
code. But how does the theme know to execute your function there? Because of the tag in the do_action()
function.
When you use the add_action()
function, you are beginning by passing a first parameter that corresponds with a tag that is found somewhere in the code that fires after your code has been loaded. You define your function, but it doesn’t fire yet. It will wait to do so until it is told to do so by the do_action()
command. Without going into too much detail this is achieved by using global variables that is modified each time you use the add_action()
function. The second parameter of the add_action()
function is the name of the function your wish to execute one you reach the hook in the code. So in our example above, we told WordPress that when it reaches the place in the code where it finds do_action('theme_main')
, we want to execute the function ck_add_header()
. Which is what it is happy to do for us. After all that is for what it is made.
Order and Priority
One of the great things about this is that we are allowed to do it as many times per hook as we want. So I could tell WordPress to execute the function ck_add_header()
multiple times if I liked by simply doing this.
<?php
add_action('theme_main', 'ck_add_header');
add_action('theme_main', 'ck_add_header');
add_action('theme_main', 'ck_add_header');
add_action('theme_main', 'ck_add_header');
?>
In this case my output would be:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main>
<h1>Clayton is a great teacher</h1>
<h1>Clayton is a great teacher</h1>
<h1>Clayton is a great teacher</h1>
<h1>Clayton is a great teacher</h1>
</main>
<footer>...</footer>
</body>
</html>
Which isn’t very helpful, but still I hope it illustrates the point. What could be helpful is to know that you could tell WordPress to fire a completely different function using the same hook. Say I wanted my <h1>
to be followed by a <p>
that contained some sort of text. That might look like this.
<?php
add_action('theme_main', 'ck_add_header');
add_action('theme_main', 'ck_add_paragraph');
//<h1> function
function ck_add_header(){
echo '<h1>Clayton is a great teacher</h1>';
}
//<p> function
function ck_add_paragraph(){
echo '<p>This is the paragraph text.</p>';
}
?>
In this example we would get the HTML output of:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main>
<h1>Clayton is a great teacher</h1>
<p>This is the paragraph text.</p>
</main>
<footer>...</footer>
</body>
</html>
So we can see what happened above is that first our action attached to ck_add_header()
fires and then our ck_add_paragraph()
action fires. This happens because we placed the add_action()
functions associated with each in that order. However, one problem that you may encounter is that it is possible that some other file in a different plugin or some other place may be using that same action as you are, and it may be injecting code in a different order than you wish for it to. For example let’s say you installed a third-party app from the WordPress Repo that contained this line.
<?php
add_action('theme_main', 'plugin_add_header');
//<h3> function
function plugin_add_header(){
echo '<h3>I am smarter than Clayton!</h3>';
}
?>
Assuming that this plugin is loaded before your plugin or theme, then your HTML code would look like this:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main>
<h3>I am smarter than Clayton!</h3>
<h1>Clayton is a great teacher</h1>
<p>This is the paragraph text.</p>
</main>
<footer>...</footer>
</body>
</html>
This could be frustrating. How are we suppose to let WordPress know that we want our code to fire first? Luckily the wonderful WordPress community has not let us down here. There is a third parameter that the add_action()
function accepts called priority. This parameter lets WordPress know which action gets the higher priority. Lower numbers will go before higher numbers. If a third parameter doesn’t exist then the number 10 is assumed. In our examples above all of the add_action()
functions had a priority of 10. So if we wanted our ck_add_header()
and ck_add_paragraph()
functions to fire before the plugin_add_header()
function, then we would need to pass a number lower than 10 as the third parameter in their respective add_action()
commands. That would look something like this.
<?php
add_action('theme_main', 'ck_add_header', 5);
add_action('theme_main', 'ck_add_paragraph', 6);
//<h1> function
function ck_add_header(){
echo '<h1>Clayton is a great teacher</h1>';
}
//<p> function
function ck_add_paragraph(){
echo '<p>This is the paragraph text.</p>';
}
?>
By placing 5 and 6 in the third parameter slots of our add_action()
function we have successfully told WordPress to execute these functions before plugin_add_header()
. It will then produce the following HTML output:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main>
<h1>Clayton is a great teacher</h1>
<p>This is the paragraph text.</p>
<h3>I am smarter than Clayton!</h3>
</main>
<footer>...</footer>
</body>
</html>
Action Variables
There is one more parameter that our add_action()
function can accept. That parameter is a number. This number tells us how many variables to expect from the do_action()
listener. Now I know that sounds confusing but just hang with me. Let’s take a look at the do_action()
function again.
Above I showed the example of do_action()
simply with one parameter and that parameter was a tag that was listening for hooks. The first parameter of do_action()
will always be this tag. However there can also be any number of extra parameters passed in do_action()
. These variables can be used by the functions to which our add_action()
function binds.
Consider this example:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main><?php do_action('theme_main', 'Some string', [1,2,3]) ?></main>
<footer>...</footer>
</body>
</html>
In the example above you will see that the second parameter of do_action()
is a string with a value of “Some string” and that the third variable with an array containing three instances with a value of 1, 2, 3. How do we access these variables? Assuming that we configure our add_action()
function correctly then these variables will become available to us in our functions that are bound to that same add_action()
function.
Consider this code below:
<?php
add_action('theme_main', 'ck_add_header', 5, 2);
add_action('theme_main', 'ck_add_paragraph', 6, 1);
//<h1> function
function ck_add_header($some_string, $the_array){
if(count($the_array) < 3){
echo '<h1>Clayton is a great teacher</h1>';
} else {
echo '<h1>' . $some_string . '</h1>';
}
}
//<p> function
function ck_add_paragraph($some_string){
echo '<p>This is the paragraph text. ' . $some_string . '.</p>';
}
?>
What we are doing in the first add_action()
function above is accepting two of the extra variables passed in the do_action()
example above this one. We can then use those as parameters in the ck_add_header()
function. We use them to perform a little logic and echo
out accordingly.
In the second add_action()
function we are only accepting one of the extra variables. In this case the ck_add_paragraph()
function uses the $some_string
variable name to accept the string with the value of “Some string” that was passed by our do_action()
statement in the example prior to this example.
This above example would result in the following output:
<html>
<head>
<title>Test Page</title>
</head>
<body>
<nav>...</nav>
<main>
<h1>Some string</h1>
<p>This is the paragraph text. Some string.</p>
<h3>I am smarter than Clayton!</h3>
</main>
<footer>...</footer>
</body>
</html>
You are probably starting to see how these action hooks are helpful. Of course the functions are simply PHP functions. So you can ultimately use any PHP logic or features (like API calls or Database I/O) that you want in them. At the end of the day you don’t even need to echo
anything out. You can perform background functions or do whatever you want.
Default Actions
In the above examples we worked with a custom do_action()
command that a developer would have placed in their plugin or theme. But most of the time you will not be working with custom action hooks. Most of the time you will be wanting to work with the default WordPress action hooks. These hooks are strategically placed in spots that will allow you to leverage the power of WordPress and fully modify or extend its functionality.
In order to get a grasp on these hooks you should refer to the Plugin API/Action Reference document on codex.wordpress.org. It will show you the most used actions in WordPress (though there are many that I use that are not on this list). It will also tell where these are located and when it is and is not appropriate to use them.
Now that we understand actions we can easily tackle filters.
Filters
Filters function in the same basic way as actions with one major difference. Filters expect a value to be returned from the bound function. Because of this filters are used to modify variables that used by the program (theme, plugin or core file). Each filter contains a tag (just like the actions) but then also contain a second parameter which contains the current value of that filter. Unlike actions the last filter will overwrite the previous filters. The equivalent of the do_action()
function for filters is the apply_filters()
function.
Consider the code below:
<?php
//Set the variable of the string
$string = apply_filters('set_string', 'Some string');
?>
<div><?php echo $string; ?></div>
In this example we have a variable of $string
that we echo
out in a <div>
. The code above would result in the following output:
<div>Some string</div>
Why is this? What is going on here? Well, essentially what is happening is that the apply_filters()
function is listening for any bound filters with a tag of ‘set_string.’ In this case it doesn’t find any so it uses the second parameter, which is a string with the value of ‘Some string,’ and it then returns that value to the variable $string
which later gets echoed. So how would we modify this? Exactly in the same way we do our actions. We hook into it with a function that proceeds it in the code.
So, in our functions.php
file in our plugin or theme we could put something like this:
<?php
//Modify the variable of string
add_filter('set_string', 'ck_set_string');
function ck_set_string(){
return 'Changed string';
}
?>
In this example we are adding a filter to the tag of ‘set_string.’ This filter will be applied when it encounters the apply_filters()
function that has the value of ‘set_string’ as the first parameter. The filter will execute the function with the same name as the value of the string in the second parameter of the add_filter()
function (in this case ‘ck_set_string’). So what do you suppose our output would be assuming that the code from the example above was executed prior to the code in the example above that? It should look like this:
<div>Changed string</div>
We added a filter, and when the apply_filters()
function executed, it went through our list of added filters (in this case only 1) and used the final return statement as the one that got returned to the $string
variable from our original example. That then was echoed out.
Order and Priority
Now just like the action hooks, filters have an order in which they execute. Simply put the final execution will win out. Consider the following example:
<?php
//Modify the variable of string
add_filter('set_string', 'ck_set_string');
add_filter('set_string', 'ck_set_string_two');
function ck_set_string(){
return 'Changed string';
}
function ck_set_string_two(){
return 'Changed string again';
}
//Set the variable of the string
$string = apply_filters('set_string', 'Some string');
?>
<div><?php echo $string; ?></div>
This would result in the following output:
<div>Changed string again</div>
Do you see what occurred here? The first add_filter()
command set the string to ‘Changed string’ only to immediately be replaced by the second add_filter()
command which set the $string
variable to ‘Changed string again.’
Now just like with action hooks this can be adjusted in order to help you in the case that a plugin or theme changes a filter after you set it but before the variable is processed. Consider the example below:
<?php
//Modify the variable of string
add_filter('set_string', 'ck_set_string', 30);
add_filter('set_string', 'ck_set_string_two', 10);
function ck_set_string(){
return 'Changed string';
}
function ck_set_string_two(){
return 'Changed string again';
}
//Set the variable of the string
$string = apply_filters('set_string', 'Some string');
?>
<div><?php echo $string; ?></div>
See if you can guess what the output will be.
<div>Changed string</div>
That’s right (at least I hope you got it right) the output is the value of the first add_filter()
function we invoked because even though it comes first programmatically, it has a priority that is set higher than the second add_filter()
. This is incredible important concept to grasp and remember when developing in WordPress.
Current Value
Just because a filter is not the highest priority doesn’t mean it doesn’t get executed. It absolutely still sets whatever variable you are trying to change. This can be useful as it can allow us to concatenate or append data whether it be a string, array, or object or it can even allow us to do complicated math. Consider the following example:
<?php
//Modify the variable of string
add_filter('set_string', 'ck_set_int');
add_filter('set_string', 'ck_set_int_two');
function ck_set_int($current_value){
return $current_value + 3;
}
function ck_set_int_two($current_value){
return $current_value / 5;
}
//Set the variable of the int
$int = apply_filters('set_string', 2);
?>
<div><?php echo $int; ?></div>
What do you think our output would be?
<div>1</div>
By passing the current value we were able to essentially factor the equation of (2 + 3) / 5, which equals 1. This may not seem too useful right now, but I assure you that it will become more useful as you begin diving into the more complex aspects of WordPress especially query variables.
Extra Variables
Just as with actions, filters are able to pass and receive a dynamic number of parameters. This works in almost the exact same way as the action variables, so I would refer back to that part of the article for refreshing. However, there is one confusing part in that the first variable in your bound function will always be the current value of the filter. So if there are four total parameters in the apply_filters()
function, then you will need to tell add_filter()
to expect three variables in order to have access to all of them. Consider the following:
<?php
//Modify the variable of string
add_filter('set_string', 'ck_set_int', 10);
add_filter('set_string', 'ck_set_int_two', 11, 3);
function ck_set_int($current_value){
return $current_value + 3;
}
function ck_set_int_two($current_value, $show_text, $text){
if(!$show_text) return $current_value / 5;
return $text . ' ' . $current_value / 5;
}
//Set the variable of the int
$int = apply_filters('set_string', 2, true, 'The number is: ');
?>
<div><?php echo $int; ?></div>
This would result in the output of:
<div>The number is: 1</div>
We passed the boolean
of true
to ck_set_int_two()
as well as the string of ‘The number is: .’ We then used those to perform logic and return the desired value. Hopefully that all makes sense.
Don’t Forget Return
The biggest mistake I make when using add_filter()
is that I almost always echo
my value instead of returning it with the return
statement. This will (at least most of the time) produced undesired results. So be careful and try to always use return
.
Default Filters
As with the action hooks above, WordPress has implemented many default filters that help you modify and extend the core files. If you are interested in knowing what some of those are and how to properly use them, then you should refer to the the Plugin API/Filter Reference documentation found at codex.wordpress.org.
Now You Know
Well there you go. You should now be a pro at using WordPress hooks. Please feel free to comment below or provide examples of work you have done implementing your favorite WordPress action and filter hooks. I would love to hear from you and learn from your experiences as well as answer any questions you may have.