Want to learn object-oriented programmming? Get my free course

Designing a class to assemble plugin classes

Learning object-oriented programming with WordPress is a lot like embarking on an epic journey. You’ll experience some lows, some highs and some downright frustrating moments. But it’s all part of the learning process.

And, so far, we’ve done quite a bit of progress on our journey. We’ve looked at various WordPress problems. And we then solved them using a class or combination of classes.

We call this process “object-oriented design“. It’s been the focus of a lot of the articles on this site. Because of that, there’s a good chance that you’ve begun using it in your own projects. (And if you haven’t, you should try it!)

When that happens, you’ll start to notice that your project has a lot of classes. This tends to become a problem for a lot of developers. They wonder, “How do I assemble all these classes together!?” It’s not unusual for them to revert back to the standard WordPress way of doing things when that happens.

This is the problem that we’re going to explore in this article. We’ll look at how you can solve it using a dedicated class for it. To do this, we’ll start thinking about the job of the main plugin class.

Defining the plugin class

What do we mean by main plugin class? Well, let’s say we had a plugin namespace called MyPlugin. The main plugin class would be MyPlugin_Plugin. (The redundancy department of redundancy approves of this name.) Let’s work on defining what this class does.

Going back to the idea of aspect

If you created a class called MyPlugin_Plugin, what would you expect its role to be? This question highlights why it’s hard to create a “Plugin” class likeMyPlugin_Plugin. A class like that doesn’t have an obvious job.

This is a problem that we’ve encountered before. That time, we were struggling to define jobs for event listeners built around the plugin API. While the problem is different here, the approach to the solution is the same.

We have to go back to the idea of aspect. An aspect is a way to describe a more abstract type of job. This type of job also tends to touch more than one part of an application. We have to find what aspect of the application MyPlugin_Plugin is in charge of.

What aspect is it in charge of?

In this situation, the term “application” is a bit too wide and misleading. What you want to ask is “What aspect of the plugin is it in charge of?” We alluded to it earlier in the introduction.

We want the MyPlugin_Plugin class to be in charge of the assembly of the plugin. By assembly, we mean assembling our classes like we mentioned earlier. (That’s what we alluded to!) But it’s not limited to that either.

In practice, you need to do more than just assemble classes to assemble a plugin. You might also need to add routes to the router if you’re using a routing system. Or you might need to register event listeners if you’re using an event management system.

In the end, it’s about making sure that your plugin is ready to work once WordPress finishes loading it. To do that, MyPlugin_Plugin needs to touch different parts of the plugin. And that’s why the idea of aspect is useful here.

Dealing with the unknown

The way that we’ll design our plugin class is going to be a bit different from how we’ve done it in other articles. In normal circumstances, we’d be designing and coding everything around the class. But we won’t be doing this today since that would imply that we’d code an entire plugin. (And we’re not there quite yet!)

To deal with these unknowns, we’ll have to make some assumptions around our plugin. Otherwise, it’s a bit hard to design a plugin class when you don’t have a plugin! So let’s take a look at them. So what are our assumptions?

What classes exists already?

Let’s begin by looking at the assumptions made about the classes in our plugin. We spoke about the event management and routing systems earlier. We’ll assume that our plugin uses both of them.

We’ll also assume that our plugin uses a custom post type. And that we’ll need an event subscriber to register it. Our plugin will assemble that class as well.

As you can see, we’re starting to put together a few things that we’ve looked at in previous articles! Now, you don’t have to know how these components work for this article. We’re just looking to assemble them within our plugin class. This is as far as we’ll go this time.

But just in case you missed them earlier, here are the links to the three components used in this article:

Using an autoloader

The second assumption that we’re going to make is that our plugin uses an autoloader. It’s an important tool for organizing your files in an object-oriented plugin. It lets PHP require class files as they’re needed as opposed to having us do it ourselves.

We call that process autoloading. An autoloader is a special class in charge of that. With it, we won’t have to worry about requiring class files for our plugin to work. That’s why we’ll assume that there’s one.

Plugin files and directory structure

