Hooks, Actions, and Filters in WordPress

0
487
Wordpress hooks

Confused by what is an action and filter in WordPress? Let’s clear that fuzz up through leveraging the idea of callback functions, the Observer pattern, and hooks.

Callback Functions and the Observer Pattern

In Callback Functions in PHP I talked a little about the different ways you can call functions in the language. Please go review that article before proceeding with this one as it will help get the basic concepts down. When you have mastered this, come back here and continue.

In addition, a program design pattern can used when it comes to callbacks. I wrote about this in the Observer pattern whereby there are Subcribers and a Publisher that sends out notifications. The idea is related and I encourage you to read that as well.

When you put these two together, you will understand hooks, actions, and filters in WordPress a whole lot better.

What is a Hook?

A hook is an entry point in a piece of code. The developer created an entry point in his code so that you can change the behavior or data within it. This allows you to extend the functionality by modifying behavior and/or the data it is working with.

Lets take a high level example. Suppose we want to create a function that renders output in either console or HTML. We’ll start out with this simple one that displays a message with an end of line:

  1. // A console text echo alias
  2. function ShowMessage($message) {
  3.   echo $message . "\n";
  4. }

Now suppose we wanted the equivalent in HTML. We can cook something up quickly like this:

  1. // A HTML paragraph wrapper
  2. function ShowMessage($message) {
  3.   echo "<p>$message</p>";
  4. }

You can see how this all of a sudden requires different PHP code to get the job done. We can do a whole lot better by making this more flexible to accomodate both scenarios with one function call.

One way to do this is through use of a PHP callback function. It may not seem so quite apparent at the start, but stay with me on this. Study these set of functions that will allow you to output HTML wrapped in a <div> or <p> and as a console text line.

  1. // Used to generate attributes in HTML tags
  2. function HtmlAttributes($attribs) {
  3.   $out = "";
  4.   foreach($attribs as $attrib => $value) {
  5.     if ($attrib == "innerHtml") {
  6.       continue;
  7.     }
  8.     $out .= " {$attrib}='{$value}'";
  9.   }
  10.   return($out);
  11. }
  12.  
  13. // A wrapper for the HTML <p> tag
  14. function HtmlP($attribs=array()) {
  15.   $out = "<p" . HtmlAttributes($attribs) . ">";
  16.   $out .= $attribs['innerHtml'];
  17.   $out .= "</p>";
  18.   return($out);
  19. }
  20.  
  21. // A wrapper for the HTML <div> tag
  22. function HtmlDiv($attribs=array()) {
  23.   $out = "<div" . HtmlAddAttributes($attribs) . ">";
  24.   $out .= $attribs['innerHtml'];
  25.   $out .= "</div>";
  26.   return($out);
  27. }
  28.  
  29. // Our multi-purpose output function
  30. function Out($str, $hook="", $attribs=array()) {
  31.   $out = $str;
  32.   $attribs['innerHtml'] = $str;
  33.   if (strlen($hook) > 0 && function_exists($hook)) {
  34.     $out = call_user_func($hook, $attribs);
  35.   }
  36.   echo $out;
  37. }

With just one output function, Out, you can generate output for the console and in HTML. How beautiful is that? Very!

Do you see how you can extend the Out function by providing custom functions for any document type and how I extended it by using a call back? Let’s get more concrete through how you can use it:

  1. Out("Hello, World!");
  2.  
  3. Out("Hello, World!", "HtmlP");
  4. Out("Hello, World!", "HtmlDiv");
  5.  
  6. Out("Hello, World!", "HtmlP", array("class" => "HtmlP"));
  7. Out("Hello, World!", "HtmlP", array("id" => "HelloWorld"));
  8. Out("Hello, World!", "HtmlP", array("class" => "HtmlP", "id" => "HelloWorld"));
  9.  
  10. Out("Hello, World!", "HtmlDiv", array("class" => "HtmlDiv"));
  11. Out("Hello, World!", "HtmlDiv", array("id" => "HelloWorld"));
  12. Out("Hello, World!", "HtmlDiv", array("class" => "HtmlDiv", "id" => "HelloWorld"));

You should now be able to extend Out for other HTML tags.

