WordPress Plugin Development: Best Practices

Share on facebook
Share on twitter
Share on linkedin

Creating an initial WordPress plugin is easy.  

Structuring the plugin when you know it’s going to be anything more than trivial is hard.

There are numerous web resources teaching you how to get started – and the official WordPress docs are pretty good too – but none of them teach you how to structure the plugin correctly, from the start. Here are the goals:

  • Use best-practice Object Oriented Programming (OOP) to create a clean, easily maintainable design.
  • Provide 100% unit test coverage of the code base.

The last goal is challenging.  WordPress has a ton of globals – both variables and functions  – that make unit testing especially challenging.

This article will show you how to use a combination of the registry and facade software design patterns to structure your plugin to achieve the above goals.  You should be familiar with PHP, WordPress, and plugin development beforehand or this article won’t make much sense.  On the other hand, if you’ve done any Laravel development, this pattern should seem kinda familiar.

All of the code presented in this article is available in this repository on Github and via composer on Packagist.


This article goes deep into the technical details fairly quickly.  You should already have a general understanding of how WordPress works and the basic structure of a WordPress plugin along with being comfortable with PHP in general.  If this is not yet the case, please make your way through the Plugin Handbook first.

What is the Registry pattern?

The registry pattern creates a central store of objects that your application can ‘register’ once and then request as needed.  The registry is a collection of objects referenced by a key – usually just a human readable name or the fully qualified name of the class.

Each object in the registry is essentially a singleton, with all the benefits but none of the drawbacks.   You can swap objects in and out of the registry for registry.

What is the Facade pattern?

At first, the facade pattern seems like one of those CS concepts that will rarely if ever be used in practice, but it turns out to be really useful in PHP.  

The facade pattern creates a class that acts as a gateway to a collection of classes that handle the actual implementation.  Usually the facade class itself doesn’t do anything but instead just calls an underlying class to do the work.

The facade class becomes a way of telling the rest of your application how to interact with part of itself.  It becomes like an internal library, dedicated to a particular concern.

There are multiple terms for the classes that implement the functionality behind a facade – , but we’ll use the term provider.  Providers are synonymous with a similarly popular term backends.

Interactive with the facade class is done via static methods.  Normally, static methods are difficult to test, but since all the work is delegated to the provider, that isn’t the case.  The end result is a very clean interaction for the rest of the application.

If you are familiar with Laravel, this approach should seem familiar.


Our approach will use a registry to hold our collection of providers.  When a method of a facade class is called, the facade will query the registry for the corresponding provider, retrieve it, and then call the method on the provider.  The facade doesn’t need to know anything about the provider, just that the provider has the method it needs.

Registry pattern

The registry implementation is fairly simple with just a handful of methods to register and query the registry itself.  

final class Registry {
    private static array $instances = array();