Let’s continue on the theme of file organization. We’re going to look the directory structure of our plugin and the assumptions that we have about it. Here’s what we expect within the context of our example.

myplugin/
├── src/
│ └── MyPlugin/
│ ├── EventManagement/
│ │ ├── EventManager.php
│ │ └── SubscriberInterface.php
│ ├── Routing/
│ │ ├── Route.php
│ │ └── Router.php
│ ├── Subscriber/
│ │ └── CustomPostTypeSubscriber.php
│ ├── Autoloader.php
│ └── Plugin.php
└── myplugin.php

Our directory structure follows the standards from PHP-FIG. The src directory acts as the root for all the plugin code. And, except for myplugin.php, each file will only contain a single class.

With that in mind, all the files above are for classes that will show up in our example. The content of the files isn’t important for this article. If you want to know more about the code in them, you can always refer to the articles linked earlier. For now, we’ll just describe the ones that we’ll focus on a bit more for this article.

Plugin.php

We’ve been speaking a lot about the MyPlugin_Plugin class. Plugin.php is the file where the code for the class will reside. It’s the main focus of this article.

Autoloader.php

Autoloader.php is the file that contains the code for MyPlugin_Autoloader. This is the PSR autoloader that our plugin depends on. If you want to see what the code for it, you can refer to this example.

myplugin.php

myplugin.php is the file that WordPress will use to load our plugin. This is where the code to setup our MyPlugin_Autoloader class should be. It’s also where we’ll do the initial call to our MyPlugin_Plugin class. (This is something that we’ll go over later.) The name of the file follows the WordPress plugin development best practices.

Laying the foundation of our plugin class

So that covers the unknowns around our plugin! We’ve also defined what our MyPlugin_Plugin class does. Now, it’s time to start building it!

class MyPlugin_Plugin
{
}

First, we need to lay some foundation. Above is the empty MyPlugin_Plugin class that this article will focus on. Like we mentioned earlier, you’ll find the code for it in Plugin.php in the src/MyPlugin/ directory.

/*
Plugin Name: My plugin
*/

// Setup class autoloader
require_once dirname(__FILE__) . '/src/MyPlugin/Autoloader.php';
MyPlugin_Autoloader::register();

The code above is the initial setup the myplugin.php file at the root of our plugin directory. We start off by adding the plugin file header so that WordPress recognizes the plugin. We only put Plugin Name because it’s the only one that’s mandatory.

Below is the code to setup our plugin autoloader. Because an autoloader can’t autoload itself (that’d be trippy!), we have to do it ourselves by requiring the file. We then call the register static method to register our autoloader with PHP.

Constructor

class MyPlugin_Plugin
{
    /**
     * The plugin event manager.
     *
     * @var MyPlugin_EventManagement_EventManager
     */
    private $event_manager;

    /**
     * The plugin router.
     *
     * @var MyPlugin_Routing_Router
     */
    private $router;

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->router = new MyPlugin_Routing_Router();
    }
}

We’ve already done enough work to make a first pass at the MyPlugin_Plugin constructor. Everything inside the constructor ties to the assumptions that we laid out earlier. It initializes the two systems that we said were part of the plugin.

We mentioned that there was an event management system. MyPlugin_EventManagement_EventManager is the class in charge of that. Meanwhile, MyPlugin_Routing_Router is in charge of the routing system.

Why do we create these objects now? It’s because we only want one instance of each of these systems for our plugin. And what better place to store them than in our MyPlugin_Plugin class!

Why not use singletons?

We’re going to do a small tangent to discuss the use of singletons here. But first, what’s a singleton? A singleton is a type of class that only allows for a single instantiated object of itself to exist at all times.

This need for MyPlugin_Plugin to assemble all these classes puts a lot of pressure on us to use it. After all, we only want one instance of MyPlugin_EventManagement_EventManager and MyPlugin_Routing_Router. So why not just force all these classes to be singletons? Because it’s addicting and goes against the nature of object-oriented programming. (If everything is a singleton, why even use object-oriented programming?)

