Menu

Underscorefunk Design

Design, Illustration, Interaction, and Artistic Direction

Use filters instead of functions

While writing code we should always be striving for clarity and consistency. As time has gone on, I’ve learned to value this over being clever. The resultant code is perhaps not as elegant, but it’s a lot more maintainable.

A lot of WordPress code I’ve seen involves something like:

Why do I think this is problematic?

  • You have to declare the default in advance.
  • There is inherent concern about if the function exists (or if the plugin is activated).
  • Creating overrides is difficult and not very clear. Each function needs to be wrapped in a ‘function_exists’ check and overrides are based on the order of declaration.
  • Changing the plugin (or initial function’s) arguments can break other plugins or implementations of the function.
  • It’s difficult to see where overrides are taking place because overrides are based on the order of declaration.

The solution

I believe using filters to get values instead of most function calls is a betters solution. The resulting code would look like this:

Some of the benefits are:

  • The default value is implied in the filter’s application.
  • The filter doesn’t care if a function is hooked into it.
  • Overrides can be added with priority instead of at run time.
  • Adding additional filter arguments doesn’t break existing implementations.
  • All functions hooked to the filter can be inspected.
  • It’s easy to see what’s happening in plugins that list all of their hooked actions in a block (an engine room of sorts).

Implementation and Extras

Here are a few things I do in implementation to make this all work nicely.

  1. When I hook the base function for a filter, I set it to priority 9, so that it runs before all of the default subsequently hooked filters would run.
  2. I make the assumption that a certain value is required to allow the hooked function to run. If it doesn’t meet this criteria, the function will assume that a hooked function ran before the default one at priority 9. This avoids double processing.
  3. I always prefix filters with my plugin’s name followed by two underscores.
  4. I build everything with OOP these days and often I’ll find myself checking the result of uf_some_plugin__get_instance against if ( $some_plugin_obj instanceof \UF_Some_Plugin ). In this situation I instantiate the plugin as a single variable and then use the filter to get its instance instead of using static classes and singleton nightmares.

Here’s how it looks in practice.