The key point to make here is that Out makes available a point in it’s block of statements with a function to be called back via call_user_func. By doing this, it lets you override behavior and data by passing this function in as an argument. If you know Object Oriented Programming (OOP), this is similar to overloading a function – but at the point of the developers choice of entry.

Now that you know a little about PHP callbacks, lets talk about WordPress’ own kind of hooks – actions and filters.

What is a Filter?

A filter is a hookable event. In particular, an event where the publisher allows subscribers to tap into data so that it can be modified. A Subscriber hooks into a filter by calling add_filter() on the filter name and telling the Publisher to make a call to the supplied callback:

  1. // Subscribers: Register to hook into a filter
  2. add_filter(filterName, callback, priority, numArgs);

WordPress allows more than one Subscriber to hook into a filter. It does so in a chain priority sequence. Themes and other plugins can hook into a filter using the same filter name. They can position themself higher up in the chain by setting a lower priority value than the rest so that they can be executed earlier.

To notify each Subscriber, the Publisher calls apply_filters():

  1. // Publisher: Call each subscriber hooked into the filter
  2. $ret = apply_filters($filterName, $data, $arg1, $arg2);

Now you may be asking what can you filter on if you were to write code playing the role of a Subscriber. Fortunately, there is a WordPress filter list available on the Codex.

A Filter Example: Modify the Tag Cloud Title

Lets take a filter example to make this a little more concrete. Suppose you want to modify the tag cloud’s setting of its title. You can do this in two ways – by developing a plugin or adding it to your current theme’s functions.php.

Lets choose to do this in our theme’s functions.php. Here is our first attempt:

  1. function ChangeTagCloudTitle($title, $arg1, $arg2) {
  2.   return("Explore the Cloud!");
  3. }
  4.  
  5. // Listen in on the Tag Cloud title
  6. add_filter("widget_title", "ChangeTagCloudTitle");

While close, this code has a problem. The widget_title filter is used across all widgets in WordPress. I know this because I did a little deep sea diving into WordPress core. Inside class-wp-widget-text.php is the following apply_filters call:

  1. $text = apply_filters('widget_text', $widget_text, $instance, $this);

We can see we got the filter name correct (widget_text) as well as the data to be filtered ($widget_text). The $instance and $this are arguments passed to the callback filter. In total, there are 3 arguments (excluding the filter name) so we need to remember this and pass it. To assist in seeing the contents of the variables, you could do a var_dump to verify. After var_dump, the $this object contains the identity of the tag cloud.

The final filter code looks like this:

  1. // Subscriber: Filter hook that modifies the tag cloud title
  2. function ChangeTagCloudTitle($title, $instance, $this) {
  3.   if ($this == "tag_cloud")
  4.     return("Explore The Cloud!");
  5.   return($title);
  6. }
  7.  
  8. // Subscriber: Register to listen in on the Tag Cloud title events
  9. add_filter("widget_title", "ChangeTagCloudTitle", 10, 3);

Go ahead and try it. Place a tag cloud widget in your theme’s sidebar and place the code above in the theme’s function.php.

What is an Action?

An action is a hookable processing event. Actions allow you to modify the behavior at a certain point in the execution of a publishers code.

Subscribers register with the publisher through add_action():

  1. // Subscribers: The callback function for ActionName
  2. function MyCallback($arg1, $arg2, ...) {
  3.   // do whatever
  4. }
  5.  
  6. // Subscribers: Register the action with the Publisher
  7. add_action("ActionName", "MyCallback", $arg1, ...);

The publisher fires an action event to notify all subscribers hooked in through do_action():

  1. // Publisher: Fire an action to notify Subscribers
  2. do_action($actionName, $arg1, $arg2, ...);

Actions are quite similar to filters. In fact, adding an action calls add_filter:

  1. function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
  2.   return add_filter($tag, $function_to_add, $priority, $accepted_args);
  3. }

The difference is, filters change data and return it. Actions let us change the behavior at a certain point in time in a publisher’s flow of execution and do not return anything. If you want to know the bulk of WordPress actions available to tap into, there is a good list you can refer to here.

Summary

Getting the idea down is often the hardest part in WordPress’ definitions of what is an action and a filter. Hopefully, by drawing upon the concept of Publishers and Subscribers and understanding the underpinnings of what a callback function in PHP is, this article has helped shed a little more light on the subject.

Of Interest