Now, this doesn’t mean that singletons are evil. They have their uses in object-oriented programming. It’s just not here in the context of WordPress.

A question of trust

It all comes down to trust. You should trust your fellow developers with your code. The reality is that it doesn’t matter if someone creates a second MyPlugin_Plugin object.

Sure, we’ll have a second instance of both the event management and routing systems. But this won’t break anything. And, if it does, that’s ok. It’s not your responsibility to prevent them from trying it. (You should leave them the freedom to experiment.)

Instead, we should focus our energy on designing a meaningful MyPlugin_Plugin class. One that solves the set of problems that we identified earlier. Not this problem of trust which you shouldn’t see as a problem to begin with.

Initializing our plugin class

Alright, with this out of the way, we can proceed with our MyPlugin_Plugin class! We’ve defined a constructor for it, but we still haven’t initialized it anywhere. We can do this in the myplugin.php file by creating a new MyPlugin_Plugin object. You can see how below.

/*
Plugin Name: My plugin
*/

// Setup class autoloader
require_once dirname(__FILE__) . '/src/MyPlugin/Autoloader.php';
MyPlugin_Autoloader::register();

$myplugin = new MyPlugin_Plugin();

Moving on to the design of our plugin class

Now that we’ve laid some good groundwork for our MyPlugin_Plugin class. It’s time to start going over the design of the class. There are some interactions between WordPress and the class that can cause problems. We’ll analyze them and discuss how we solved them in our class.

Loading our plugin

Our first design challenge is how to load our plugin. What does loading mean in this context? Well, it’s not autoloading! We took care of that earlier in myplugin.php.

No, this is something different. We’re coming back to this idea of assembling our plugin. We’re going to focus on what our plugin needs to do during WordPress loading process. What outcome are we looking for when WordPress finishes loading?

Well right now the plugin doesn’t do anything, so we need to get it to work! (Duh!) But what does that imply? It implies that whatever we didn’t assemble in the constructor needs to be at that point. Based on our assumptions, let’s look at what we haven’t assembled yet.

Event subscribers

While we already initialized the MyPlugin_EventManagement_EventManager class, we didn’t assign it any event subscribers. Without these, our plugin can’t interact with WordPress using the plugin API. Since our MyPlugin_EventManagement_EventManager class comes with an add_subscriber method, we can use it to add event subscribers.

class MyPlugin_Plugin
{
    // ...

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->router = new MyPlugin_Routing_Router();

        $this->event_manager->add_subscriber(new MyPlugin_Subscriber_CustomPostTypeSubscriber());
    }
}

Above is our modified constructor. We added a call to the add_subscriber method which registers the MyPlugin_Subscriber_CustomPostTypeSubscriber class. This is an event subscriber in charge of registering the custom post type used by our plugin.

Now, the way we just did this is a bit sloppy. A plugin can have dozens of event subscribers! We should find a cleaner way to add them without adding dozens of add_subscriber calls.

class MyPlugin_Plugin
{
    // ...

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->router = new MyPlugin_Routing_Router();

        foreach ($this->get_subscribers() as $subscriber) {
            $this->event_manager->add_subscriber($subscriber);
        }
    }

    /**
     * Get the plugin event subscribers.
     *
     * @return MyPlugin_EventManagement_SubscriberInterface[]
     */
    private function get_subscribers()
    {
        return array(
            new MyPlugin_Subscriber_CustomPostTypeSubscriber(),
        );
    }
}

Above is a cleaner way to handle this. Instead of dozens of add_subscriber calls, we have one inside a for loop. The loop goes through all the event subscribers returned by the get_subscribers method.

The get_subscribers method is just a way for us to group all our event subscribers. You don’t have to do it this way if you prefer not to. But you should try to keep the constructor clean and that’s one way to do it.

Routes

Next up is the routing system. Like the event management before it, we only initialized one part of the routing system. That’s the MyPlugin_Routing_Router class. We still need to register routes using the add_route method.

class MyPlugin_Plugin
{
    // ...

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->router = new MyPlugin_Routing_Router();

        foreach ($this->get_routes() as $route) {
            $this->router->add_route($route);
        }

        foreach ($this->get_subscribers() as $subscriber) {
            $this->event_manager->add_subscriber($subscriber);
        }
    }

    /**
     * Get the plugin routes.
     *
     * @return MyPlugin_Routing_Route[]
     */
    private function get_routes()
    {
        return array(
            // Our plugin routes
        );
    }

    // ...
}

So we’ve gone ahead and done the same thing that we did with the event subscribers. We have a get_routes method that’ll return all our plugin routes. We then pass them all into the router using add_route.

Making it work with the plugin API

An important component of building plugins is the plugin API. After all, that’s why we have an event management system! And we’ve been putting it to use by connecting event subscribers to WordPress events.

But while we’ve done good work on that front, we haven’t returned the favor to other developers. We haven’t given them a way to connect to us so that they can change the behaviour of our plugin class. This is what we’ll tackle next.

Adding connection points

So where should we add these connection points? The two obvious ones are in our get_routes and get_subscribers method. This would let other developers add their own routes and event subscribers to our plugin.

class MyPlugin_Plugin
{
    // ...

    /**
     * Get the plugin routes.
     *
     * @return MyPlugin_Routing_Route[]
     */
    private function get_routes()
    {
        return $this->event_manager->filter('myplugin_routes', array(
            // Our plugin routes
        ));
    }

    /**
     * Get the plugin event subscribers.
     *
     * @return MyPlugin_EventManagement_SubscriberInterface[]
     */
    private function get_subscribers()
    {
        return $this->event_manager->filter('myplugin_subscriber'myplugin_routes', s', array(
            new MyPlugin_Subscriber_CustomPostTypeSubscriber(),
        ));
    }
}

As you can see, we’ve changed both our get_routes and get_subscribers method. They don’t just return their arrays anymore. Instead, we pass them to our event manager’s filter method. Whatever the filter method returns is what gets sent back to the constructor.

Just in case, here’s a small refresher on the filter method. In the event management system, it’s the method that calls the apply_filters function. This is what will let other developers change our routes and event subscribers.

A problem with our timing

While our get_routes and get_subscribers method work as they should, our work isn’t done here. There’s still a problem that we haven’t addressed. That’s the timing of the call to the filter method.

Right now, it happens at a random time. That’s because we’ve tied it to when WordPress loads our plugin. The issue with that is that WordPress doesn’t load plugins in the same order. It varies from site to site and plugin authors can also change it.

This means that our changes to get_routes and get_subscribers are inconsistent. If someone’s plugin loads before ours, they can change our routes and event subscribers. But if their plugin loads after ours, they can’t. We’ve already called the filter method in our get_routes and get_subscribers methods.

Delaying the loading of our plugin

A solution to this problem is to delay when we’re going to call our timing sensitive methods. Instead of doing it in the constructor, we do it at a later time. That way, we can be sure that every plugin has had a chance to load.

class MyPlugin_Plugin
{
    // ...

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->router = new MyPlugin_Routing_Router();
    }

    /**
     * Loads the plugin into WordPress.
     */
    public function load()
    {
        foreach ($this->get_routes() as $route) {
            $this->router->add_route($route);
        }

        foreach ($this->get_subscribers() as $subscriber) {
            $this->event_manager->add_subscriber($subscriber);
        }
    }

    // ...
}

The first thing to do is move these two methods out of the constructor. That’s the job of the load method shown above. It’s where we’ll put these timing sensitive method calls.

Now that we’ve moved our method calls to the load method, we need to ensure it gets called at the right time. We’ll do this by registering the load method with a WordPress hook. Which one should we use?

The one that makes the most sense is wp_loaded. It’s the last hook that WordPress calls during its loading process. Unlike the plugins_loaded hook, this one also lets themes make changes to our plugin.

/*
Plugin Name: My plugin
*/

// Setup class autoloader
require_once dirname(__FILE__) . '/src/MyPlugin/Autoloader.php';
MyPlugin_Autoloader::register();