    public static function register( string $name, $instance ) {
        self::$instances[ $name ] = $instance;
        return $instance;

    public static function get( string $name ) {
        return self::$instances[ $name ] ?? null;

    public static function unregister( string $name ): void {
        unset( self::$instances[ $name ] );

    public static function has( string $name ): bool {
        return array_key_exists( $name, self::$instances );

All comments and error checking are removed for readability above.

Instead of a bunch of globals we have an array of instances, keyed by name.  The advantage is that unlike globals we can easily replace a particular instance, by name, during testing.

Facade/Provider pattern

The facade pattern implementation is more complex, in part due to the fact that it has two parts: the facade itself and the provider.

Here is the facade base class:

abstract class Facade {
    protected static function get_provider_alias(): string {
        $class = Utils::class_basename( get_called_class() );
        return Utils::snake_case( $class );

    public static function __callStatic( string $method, $args ) {
        $provider = Registry::get( static::get_provider_alias() );
        return $provider->$method( ...$args );

The __callStatic magic method is used to:

  • Get the relevant provider from the registry.
  • Call the same method name with the same on the provider.
  • Return the result.

The get_provider_alias method determines the registry key to be used – usually just the snake case class name of the calling class.

Creating a concrete facade class is often as simple as just extending this abstract class and adding some @method PHP doc comments to document the interface (mostly for your editor).

The base provider base class is actually simpler:

abstract class Provider {
    protected static function get_alias(): string {
        $class = Utils::class_basename( get_called_class() );
        if ( Utils::str_ends_with( $class, 'Provider' ) ) {
            $class = substr( $class, 0, strlen( $class ) - 8 );
        return Utils::snake_case( $class );

    public static function __callStatic( string $method, $args ) {
        if ( 'provide' === $method ) {
            $instance = new static( ...$args );
            Registry::register( static::get_alias(), $instance );

            return $instance;
        throw new Exception( 'Method ' . $method . ' is not supported.' );

All this class really does is:

  • Make sure the provider is linked to the facade by the key in the registry.
  • Defined the provide() method.

The provide() method is defined using __callStatic so that each provider concrete class may override it with its own signature, returning an instance of itself, instead of a generic ‘Provider’ instance.

Subclasses of the provider are where the main logic goes.  For example:

final class MyProvider extends Provider {
    public static function provide(): MyProvider {
        return parent::provide();

    public function my_method() {

Note that all methods except provide are non-static.

WordPress plugin structure

A facade/provider combo acts as a kind of library inside your plugin.  It should address one, specific concern with the facade providing the interface to the rest of the application (conceptual interface, not a true PHP interface).  The provider often has multiple subclasses that it uses internally, but these classes are never interacted with directly by the rest of the application.  (Like friend classes, if PHP had the concept).

In the plugin, you instantiated the provider once and then nothing interacts with it again, aside from its corresponding facade.  To put it another way, the plugin code only ever uses a facade, never the provider directly.


Once we have these classes available, usage is straightforward: we just call provide() on every provider we need, before we need it.  In practice this is done in the main plugin file i.e. the one with the special header.

The approach we use is immediately invoked, anonymous function – in Javascript terms an immediately invoked function expression (IIFE).  Basically just a function without a name that is immediately called:

(function() {
   $provider = MyComponentProvider::provide();

The above works in PHP 7, for earlier versions you can use call_user_function to do the same thing.

The purpose of this seeming over complication is just to avoid polluting the global namespace with variables you need for initialization – like $provider above.

Once you have defined the provider, your application logic calls a method statically like:


MyComponent now has a clear, defined interface that the rest of the application interacts with.

Normally, a bunch of static methods would make things much more difficult to test, but in this case, we actually do this solely to make the code testable as we’ll see below.

Unit Testing

The ability to unit test is the main reason for this structure.  We’re not going to get into what the true definition of unit testing is or unit vs functional tests – it would make this already long article much longer – but instead use a lowest-common-denominator definition that you can adapt to your needs, specifically:

  • Allows all code to be executed – i.e. 100% test coverage
  • Does not require WordPress core to run.

The last requirement means that our test cases inherit from PHPUnit\Framework\TestCase instead of WP_UnitTestCase.

This does not mean that you shouldn’t use WP_UnitTestCase – in fact, you very much should in practice – just that you don’t have to in order to test your core functionality.  Instead, the testing can be done in isolation.

The overall approach is that for a given piece of code to be tested, we’re going to mock – using Mockery – all of the facades used by that code.

Take, for example, a class representing a settings field in the admin dashboard.  We want to test a method on that call called validate.  (This method would be passed to the sanitize_callback argument of register_setting.)  All it does is see if the value passed is empty, and if so, it calls the global WordPress function add_settings_error and returns an existing value:

class Field {
    private string $value = ‘abcd’;

    function validate( string $arg ) : string {
        if ( empty( $arg ) ) {
                ‘Invalid argument’
            return $this->value;
       return $arg;

It’s a simply example but would be challenging to test without add_settings_error wrapped in the Globals facade.

Here is our test case for this function:

class FieldTest extends PHPUnit\Framework\TestCase {

    public function test_validate() {
        $field = new Field();
        self::assertEquals( '123', $field->validate( '123' ) );

    public function test_validation_error() {
        $globals = Mockery::mock(
        Registry::register('globals', $globals);

        $field = new Field();
        self::assertEquals( 'abcd', $field->validate( '' ) );

This test covers all lines of our function without requiring any WordPress code to run.  We’ve mocked the GlobalsProvider that is part of the framework, but the same approach works for your own providers – simply specify the fully-qualified class name.

Of course, you’d still need additional testing to ensure these results are what you actually want.

The above code is simple enough that we could’ve achieved 100% test coverage using WP_UnitTestCase but more often than not this isn’t the case.  Trying to unit test code that calls wp_redirect, for instance, is nearly impossible (PHPUnit complains when it tries to write headers).  But if the wp_redirect function is called through the Globals facade, the problem goes away.


Every approach has downsides and you should definitely question any blog article that claims otherwise. Fortunately the issues with this approach are relatively minor.

Providers can’t be marked final.

You might want to mark your provider class as ‘final’.  It makes sense, it is the provider so it won’t be subclassed.  However, doing so prevents you from fully mocking the class (partial mocks would still be possible, but discouraged.)  This is really a limitation of mocking in PHP, but it affects your design nonetheless.

Code editors sometimes get confused.

The facade hides the provider implementation from the rest of the plugin – as it’s designed to do – but that means the implementation is also hidden from the editor.  This results in editors complaining about unused methods and especially not understanding what exceptions a method throws.  This can be partially mitigated by PHPDoc tags (i.e. @method) but exception information is still lost.

Type information may be lost.

Since the registry is a collection of generic instances, its impossible to know the type of an instance you get out of the registry (via get()).  

Your code – especially test cases – inevitably ends up with PHPDoc comments to specify type like:

/* @var LoggerProvider $logger */

Alternative approach: Dependency Injection

An alternative approach that bears mentioning is dependency injection.  In its simplest form, dependency injection is just passing everything you need to a function or class instead of using the corresponding global.  For OOP, this is usually done via the class constructor.

Using this method, mocks are passed in during testing instead of the real objects – global functions still have to be wrapped in a class – which allows the same level of testing as above.

The problem with using dependency injection in WordPress is the shear number of objects the plugin inevitably ends up passing around.  If your objects use separation of concern at all, any reasonably sized plugin will end up having constructors with far too many parameters – sometimes only used to pass on to subclasses.  

There are more sophisticated methods for doing DI like automatically injecting the parameter based on the name from a container, but then you’re essentially back to the registry pattern.


Starting with the correct plugin design, with a thorough testing process, guarantees that a plugin won’t turn into an unmaintainable mess.  This initial investment will save you time in the long run.  We’ve learned that lesson over and over.

Using this approach ensures your plugin will have a clean design and be easily maintained no matter how complex it becomes.  The Github repo, including the example plugin, should jump start this development process.  It may not be as quick as knocking out ‘Hello Dolly’ but it’s well worth the time.






composer require mindspun/wordpress-plugin-framework