Want to get more articles like this one? Join my newsletter

Using dependency injection with WordPress

As you use object-oriented programming with WordPress more and more, you’re going to encounter new sets of problems. These problems aren’t as simple as the ones that you encountered when you started. But that’s also good news. It also means that you’re making progress.

One set of problems that you start to encounter as you advance in your use of object-oriented programming deal with scaling. As your code has more and more classes, it becomes a challenge to assemble these classes together. That’s why in the past we looked at how to design a class whose job was to do that.

But there are other problems that come with having a lot of classes in your code. It’s how much your classes depend on other classes. This is what we call coupling in programming. (Not just object-oriented programming.)

To help with coupling, a famous software engineer (Robert C. Martin who’s also known as Uncle Bob) created the dependency inversion principle. It helps you decouple your classes by making dependencies external to them. (There’s more to it than that. But this is one of the core elements of the dependency inversion principle.)

This reduces coupling but creates a new problem as well. That problem is that you can’t initialize objects inside your class anymore. You have to pass them to your own object through its constructor or some other method like a setter method.

This makes it even harder to assemble your classes together. This is why software engineers created dependency injection. It’s a programming technique that helps solve this problem.

Most web application frameworks use it in some form or another. But that’s not the case with WordPress since a lot of its code isn’t object-oriented. That said, if you want to build an object-oriented plugin for WordPress, it’s pretty much mandatory. Lucky for us, it’s not that hard to use dependency injection in your own code!

A small warning about PHP versions

While it’s not recommended anymore, WordPress still has a minimum requirement of PHP 5.2.4. And, while it’s not impossible to use dependency injection with PHP 5.2, it’s a lot harder. For the sake of keeping our implementation of dependency injection simple, we’re going to use a specific feature from PHP 5.3.

That feature is anonymous functions. Anonymous functions as their name implies are functions without a name. (And thus anonymous!) We’ll look at why we want to use anonymous functions to implement dependency injection a bit later.

This was just a small warning before we get further into this article. If your code has to support PHP 5.2, you won’t be able to use the code that you’ll see in this article. That said, dependency injection is still a super important concept in modern programming. (So you’ll still learn something useful!)

Going over some concepts

Before we can begin, let’s go over some important concepts. They’ll help you make sense of what we’re doing throughout the rest of the article.

Dependency injection explained

First, there’s dependency injection. (The topic of this article!) We mentioned earlier that it’s a programming technique. But that’s a pretty vague description. It doesn’t tell us that much about it.

The story behind dependency injection begins with the dependency inversion principle. Without it, there would be no need for dependency injection. That’s because, as we explained in the introduction, the dependency inversion principle forces you to move your class’s dependencies outside of it.

You want to do that because it reduces the amount of coupling that your class has. And one way that the dependency inversion principle achieves that is by removing the instantiation of new objects from your class. Instead of doing that, you need to pass those objects to your class using a constructor or a method.

But your class still needs to receive its objects from somewhere. If you can’t do it inside your class, where can you do it? Well, as you can guess, that’s the question that dependency injection wants to answer!

Dependency injection is this idea that there should be a system that handles these objects that your class depends on. This system would inject those objects (thus the name dependency injection!) into your class. It can do either by constructing the object and passing it its dependencies. Or it can use methods that you’ve created for that purpose like setter methods. We call those constructor injection and setter injection respectively.

On service classes and singletons

But dependency injection isn’t the only concept that we need to go over. There’s another important one that you need to understand with dependency injection. It’s the idea of a service.

A service is a class that offers a specific functionality (or service!) that your application depends on. At a glance, there’s no obvious difference between a class that’s a service and one that’s not. That’s due to the fact that what makes a service special and worth discussing is how you instantiate it.

A service is often a class that you want to instantiate only once. But why would you want to instantiate a service class only once? It’s because that service object represents the service for the entire application.

If a class needs to use that service, it really just wants to use that one service object. This is why it’s common for people new to object-oriented programming to build their service classes using the singleton pattern. This helps them enforce this single object requirement for service objects.

This is also why a lot of WordPress developers end up designing all their classes as singletons. Most of the classes that they create are service classes. And they feel compelled to use the singleton pattern to ensure they get created once.

