WordPress Hooks: Beginner’s Guide to Adding Your Own Hooks

Plugins are the way to extend WordPress. The reason it is so easy to make WordPress do your bidding has to do with hooks that are littered across the source code.

Want to do something as soon as a post is published? Want to modify the excerpt length? Want to create your own page view stats? Finding the right hook is all you need to do.

But what about doing the same with plugins? Hooks are not just for use in WordPress, they are essentially a design pattern you can implement in your own work.

In this article, I’ll show you how you can create your own hooks to make it easy for you or others to build upon your work.

Why We Need Hooks

To show you how awesome hooks can be, let’s look at an example from the real world: Advanced Custom Fields. This plugin allows you to add flexible custom data fields to posts; we’re talking about Google Maps, number fields, multiple choice and more.

All this is done via a nice user interface, all you need to do is use functions like get_field() or the_field() on the front end. So far so good.

Let’s assume that you’re building a tool which allows people to sell things, offering a field for the price and one for the currency. Internally however, you always want to store prices in Dollars, regardless of what currency the user chooses to display in.

If ACF (Advanced Custom Fields) doesn’t have any hooks, this would be difficult to do, you may need to use a mechanism outside of ACF to save this field. Luckily, we’re covered by the acf/save_post action. You can use this action to modify $_POST data before it is saved – a perfect time to convert the price to Dollars.

This is a very practical example of hooks but there is another reason: future proofing and extendability. Hooks are kind of like an API, they give you direction; a framework so to speak. If you’re saving important data you should automatically create a hook so others can manipulate the data if needed. If you’re displaying something critical to your application, images for example, use a filter which allows uses to define the number of images shown.

How Hooks Work Internally

To create our own hooks, we’ll do exactly what WordPress does internally – use the do_action() and apply_filters() functions to execute all hooked functions. Let’s turn to the WordPress source code to see how these work.

WordPress uses the wp_trim_excerpt() function internally to create an excerpt. In WordPress 4.1 this function is defined in wp-includes/formatting.php, starting on line 2542. If you turn your attention to line 2560 you should see the following:

$excerpt_length = apply_filters( 'excerpt_length', 55 );

What this means is the following: Run each and every function hooked into the excerpt_length hooks and return the final value. Let’s say you use 3 plugins which modify the excerpt length, what happens then? Let’s gather these function in one place and take a look:

add_filter( 'excerpt_length', 'plugin_a_excerpt_modify', 20 );
plugin_a_excerpt_modify( $length ) {
    return $length - 10;
}

add_filter( 'excerpt_length', 'plugin_b_excerpt_modify', 32  );
plugin_b_excerpt_modify( $length ) {
    return 104;
}

add_filter( 'excerpt_length', 'plugin_c_excerpt_modify', 16 );
plugin_c_excerpt_modify( $length ) {
    return 20;
}

In the end, the excerpt length will be 104 words long. Filters are executed in their order of priority. Therefore the function of plugin C is executed first. If no other function were hooked in, the length would now be 20 words. Next, plugin A kicks in and the length becomes 10. Finally, plugin B weighs in, making the length 104.

do_action() uses the exact same mechanism, executing all functions tied to the hook defined as the first parameter.

Note that this pattern is completely enclosed, it does not rely on any special definitions. You can use do_action( 'lol_hook' ) anywhere you like, worst case scenario, no functions are tied to lol_hook. In other words, we already have the infrastructure to create our own hooks!

Creating Our Own Hooks

As I mentioned above, all we need to do is make sure to use do_action() and/or add_filter and make sure to document it. This way coders and users will actually know it’s there.

Let’s presume we’re creating a gallery of the latest uploaded images pulled straight from the database, something like this:

$args = array(
    'post_type' => 'attachment',
    'post_status' => 'any',
    'orderby' => 'date',
    'order' => 'DESC',
    'posts_per_page' => 10,
    'fields' => 'ids'
);
$images = new WP_Query( $args );
$gallery = '['.'gallery ids="' . implode( ',', $images ) . '"'.']';
echo do_shortcode( $gallery );

In this example we create a query making sure we only retrieve images, ordered by date and limited to 10 results. Using the fields parameter I made sure that an array of ids is returned. I then built a regular WordPress gallery shortcode out of this information.

There are two approaches you could take to add hooks here. You could add it simply before the definition of the $args array, like this:

$posts_per_page = apply_filters( 'my_gallery/posts_per_page', 10 );

In this case, you would need to use the $posts_per_page variable in the array. Perhaps a better solution would be to let the whole array be modified. This would allow other plugins (or you, in the future) to add category, tag and other restrictions on the galleries – here’s the full code:

$args = array(
    'post_type' => 'attachment',
    'post_status' => 'any',
    'orderby' => 'date',
    'order' => 'DESC',
    'posts_per_page' => 10,
    'fields' => 'ids'
);

$args = apply_filters( 'my_gallery/query_args', $args );

$images = new WP_Query( $args );
$gallery = '['.'gallery ids="' . implode( ',', $images ) . '"'.']';
echo do_shortcode( $gallery );

That’s all there is to it. Other plugins can now use the my_gallery/query_args hook to modify the functionality of the galleries created.

A note on the naming: the forward slash is not necessary at all. You could use ‘my_plugin_query_args’ or any other format you wish. I prefer the forward slash because it clearly states the plugin creating it and the functionality it provides.

Conclusion

Hooks are extremely powerful and can be used not only to extend WordPress, but to extend your own plugins or even themes. You should take care not to go overboard and add hooks into every nook and cranny of your application.

Instead of adding a hook to the order, post status, post_type and posts_per_page separately, I added a filter that can modify the whole array at once. Other bits of data may not make sense to add hooks to, this is ultimately up to you.

Good luck creating more modular and extendable applications, let us know if you’ve used your own hooks somewhere cool before!