$myplugin = new MyPlugin_Plugin();
add_action('wp_loaded', array($myplugin, 'load');

The last thing we need to do is register our load method with the wp_loaded hook. To do this, we need to make a small change to our myplugin.php file (shown above). We added a call to the add_action after we initialized our MyPlugin_Plugin object.

Letting your plugin load only once

As things stand, a developer can call our load method more than once. It’s possible that you’re uncomfortable with that idea. Let’s look how we can prevent our load from running again after it’s run for the first time.

class MyPlugin_Plugin
{
    // ...

    /**
     * Flag to track if the plugin is loaded.
     *
     * @var bool
     */
    private $loaded;

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->loaded = false;
        $this->router = new MyPlugin_Routing_Router();
    }

    /**
     * Loads the plugin into WordPress.
     */
    public function load()
    {
        if ($this->loaded) {
            return;
        }

        foreach ($this->get_routes() as $route) {
            $this->router->add_route($route);
        }

        foreach ($this->get_subscribers() as $subscriber) {
            $this->event_manager->add_subscriber($subscriber);
        }

        $this->loaded = true;
    }

    // ...
}

We added a loaded variable to our MyPlugin_Plugin class. We’re going to set it to false in the MyPlugin_Plugin constructor. This way we ensure that it always starts as false.

We then use the loaded variable in our load method. We gate the beginning of the method by checking if loaded is true. If it is, we exit the method right away.

If it’s still false, we proceed and run the code in the load method. Once it gets to the end, it sets loaded to true. This is what will prevent it from running again if someone calls it again.

Dealing with directory sensitive WordPress functions

We spoke earlier about the directory structure and where we put our plugin files. This decision to organize our plugin this way doesn’t come for free. There’s a repercussion to it that we haven’t discussed yet.

In some cases, WordPress relies on files being in the root directory of their plugin directory. Certain functions like plugin_basename expect our files to be there by default. That’s because they expect you to pass the __FILE__ predefined constant to them.

But we only have one file in the root of our plugin directory. It’s myplugin.php. So how can we use this fact to pass this information to our plugin?

/*
Plugin Name: My plugin
*/

// Setup class autoloader
require_once dirname(__FILE__) . '/src/MyPlugin/Autoloader.php';
MyPlugin_Autoloader::register();

$myplugin = new MyPlugin_Plugin(__FILE__);
add_action('wp_loaded', array($myplugin, 'load');

An easy solution is to pass __FILE__ to our MyPlugin_Plugin constructor. This is what we did in the myplugin.php file above. Doing this gives our plugin the opportunity to use the WordPress functions that need it.

class MyPlugin_Plugin
{
    // ...

    /**
     * The basename of the plugin.
     *
     * @var string
     */
    private $basename;

    /**
     * Constructor.
     *
     * @param string $file
     */
    public function __construct($file)
    {
        $this->basename = plugin_basename($file);
        $this->event_manager = new MyPlugin_EventManagement_EventManager();
        $this->loaded = false;
        $this->router = new MyPlugin_Routing_Router();
    }

    // ...
}

The only thing left is to update the MyPlugin_Plugin constructor. Our constructor now has a file parameter. We pass it to the plugin_basename function to get the base name of our plugin. We then assign that value to the basename variable that we added to the class.

Overall, this is a good solution to the problem. The MyPlugin_Plugin class is the natural location for this information to reside in. After all, it should be the one in charge of knowing about its own basename.

The next phase of our journey

And with this, we’re going to wrap up our look at our MyPlugin_Plugin class! You can find all the complete code for it here. Feel free to refer to it as needed!

Now, from a design perspective, this class wasn’t too complicated. But it’s still possible that you found this a bit overwhelming to understand. That’s ok!

You shouldn’t feel ashamed about feeling this way. We’re trying to combine a lot of moving pieces. It’s normal if your head spins a bit thinking about it. This is where most of the complexity comes from after all.

That said, this is an important moment in our journey. We can’t keep looking at problems in isolation forever. We need to start looking at the big picture. That’s why we started piecing all these different solutions together.

Photo credit: Alden Jewell

Creative Commons License