This is why we’re seeing the concept of a service class. Being able to create service objects is an important part of any dependency injection system. This means that, by implementing your own dependency injection system, you should be able to move beyond the use of singletons. (Yay!)

Using dependency injection

Now that we’ve gone over the concepts surrounding dependency injection, we can start looking at how to use it. As we mentioned earlier, dependency injection relies on a system to inject our objects. This system can be large and complex with a lot of classes. But it can also be small with a single class doing all the work.

As you can guess, we’re not interested in building a large dependency injection system. We’re going to keep this simple and build one class to handle dependency injection for us. We call this class a container.

Building a container class

Alright, so let’s begin building our MyPlugin_Container class! As usual, we’re going to start with an empty class. You can find it below:

class MyPlugin_Container
{
}

Adding some storage

But a container wouldn’t be a container without some way of storing things inside of it. The easiest way to do that is with an array. So let’s add that to our MyPlugin_Container class.

class MyPlugin_Container
{
    /**
     * Values stored inside the container.
     *
     * @var array
     */
    private $values;

    /**
     * Constructor.
     *
     * @param array $values
     */
    public function __construct(array $values = array())
    {
        $this->values = $values;
    }

    // ...
}

Above you can see that we added the values properties to our MyPlugin_Container class. This is the class property that we’ll use to store everything in our container. We also need it to be an array.

That’s why we also added a constructor to our MyPlugin_Container class. The constructor has values as a parameter which we force to be an array using type hinting. We also give it an empty as a default value. This ensures that, when we assign it to the values property, it’s always an array.

Accessing our storage

Now, the values property that added is private. That means that there’s no way for other developers to access the values stored in the container. As with most things in programming, there are various ways that we can do this. (Wouldn’t it just be more convenient if there was always one good answer!?)

Most of us are familiar with arrays, so it would make a lot of sense if our MyPlugin_Container class behaved like one. This is something that isn’t too complicated to do. We just need our class to implement a specific PHP interface.

class MyPlugin_Container implements ArrayAccess
{
}

The interface in question is the ArrayAccess interface. It’s an interface that lets you access objects like you would an array. The interface requires that we implement four methods: offsetExists, offsetGet, offsetSet and offsetUnset.

Implementing the interface

Let’s look at how we’ll implement these methods in our MyPlugin_Container class. That said, we won’t implement them all right away. We’ll start with three of the four methods that the ArrayAccess interface wants.

class MyPlugin_Container implements ArrayAccess
{
    // ...

    /**
     * Checks if there's a value in the container for the given key.
     *
     * @param mixed $key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return array_key_exists($key, $this->values);
    }

    /**
     * Sets a value inside of the container.
     *
     * @param mixed $key
     * @param mixed $value
     */
    public function offsetSet($key, $value)
    {
        $this->values[$key] = $value;
    }

    /**
     * Unset the value in the container for the given key.
     *
     * @param mixed $key
     */
    public function offsetUnset($key)
    {
        unset($this->values[$key]);
    }

    // ...
}

You can see the three methods that we implemented in our MyPlugin_Container class above. The offsetGet method is the method that we left out. It’s a much more important component of our dependency injection container so we’re going to cover it a bit later.

The first method that we added was the offsetExists method. PHP will call it when you do an isset check on your object like you would on an array. The method itself returns the result of the call to the array_key_exists function. We pass it the key value that you used with the object and our values array.

The second method is offsetSet. This is the method that PHP will call when you try to assign a value to your object like you would with an array. The method has two parameters: key and value. We just use the key value as the index for our values array and value is the value that we assign to it.

The offsetUnset method has the opposite effect of the offsetSet method. PHP will call this method when you want to remove a value from your object. This happens when you use the unset function on your object like you would on an array. We just do the same call to the unset function on our values array.

Going back to anonymous functions and services

So why haven’t didn’t we implement the offsetGet method with the others? Well, that’s because it’s where the magic inside of our MyPlugin_Container class happens! But before we can look behind the scenes at how that magic works, we need to take a step back.

That’s because we need to talk more about two concepts that we talked about earlier. These two concepts are anonymous functions and services. It’s time to look how they fit into the larger picture of dependency injection.

The link between anonymous functions and dependency injection

Let’s start with anonymous functions. Why are anonymous functions so critical to dependency injection? It’s because they allow us to encapsulate the creation of an object.

$myplugin_class = function () {
    return new MyPlugin_Class();
};

Here’s an example of an anonymous function. The function returns a new instance of the MyPlugin_Class class. (To keep things simple for now, we’ll say that the MyPlugin_Class class constructor doesn’t take any arguments.)

$myplugin_object1 = $myplugin_class();
$myplugin_object2 = $myplugin_class();

We can then use the variable containing our anonymous function to create new objects. Both myplugin_object1 and myplugin_object2 are different instances of MyPlugin_Class. So, if you did $myplugin_object1 === $myplugin_object2, it would return false.

We call the === comparision operator an identical check. With objects, it will only return true if the objects share the same reference. Since we created two different objects with our anonymous function, they aren’t identical. That’s why $myplugin_object1 === $myplugin_object2 returns false.

Using anonymous functions for services

But, if we want to use an anonymous function to create a service object, we need our two objects to be identical. This means that we need our anonymous function to return the same service object every time. So let’s create an anonymous function that creates a service object instead of a new one each time.

$myplugin_class_service = function ()  {
    static $object;

    if (!$object instanceof MyPlugin_Class) {
        $object = new MyPlugin_Class();
    }

    return $object;
};

The key to creating a service object with an anonymous function is to use the static keyword. If you’ve designed a singleton before, there’s a good chance that the structure of the anonymous function is familiar to you. It’s pretty much the same as the one you’d see in a singleton.

We start by defining an object variable using the static keyword. We then use a guard clause to check if we’ve assigned a value to our object variable. We do this by using the instanceof to check if object is an instance of MyPlugin_Class.

$myplugin_object1 = $myplugin_class_service();
$myplugin_object2 = $myplugin_class_service();

Now, let’s go back to code that we used earlier to create to create myplugin_object1 and myplugin_object2. We changed it to use our new myplugin_class_service anonymous function to create the objects. In this scenario, both myplugin_object1 and myplugin_object2 will be identical. If you did $myplugin_object1 === $myplugin_object2, it would return true.

Getting objects from our container

Alright, so now we can go back to our missing offsetGet method! The goal of our MyPlugin_Container class is to be able to store anonymous functions like the ones that we just saw. So the code that we had earlier would become something like this:

$container['myplugin_class'] = function () {
    return new MyPlugin_Class();
};

$myplugin_object = $container['myplugin_class'];

The issue with this example is that our dependency injection container can also store other things. It doesn’t just store anonymous functions. So we need a way for it to know if we’re trying to create an object with an anonymous function.

$container['myplugin_class'] = function () {
    return new MyPlugin_Class();
};
$container['myplugin_variable'] = 'variable';

$myplugin_object = $container['myplugin_class'];
$myplugin_variable = $container['myplugin_variable'];

Our offsetGet method needs to work for both myplugin_object and myplugin_variable. For myplugin_object, would return a new instance of the MyPlugin_Class class. And, for myplugin_variable, it needs to just return the value that we stored.

Anonymous functions are objects

To do that, we’re going to use a lesser known fact about anonymous functions. In PHP, anonymous functions are actually instances of the Closure class. This means that we can detect if we stored anonymous function by using the instanceof operator.

class MyPlugin_Container implements ArrayAccess
{
    // ...

    public function offsetGet($key)
    {
        if (!array_key_exists($key, $this->values)) {
            return new WP_Error('no_value_found', sprintf('Container doesn\'t have a value stored for the "%s" key.', $key));
        }

        return $this->values[$key] instanceof Closure ? $this->values[$key]() : $this->values[$key];
    }

    // ...
}

So here’s our offsetGet method at last! For all the explanations that we went through, it’s still a small method. That said, there are still a few more things going on that are worth explaining.

First, we have a guard clause. It checks if we even have the given key in our values array. We perform the check using the array_key_exists function like we did in the offsetExists method.

If the key isn’t present in our values array, we return a WP_Error object. (You could also throw a InvalidArgumentException if you prefer!) Otherwise, we return the value stored in our dependency injection container for the given key.

We use the ternary operator to do that. We use it to determine if we stored an anonymous function or not. If we have an instance of a Closure, we call our stored value as a function. (Like we did earlier!) Otherwise, we just return it as is.

Storing a service object

At this point, our MyPlugin_Container is pretty well rounded. But there’s still one more element that we haven’t covered yet. It’s how to create service objects using our dependency injection container.

We saw earlier that we can create service objects using an anonymous function and the static keyword. But, in our example, we also created manually by doing new MyPlugin_Class(). This doesn’t work with the way that we create objects with our dependency injection container.

With our dependency injection container, we use anonymous functions to create our objects. This means that our anonymous function for creating service objects should work with them as well. We can do this by making the anonymous function for creating service objects reusable.

class MyPlugin_Container implements ArrayAccess
{
    // ...

   public function service(Closure $closure)
    {
        return function () use ($closure) {
            static $object;

            if (null === $object) {
                $object = $closure();
            }

            return $object;
        };
    }

    // ...
}

This is the goal of the service method above. The method has a single parameter: closure. We also enforce that closure is a Closure object using a type hint.

The method itself just returns an anonymous function similar to the one we saw earlier for service objects. We pass this closure to another anonymous function using the use language construct. (We’re going full closure inception here!) The anonymous function then uses that closure if we didn’t create that object already.

$container['service'] = $container->service(function () {
    return new MyPlugin_Class();
});

And here is how we’d use our service method with our container. We store the anonymous function returned by the service method inside our dependency injection container. And we pass the service method the anonymous function that creates our MyPlugin_Class object.

Injecting dependencies with our container

Alright, so, at this point, our MyPlugin_Container class is pretty much done. That said, to keep things simple, we worked under an important assumption. It’s that the constructor of our MyPlugin_Class (or any other class) doesn’t take any arguments.

But that’s not how things work in practice. Most classes have constructors that require that you pass them one or more arguments. That’s the point of dependency injection after all! So we need to tweak our MyPlugin_Container class so that we can inject dependencies when we create our objects.

The way we do this is pretty simple. All that we have to do is pass our MyPlugin_Container object to our anonymous functions. If we did that, we could create anonymous functions that look like this:

$container['myplugin_class'] = function (MyPlugin_Container $container) {
    return new MyPlugin_Class($container['service']);
};

You’ll notice that we added our MyPlugin_Container as a parameter of our anonymous function. This lets us use our dependency injection container inside the anonymous function. And that way we can use it to inject a dependency into our MyPlugin_Class.

This is the trick to doing dependency injection with this type of dependency injection container. We pass the dependency injection container to the anonymous functions creating our objects. And they use it to inject the dependencies that they need to create the object in question.

Modifying our container class

Alright! So the only thing left to do is to modify our MyPlugin_Container class. We need it to pass itself to our anonymous functions when it calls them. This isn’t too hard to do because there are only two places where this happens in our MyPlugin_Container class.

class MyPlugin_Container implements ArrayAccess
{
    // ...

    public function offsetGet($key)
    {
        if (!array_key_exists($key, $this->values)) {
            return new WP_Error('no_value_found', sprintf('Container doesn\'t have a value stored for the "%s" key.', $key));
        }

        return $this->values[$key] instanceof Closure ? $this->values[$key]($this) : $this->values[$key];
    }

    // ...

    public function service(Closure $closure)
    {
        return function (MyPlugin_Container $container) use ($closure) {
            static $object;

            if (null === $object) {
                $object = $closure($container);
            }

            return $object;
        };
    }
}

Above is our modified MyPlugin_Container class. We made two small changes to it. Those were at the two locations where we interacted with anonymous functions.

The first change is in the offsetGet method. We now pass the anonymous function a copy of the object when we have the values array stores a Closure. We do this by passing the this pseudo-variable representing our object to the anonymous function.

The other change that we did is to the service method. Here, we changed the anonymous function that the method returns. It now has a container parameter like the anonymous function that we saw earlier.

Organizing the configuration of our container

So, at this point, our MyPlugin_Container class is fully functional. You can use it in your plugin to create that objects that it needs. That said, there’s still a small improvement that we can make to it.

This improvement relates to how we configure our dependency injection container. So far, our dependency injection container configuration examples have been quite simple. We were always configuring a single service or object.

But this isn’t how things will play out in practice. In reality, you’ll have dozens of classes, parameters and services. And you’ll want to configure them all inside your dependency injection container. This can get messy really fast if you don’t have a way to organize things. (You always want to keep your files organized!)

This is the final improvement that we’ll make to our MyPlugin_Container class. We’ll create a way to organize the configuration of the container. That way you won’t be stuck with a single large file with all the container configurations in it.

Creating a configuration contract

The easiest way to do this is with an interface. Why an interface? Because we’re not interested in creating an object for configuring our dependency injection container.

What we really want is to create a contract that tells other developers how they can configure it. This gives them more flexibility when trying to organize how they want to configure the container. It also makes sense because, as you’ll see soon, these configuration class don’t have any code that we want to reuse.

interface MyPlugin_ContainerConfigurationInterface
{
    /**
     * Modifies the given dependency injection container.
     *
     * @param MyPlugin_Container $container
     */
    public function modify(MyPlugin_Container $container);
}

But first, let’s look at the interface itself! Above is the code for our MyPlugin_ContainerConfigurationInterface interface. The interface has a single method: modify.

The role of the modify method in our MyPlugin_ContainerConfigurationInterface interface contract is pretty explicit. It expects to receive the MyPlugin_Container dependency injection container that we’re configuring. And it’ll make modifications to it.

Implementing our configuration contract

Now that we have our MyPlugin_ContainerConfigurationInterface interface, we can look at how to use it. Let’s imagine that our plugin has a specific component or system like an event manager or a router. A configuration class would look something like this:

class MyPlugin_ComponentConfiguration implements MyPlugin_ContainerConfigurationInterface
{
    public function modify(MyPlugin_Container $container)
    {
        $container['component_parameter'] = get_option('myplugin_component_parameter', 'default_value');

        $container['component_service'] = $container->service(function (MyPlugin_Container $container) {
            return new MyPlugin_ComponentClass($container['component_parameter']);
        });
    }
}

We have the MyPlugin_ComponentConfiguration class for our imaginary component. It implements the MyPlugin_ContainerConfigurationInterface interface that we created in the previous section. This means that the class also has the modify method required by the interface contract.

The modify method itself adds two things to our dependency injection container. First, it adds the component_parameter which is an option stored in the WordPress database. We fetch it using the get_option function.

We then use that component_parameter parameter when creating our component_service. The component_service uses the service method that we created earlier. We pass it an anonymous function that creates a MyPlugin_ComponentClass object using the component_parameter parameter.

On creating a WordPress configuration class

You might be wondering about the use of the get_option function in the modify method. Is it a good idea to use WordPress function like that when configuring a dependency injection container? The answer is yes! It’s actually a great use of a dependency injection container.

That’s because it’s a real challenge to design classes that don’t use WordPress functions. In fact, it’s the biggest struggle that WordPress developers have when they’re trying to learn to use object-oriented programming. But with a dependency injection container, we can decouple your code from WordPress code.

class MyPlugin_WordPressConfiguration implements MyPlugin_ContainerConfigurationInterface
{
    public function modify(MyPlugin_Container $container)
    {
        $container['wordpress_database'] = $container->service(function (MyPlugin_Container $container) {
            global $wpdb;

            return $wpdb;
        });

        $container['wordpress_query'] = function (MyPlugin_Container $container) {
            return new WP_Query();
        };

        $container['wordpress_user'] = wp_get_current_user();
    }
}

The MyPlugin_WordPressConfiguration class above is an example of what we mean by that. We use it to modify our dependency injection container with things that would normally couple us to WordPress code. You can add WordPress related classes, parameters and services.

The MyPlugin_WordPressConfiguration class has an example of each. First, we added the wpdb class as a service. Next, we added an anonymous function which creates WP_Query object each time that you ask for it. The last one stores the current user as returned by the wp_get_current_user function.

There’s really no limit to what you can store inside your dependency injection container. Any time that you need something from WordPress, you should just add it to your dependency injection container. That way you won’t need to use WordPress functions inside your classes anymore! (Well, at least, not as much!)

Configuring a container using configuration objects

So, at this point, we broke down the configuration of our dependency injection container into configuration classes. But we still can’t use these configuration objects with our MyPlugin_Container class. We need to add a way to pass to objects to it.

class MyPlugin_Container implements ArrayAccess
{
    // ...

    /**
     * Configure the container using the given container configuration objects.
     *
     * @param array $configurations
     */
    public function configure(array $configurations)
    {
        foreach ($configurations as $configuration) {
            $configuration->modify($this);
        }
    }

    // ...
}

That’s the goal of the configure method shown above. The method has a single parameter: configurations. configurations is an array of configuration objects implementing the MyPlugin_ContainerConfigurationInterface interface.

We use a foreach loop to go through each configuration object. The loop itself just calls the configuration object’s modify method. And we pass it the this pseudo-variable representing our container as the container argument.

It’s worth noting that we’re not doing any validation on the content of the configurations array. We assume that the array will only contain objects implementing the MyPlugin_ContainerConfigurationInterface. But it’s possible that you won’t feel comfortable with that. If that’s the case, you can add some validation inside the foreach loop to check if configuration variable implements the interface. (The easiest way to do that is with the instanceof operator.)

$container = new MyPlugin_Container();
$container->configure(array(
   new MyPlugin_ComponentConfiguration(), 
   new MyPlugin_WordPressConfiguration(), 
));

This brings us to how we’d use the configure method in practice. This is what this last code sample shows. We initialized our MyPlugin_Container class and then called the configure method. As an argument, we passed it an array with the two configuration objects that we created earlier.

A requirement with object-oriented programming

And this is how you can use dependency injection with WordPress! The whole thing revolves around the MyPlugin_Container class that we created. You can find the complete code for it below:

class MyPlugin_Container implements ArrayAccess
{
    /**
     * Values stored inside the container.
     *
     * @var array
     */
    private $values;

    /**
     * Constructor.
     *
     * @param array $values
     */
    public function __construct(array $values = array())
    {
        $this->values = $values;
    }

    /**
     * Configure the container using the given container configuration objects.
     *
     * @param array $configurations
     */
    public function configure(array $configurations)
    {
        foreach ($configurations as $configuration) {
            $configuration->modify($this);
        }
    }

    /**
     * Checks if there's a value in the container for the given key.
     *
     * @param mixed $key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return array_key_exists($key, $this->values);
    }

    /**
     * Get a value from the container.
     *
     * @param mixed $key
     *
     * @return mixed|WP_Error
     */
    public function offsetGet($key)
    {
        if (!array_key_exists($key, $this->values)) {
            return new WP_Error('no_value_found', sprintf('Container doesn\'t have a value stored for the "%s" key.', $key));
        }

        return $this->values[$key] instanceof Closure ? $this->values[$key]($this) : $this->values[$key];
    }

    /**
     * Sets a value inside of the container.
     *
     * @param mixed $key
     * @param mixed $value
     */
    public function offsetSet($key, $value)
    {
        $this->values[$key] = $value;
    }

    /**
     * Unset the value in the container for the given key.
     *
     * @param mixed $key
     */
    public function offsetUnset($key)
    {
        unset($this->values[$key]);
    }

    /**
     * Creates a closure used for creating a service using the given callable.
     *
     * @param Closure $closure
     *
     * @return Closure
     */
    public function service(Closure $closure)
    {
        return function (MyPlugin_Container $container) use ($closure) {
            static $object;

            if (null === $object) {
                $object = $closure($container);
            }

            return $object;
        };
    }
}

This class might not seem like much to you. Most of it is just documentation anyways. (Because documentation is important!) But, as you use it, you’ll come to realize how important it is to your success with object-oriented programming.

That’s because, as we mentioned in the introduction, you’re going to create more and more classes as time goes on. It’s the nature of object-oriented programming. (And a legitimate complaint that some developers have with it.) Managing all these classes in a sane way will become a real issue.

This is when using a dependency injection container will go from something that’s nice-to-have to a must-have. It’s a good thing that using one isn’t that complicated. You can just reuse this class and start building some configuration classes of your own.

Creative Commons License