Three Tools To Make Your WordPress Plugin Extendable

Posted on:

WordPress is known for being a very extend-able platform. I believe that the litany of plugins that extend WordPress are what has made this particular CMS so ubiquitous in web development. But as a developer, this extend-ability forces us to make a decision - just how open do I make my plugin to extension? The answer to this question depends very much on how your plugin is distributed, and determines how you approach supporting your plugin, and so many other things.

When you dive deeper into answering this question, you'll find that many of the more-robust WordPress plugins have implemented other ways to make their plugin extend-able that goes above and beyond what WordPress core offers. This post talks about some different ways you can make your plugin extend-able.

Hooks and Filters

If you've spent any time working in WordPress, you've probably worked with one of these. A WordPress hook allows you to inject your own code in different places throughout WordPress. They work by adding a function call to a hook name, and when that hook is called all of the function calls that were associated with that hook beforehand run.

The awesome thing about hooks and filters is their simplicity. You simply pass a call to either apply_filters or do_action in the right place, make sure the hook name is unique and voila! You just made your plugin extend-able.

With that simplicity comes some issues, however. When making your plugin extend-able it's best to think deeply on how it would be extended, and work to make it easy for developers to extend in the ways they want. With hooks and filters, it's easy to do that, but sometimes you can make your plugin too open, and that can make a parts of a plugin extend-able in a way it should not be extended.

If you're selling a commercial plugin with an ecosystem of third party plugins, you will want to protect the integrity of your core plugin. After all, if a third party plugin messes up some important core feature, the WordPress admin may not have any idea that it's the third party - and it will reflect poorly on you. Nobody wants that!

They can also be difficult to troubleshoot because it is unclear what actions fire unless you're looking at the code itself. If you're troubleshooting a site you don't know much about, this can make troubleshooting a problem with your site difficult.

I personally use hooks sparingly, in very simple situations that impact a small, specific part of the plugin.

Underpin's Decision Lists

Underpin has a decision list loader. Decision Lists contain a list of Decision classes. When a decision needs to be made, the Decision List will loop through each decision class, in order of priority, see if they meet the criteria to be the selected decision, and if so run a specific action. If the decision does not meet the criteria, Underpin logs why that decision was not made, and moves on to the next one. This is a little more complicated than a filter or hook, but Underpin hides most of this complexity so all you need to provide is an array of decisions to make, and it will take it from there.

What's nice about this approach is that you can actually extend the decision list class itself. This allows you to override how the decision list actually makes its decisions if you want. You can add default values, and even add extra logging if you need that. This gives you a bit more control over how developers extend your plugin.

Decision lists also log every decision item that it tries to run. If the decision should not run, it will return a WP_Error object which gets returned by the decision list alongside the actual result. This gets logged to the debug log, so when you're troubleshooting your plugin you can figure out what decision was made, as well as which ones were not made, and why. (Side note - this is a great example of why you should stop returning false in many cases!).

Registry Middleware

I wrote an entire blog post dedicated to different ways you can use registries to make your plugin extend-able. The gist is that a registry is basically a fancy array. It's usually accessed using functions or methods instead of directly manipulating the array itself. Underpin's entire system revolves around making registries easy to create, and use. In-fact, the other two examples are technically considered registries themselves.

Oftentimes, I will create a custom registry specific to some kind-of construct that my plugin uses. This allows me to control how these items are used in my plugin, and helps to keep my core plugin intact and in-control over how it is extended. If you've ever used register_block or register_post_type, you're essentially using the pattern I'm talking about, only what you register would be custom to your needs.

Sometimes, you will find that a some tasks need to run on some registered items as soon as they're registered. This can be done by extending the class that is used in the loader, but this can be pretty verbose, and isn't always possible. By allowing third party developers to extend your class, you're also giving them full access to change any protected method in your class. This isn't always wanted.

In Underpin, Registry Middleware allows you to do a specific set of tasks just after the item is added to the registry. These tasks have access to the registered item from outside the class. This means that you can control what can be changed without giving up the ability to change these things in your core plugin.

Another thing that's nice about this method is that these middleware items can be re-used for multiple registered items, or you can create your own custom middleware. This allows you to pre-build middleware items that other developers can use to extend how their registered item behaves without forcing them to re-write the same function over, and over again.

Conclusion

Even if you don't use Underpin, you can apply the concepts behind these different patterns in your own plugin. If you take the time to think deeply about how your plugin needs to be extended, and choose the best route accordingly, you can save yourself a lot of backwards compatibility headaches in the future.