A Guide to WordPress Hook Assignments

WordPress hooks are functions that allow you to manipulate the functions of other parts of the site. They’re straightforward to understand and use; but, they can be a little confusing in different contexts if you want to use them outside of a simple global function call. This article is meant to help by outlining all the ways you can assign functions to hooks as well as the advantages and disadvantages of each method.

For the sake of simplicity, I’ve omitted the extra parameters for add_action() like the priority and number of arguments. You should include these in your code always. The ideas here also applies to the add_filter() hooks.

Some extra reading:

Globally Scoped

Anonymous Functions

add_action( "wp_footer", function() {
  echo 
"<script async src='.../gtag/js?id=UA-10101010'></script>
  <script>
     window.dataLayer = window.dataLayer || [];
     function gtag(){dataLayer.push(arguments);}
     gtag('js', new Date());
     gtag('config', 'UA-10101010');
  </script>";
} );

Anonymous functions, aka closures are in my opinion the quickest and easiest way to assign code to a hook (… and before you @ me, the PHP manual considers them both the same thing; however, I do see your point about the validity of the PHP manual.)

If you’re new to anonymous functions / closures, note that in place of the value (which typically in this case would be an array) is a function without a name (hence, “anonymous”.)

The advantages for hooking into WordPress this way is that it’s faster to set up and that it’s also easier to read. Instead of having to search for the corresponding function, you can see the code right there.

The disadvantages for this is that it’s much more memory intensive. If your assigning code is more than a few lines, you’ll want to avoid doing this too much.

$ga_code = "UA-10101010";
$ga_js = 
"<script async src='.../gtag/js?id=%s'></script>
  <script>
     window.dataLayer = window.dataLayer || [];
     function gtag(){dataLayer.push(arguments);}
     gtag('js', new Date());
     gtag('config', '%s');
  </script>";
add_action( "wp_footer", function() use ( $ga_code, $ga_js ) {
  echo sprintf( $ga_js, $ga_code, $ga_code );
} );

This is another example of an anonymous function with variable inheritance. Normally you won’t need to do this but remember hooks are called in a completely separate section and context of the runtime from where you’re assigning it. Any variables that you have in the same scope will have to be inherited or they’ll be initialised as null.

Advantages

  • Less typing
  • Simpler
  • Easier to read.

Disadvantages

  • Can be more memory intensive

Function Calls

function aa_wp_footer() {
  echo 
"<script async src='.../gtag/js?id=UA-10101010'></script>
 <script>
   window.dataLayer = window.dataLayer || [];
   function gtag(){dataLayer.push(arguments);}
   gtag('js', new Date());
   gtag('config', 'UA-10101010');
 </script>";
} );
add_action( "wp_footer", "aa_wp_footer" );

This type of assignment is what you’ll find most often when reading about WordPress hooks. You write a function that contains your logic, then assign that function to the hook.

The advantages for this method is that it’s simple and straightforward to do. It’s also faster to deploy.

There are significant disadvantages to be aware of. Above a certain number of hook assignments, your code files can begin to look very messy, hard to read, and difficult to maintain. As your theme or plugin grows, you’re also adding more functions to the global scope. This isn’t so bad from a resource perspective but the most worrying issue is that you stand a greater chance of having a function name collision where another plugin or theme has named their function the same as yours. Unless you have a good reason, you really want to stay away from these types of calls.

Advantages

  • Simple
  • Quick

Disadvantages

  • Scales poorly
  • Difficult to maintain
  • Difficult to read
  • Higher chance of function name collision

Object / Class Scoped

The following sections are ideally the way you should be assigning code to your hooks. Object-oriented code is a bit more complex to setup, but they make maintenance easier over time.

Let’s assume an object with static and dynamic functions for our code:

namespace CompanyName\Plugin;

class Template {

  //
  // Static logic
  //

  static function aa_wp_footer_static( $gacode=null ) {
    $gacode = is_null( $gacode ) ? "UA-10101010" : $gacode;
    echo
    "<script async src='.../gtag/js?id=$gacode'></script>
     <script>
       window.dataLayer = window.dataLayer || [];
       function gtag(){dataLayer.push(arguments);}
       gtag('js', new Date());        
       gtag('config', '$gacode');
     </script>";
  }

  //
  // Non-static logic
  //

  protected $ga_code = null;

  public function __construct() {
    $this->ga_code = "UA-10101010";
  }

  public function aa_wp_footer( $ga_code=null ) {

    if ( is_null( $ga_code ) ) {
      $ga_code = $this->ga_code;
    }

    echo
    "<script async src='.../gtag/js?id=$ga_code'></script>
     <script>
       window.dataLayer = window.dataLayer || [];
       function gtag(){dataLayer.push(arguments);}
       gtag('js', new Date());        
       gtag('config', '$ga_code');
     </script>";
  }

}

Static Objects / Classes

Here are examples of static function hook assignments.

add_action( 
  "wp_footer",
  [ "\\CompanyName\\Plugin\\Template", "aa_wp_footer" ]
);

You can assign a hook an array with two elements; the first element is a string of the function class as well as its namespace and, the second is a string of the function name.

add_action( 
  "wp_footer",
  "\\CompanyName\\Plugin\\Template::aa_wp_footer()"
);

This second example is also calling a static class method, but instead of an array you can assign a string that is a static function call.

Dynamic Objects / Classes

Here are examples of nonstatic, dynamic function hook assignments.

$Template = new \CompanyName\Plugin\Template();
add_action( 
  "wp_footer",
  [ $Template, "aa_wp_footer" ]
);

We’re passing an array again to the hook, but instead of a string with the namespace and class in the first element, we’re passing an actual object.

You can also use $this as the target object instead of a separate object. So if your hook code is in the same context as your code object, you can do the following:

class TemplateHooks extends \CompanyName\Plugin\Template {

  public function queue_hooks() {
    add_action(
      "wp_footer",
      [ $this, "aa_wp_footer" ]
    );
  }

}
$TemplateHooks = new \CompanyName\Plugin\TemplateHooks();
$TemplateHooks->queue_hooks();

In this example I extended the Template class; but, you could also have both the add_action() and aa_wp_footer() functions in the same class file. They’re both effectively the same thing.

The advantage to using hook calls in an object-oriented context like this is that its forces you to organise and compartmentalise your code. It’s easier to maintain in the long run as function logic is grouped. Also it greatly decreases the possibility of a function name collision from happening.

The disadvantage is that it’s more work at the start. Instead of quick global functions, you have to set up a namespace, modify the autoloader, add functions, and etc.

Advantages

  • More organised code
  • Easier to maintain
  • No function name collissions

Disadvantages

  • More work initially

On Parameters

The one issue with all of the assignment calls reviewed prior is that they don’t permit for function arguments with your hooks.

The only way around this is to use anonymous functions and closures. The following are two examples, one static and one nonstatic.

$different_ga_code = "UA-20202020";
add_action( 
  "wp_footer",
  function() use ( $different_ga_code ) {
    \CompanyName\Plugin\Template::aa_wp_footer_static(
      $different_ga_code
    );
  }
);
$different_ga_code = "UA-20202020";
add_action( 
  "wp_footer",
  function() use ( $different_ga_code ) {
    $Template = new \CompanyName\Plugin\Template();
    $Template->aa_wp_footer( $different_ga_code );
  }
);

As mentioned, you want to not do this often as closures do use up more memory than an object/function pointer. (But sometimes, you gotta do what you gotta do.)

Conclusion

Thanks for reading!

I hope this article helped expand your use of WordPress hooks and that it improved your skills as a software developer.

Did I miss anything? Do you have any questions? Did I make a mistake with my sample code? Do you have an angry/frustrated rant on PHP or WordPress?

Let me know!