Writing extensible plugins: Early exit

How filters and actions are used in the WordPress Core is very interesting and a nice principle for extensible plugins or themes. Within the last years I stepped several times through the WordPress core and saw some lovely structures. Let me show you some of them so that your plugin or theme is more flexible and makes other developers happy.Read the more up to date and famous one afterwards!
WordPress 4.7: Custom bulk actions, Theme inheritance and more
Open in new tab for later

If you know other principles or tricks to keep a plugin or them extensible then please leave a comment below.

Edit: Some complained that this is very harsh and slows down the plugin. So please consider using this only when needed or appropriate. And don’t use it everywhere. Just where you allow others to extend your plugin. Sorry, for this interruption.

Extensible plugins and themes

Currently a friend and I are writing a plugin. We like to do it clean and correct from the beginning. By now it has …

  • 1 feature
  • 1 action
  • 3 options
  • 4 functions
  • and 4 filters

That is not much, it is enough and it is needed there. Almost every point and data in the API can be changed by some other third-party plugin. I hope that this satisfy all developers as they can manipulate the whole plugin for the interests of their customers.

Structured programming to the rescue

Don’t worry, we both studied applied computer science and know when to stop. In addition we derive the extensible code from well known and old principles. I like to share some of those structured programming deviations and suggestions how to write flexible plugins with …

  • Early exit (this post, now)
  • Multiple entry in loops (come back Friday)
  • Late entry manipulating the return-value (maybe Sunday)

Writing maintainable code is about good control flow graphs, so that you don’t end in spaghetti code. Since 1966 this is known as the “structured program theorem” (or more nerdy: “Böhm-Jacopini theorem”) where languages like ALGOL 60 emerged and allowed block structures. Even Edsger W. Dijkstra (known for his “shortest path algorithm”) joined the discussion and paved the way for structured programming. Back then it was meant for procedural programming and can still be found in modern high level languages today.

Enough of the theory, let’s write code around it!

The early exit deviation / filter

The most common thing that everyone should be aware of is an “early exit” from a function or a loop. Early exists are often solved by testing things at the very beginning and jump out in case something went wrong. Here is a simple example with two early exists (see “A” and “B”):

// example for early exits via assertions
function calc_something( $price, $amount ) {
  if ( ! is_numeric ( $price ) || ! is_numeric ( $amount ) ) {
    // A: whoa? I want numbers! => drama queen mode enabled
    return false;
  }

  if ( $price < 0 || $amount < 0 ) {
    // B: negative values => we refuse to handle that
    return false;
  }

  // we made it through, yay \o/ => calc
  return $price * $amount;
}

So the first point (A) checks if the passed arguments are valid numbers. If not we early exit without doing anything. The second point (B) asserts that the numbers are bigger than zero, so that no one can trick us in the shop. If not the script exits another time very early. At last and when everything feels good the script does what it is meant for.

I like it as this code is very well structured and so readable. Instead of 200 nested if-statements you just have one level and exit the function when something is wrong. Very flat complexity, easy to maintain.

Overwrite all shortcodes, widgets and so on

So now you are in your plugin or theme and create some shortcodes, widgets or other functionality. Today you might like to write it with some “if-function-exists”:

if ( ! function_exists ( 'calc_something' ) ) {
  function calc_something() {
    // ...
  }
}

A very common way to allow child-themes overwriting functions. Changing the behavior of shortcodes and others. But this does not really apply to plugins. We need something different and the solution can be found in the WordPress Core.

The WP Core uses early exit filter

As I investigate the get_option functions I found plenty of early exists solved via filters. It is possible to overwrite every single option with an own value during run-time. In general it means you can overwrite the way how get_options work and replace it with something totally different. This is nice, we want that power of modular extensibility for our theme or plugin:

function calc_something( $price, $amount ) {

  // ask who likes to handle that
  $pre = apply_filters(
    'pre_calc_something',
    null,    // assume no one want's to
    $price,  // but if so, then give away all arguments
    $amount
  );

  if ( null !== $pre ) {
    // someone changed us => use his result
    return $pre;
  }

  // ... our way ...
}

Let’s take a good look what happens here. Imagine the functions is for a shortcode or so. Once the function is called it uses the apply_filters function to ask everyone if they have a “better” solution. When there is no response the value will stay “null” and the function will go on.

Using a filter-name that represents the function is a common and very intuitive way. WordPress uses “pre_{function-name}” at some points, so do we. At that point every third-party plugin can hook into our plugin.

Extend any function with filters

With the filter we allowed other functions to replace our own logic. Keep in mind that this post is all about replacing functionality. There is a more softer way of changing behavior via “multiple entries” what the upcoming article on Friday is about. Very fancy things.

For now lets see how some third-party plugin can enhance our plugin:

// hook in and receive the two arguments
add_filter( 'pre_calc_something', 'everything_for_free', 10, 3 );

function everything_for_free( $value, $price, $amount ) {
  return 0;
}

Okay, it is not necessary to get the last two arguments but I liked to give you a complete example. Now everyone that uses the calc_something function will get the results of your function instead. Everything will be delegated to another function like our everything_for_free above. And even that can be overridden by other functions that hook in the pre_calc_something filter.

Conclusion

Call it pattern. There are so much to know about, so much to learn. The more I apply to the plugin the better it gets. Meanwhile the fear of performance issues grows. Just avoid excessive use of filters or actions and keep it where it makes sense. Separate the plugin extensible functions with some filter and internal functions without any filter but options. This will do.

What other methods do you have for better or well structured plugins?
Please let me know in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *