A common design pattern for WordPress plugins is to have one “main class” that implements the singleton pattern. This pattern is called the “God Class” and it loads all of the other classes the plugin needs as well as instances of those classes in properties of that class. I’ve utilized this anti-pattern, because it accomplishes a job and often is the best option for codebases that support PHP 5.2. But it’s not nearly as efficient as dependency injection to achieve a similar outcome.
The advantage of an implementation of the “God Class” anti-pattern is that you only need one singleton. Also, it’s easy to get those instances of the other classes via the one main class singleton. Here is an example of that pattern:
<?php /* Plugin Name: Example of the God Singleton */ final class Plugin { public $emails; public $settings; public $database; public $api; public $admin; public $front_end; public $assets; protected static $instance; private function __construct(){} public function get_instance(){ if( null === self::$instance ) { self::$instance = new self(); self::$instance->emails = new Plugin_Emails(); self::$instance->settings = new Plugin_Setttings(); self::$instance->database = new Plugin_Database(); self::$insanct->api = new Plugin_Api(); self::$instance->front_end = new Plugin_Front_End(); self::$instance->assets = new Plugin_Assets(); } return self::$instance; } } add_action( 'init', array( 'Plugin', 'get_instance' ) );
This works, I’ve written plugins like this. But, there are much better ways to do things. This pattern came about to avoid using globals singletons, both of which are considered bad practice. And yes, those two vague rules, are, in general right.
And yes, I get that legacy projects have to keep supporting a version of PHP that is almost a decade out of date and provide backward compatibility. But for new plugins or code for custom site development, there is no reason to follow this out of date pattern. It’s inefficient and inflexible.
In this article, I will talk briefly about why it is inefficient and ways dependency injection make it more flexible and more testable. I previously wrote about dependency injection, in the context of database abstractions. This article will take that concept further in terms of structuring plugins.
Please keep in mind all of the example code in this article, from here forward is written for PHP7. If you’re working on a plugin for release, or your server doesn’t support PHP7, all of this will work if you get rid of the return type declarations. I like return type declarations because they make code more self-documenting and improve performance. Using them in this tutorial will help you and your IDE understand the code faster.
Why The WordPress God Singleton Is Bad
In the example I showed above when the plugin is active, so is every single class it might need. That means before continuing, PHP needs to instantiate every one of those classes and store them in memory. Any database queries, API calls, or other logic that runs when they are instantiated happen.
That means if you need two of those classes for the current HTTP request, all of them are loaded. If you need none of them, all of them are loaded. That’s inefficient.
A good thing about this anti-pattern is that because the other classes don’t use singletons we can still create new instances of those classes and pass other values into their constructors. That’s good, except this type of architecture doesn’t really support classes that take different arguments in their constructors. This makes your classes less flexible to the point you might as well have just used a bunch of individual singletons. A class where each instance is identical to any other instance is a good use for the singleton pattern or for a class with all static methods. That’s easier to use and allows for lazy loading.
Lazy Loading FTW
One good thing about singletons is that the class isn’t instantiated until the first time it is used. This is an example of lazy loading. If it is never used, then it is never instantiated and no memory is consumed.
Singletons are one way to implement lazy loading, but they reduce flexibility.
Let’s imagine you were creating a plugin that needed to display recent posts, and recent products — stored in the post type “product”. I would create a “Posts” class that provided a reusable system for building WP_Query objects and getting the posts from the query.
Here is a basic example:
<?php class Posts { /** * @var \WP_Query */ protected $query; public function __construct( array $args ) { $this->query = new \WP_Query( $args ); } public function getQuery() : \WP_Query { return $this->query; } public function getPosts() : array { return $this->query->posts; } }
With this class in place, I could make a class that could return arrays of posts for each of the two post collections I need:
<?php class PostCollections { protected static $recent_posts; protected static $products; public function recent_posts() : array { if( null === static::$recent_posts ){ static::$recent_posts = ( new Posts( [ 'posts_per_page' => 3 ] ) )->getPosts(); } return static::$recent_posts; } public function products() : array { if( null === static::$products ){ static::$products = ( new Posts( [ 'posts_per_page' => 15, 'post_type' => 'product' ] ) )->getPosts(); } return static::$products; } }
This class will lazy load objects of the Posts class. In addition, since it stores the found posts, I can reuse it twice in the same session without extra queries or object instantiation.
That’s pretty good, but it’s not scalable. If I need a third collection, I’m going to have to make another property and method. If I need 10 different post collections, it’s going to get messy. Very quickly this method goes from being a useful abstraction to a copypasta monster. It’s not a system at all really.
Before improving on this, let’s remember what is good about this. It avoids having to use a god singleton that would do all of these queries for every request, whether they were needed or not. These objects and the underlying queries or cache calls are only run when needed.
This class also provides access to collections of posts anywhere in your project. Using a static class like this is one solution I suggested in an article for problems of cross-cutting concerns.
First A Factory
The Posts class I showed above is a good wrapper for WP_Query, but it’s kind of annoying to use. Yes, I want all of the arguments of WP_Query to be useable, but at the same time, I’m most likely going to need to set posts per page, and post type(s). I don’t want to change this class because it’s useful, but a factory for creating objects of this class with more flexible arguments would be really helpful. In addition, that factory might be a good place to add caching later.
A factory is a class that constructs other classes. Here is an example that we can use for creating objects of the posts classes more easily:
<?php class PostsFactory { public static function create( $post_type, int $posts_per_page = 10, int $page = 1, array $args = [] ) : Posts { $args = wp_parse_args( $args, [ 'post_type' => $post_type, 'posts_per_page' => $posts_per_page, 'page' => $page ] ); return new Posts( $args ); } }
Now I have a more obvious and more useful way to create a Posts objects. That’s nice, but it’s going to become especially helpful in the next step.
Moving To Dependency Injection
The scenario we are working with here is a plugin that displays recent posts and products, with flexibility to use different post collections later. There may be classes for using these post collections in widgets, shortcodes, REST API endpoints — really anything. The point is to provide a reusable and efficient way to provide arrays of posts to other classes that can format or store them in some way.
What I am saying in more abstract terms, is we want to make the display layer take the post collection as a dependency. The class for the display will not be responsible for finding its own dependencies, they will be provided to those classes.
Here is a before and after example of using dependency injection for a shortcode:
class recent_posts_shortcode{ /** * @var \WP_Query */ protected $query; public function __construct() { add_shortcode( 'recent_posts', [ $this, 'handler' ] ); } public function handler(){ $this->do_query(); while( $this->query->have_posts() ){ $this->query->the_post(); echo $this->query->post->post_title; } } protected function do_query(){ $this->query = new \WP_Query( [ 'post_type' => 'posts', 'posts_per_page' => 3 ] ); } } add_action( 'init', function(){ $class = new recent_posts_shortcode(); });
In this case, this class is in violation of the single responsibility principle. It adds a shortcode, does a database query and prints content to the screen. If I wanted to do any of those three things, without the other, I couldn’t. I don’t even have a good way of just calling this as a function without the shortcode.
But in our scenario, we have a way to create a post collection, and we want to be able to show that collection in a variety of ways. Here is one way to use both the post collection and the template for the view as dependencies:
<?php class RecentPostShortcode { protected $posts; protected $template_path; const SHORTCODE_NAME = 'recent_posts_shortcode'; public function __construct( array $posts, string $template_path ) { $this->posts = $posts; $this->template_path = $template_path; } public function handler(){ ob_start(); $posts = $this->posts; include $this->template_path; return ob_get_clean(); } } add_action( 'init', function(){ $object = new RecentPostShortcode( PostsFactory::create( 'post', 3 ), '/path/to/template.php' ); add_shortcode( RecentPostShortcode::SHORTCODE_NAME, [ $object, 'handler' ] ); });
This allows us to reuse that template in a different view, such as a widget, or with a different post collection. You could refactor this class into an abstract class for shortcodes or even a shortcode factory.
So even though this class uses dependency injection, it’s got a major shortcoming — the query is run when the shortcode is added. That’s not efficient at all.
We could go back to the lazy loading example I showed before, but I already explained why that isn’t scalable. It’s also not very testable.
Automatic Dependency Injection
In most modern PHP MVC frameworks, you can type hint an argument in a method of your controller and the framework will provide that dependency auto-magically. For example, in a Laravel controller, you can type hint Request and it just automagically provides the current Request object. This is because of Laravel’s dependency injection container.
WordPress isn’t an MVC framework, and I don’t think it is wise to try and force that pattern on WordPress. But creating a limited dependency container in a case like in the scenario that we are discussing is quite useful.
Let me show you how simple that can be using a PHP-DI, and open source project for automatic dependency injection in PHP. First, you will need to install it in your project. Presuming you are using Composer, simply require it and its optional dependency that allows for lazy loading:
composer require php-di/php-di composer require ocramius/proxy-manager
PHP DI is really well documented, but I want to provide an example of how to create the container and use it to lazy load the post collections into shortcodes.
The first thing we need is a simple class to create the dependency container and make it available to the app:
use DI\ContainerBuilder; final class System { /** * @var \DI\Container */ protected $container; /** * @var System */ protected static $instance; protected function __construct() { $this->buildContainer(); } public function getInstance() : System { if( null == static::$instance ){ static::$instance = new static(); } return static::$instance; } public function getContainer() : \DI\Container { return $this->container; } /** * @return \DI\Container */ protected function buildContainer() { $builder = new ContainerBuilder(); $builder->addDefinitions( [ 'RecentPosts' => function ( ContainerBuilder $c ) { return PostsFactory::create( 'post', 3 ); }, 'Products' => function ( ContainerBuilder $c ) { return PostsFactory::create( 'product', 50 ); } ] ); $this->container = $builder->build(); } }
Take a look at the “buildContainer” method. This is where we construct our dependency injection container. We are making two objects, both of which are lazy-loaded using our PostsFactory to provide post collections.
Let’s look at how they would be implemented to make our shortcodes with the ability to define each shortcode in a single line.
class Shortcodes { /** * Shortcode collection * * @var array */ protected $shortcodes; /** * Shortcodes constructor. * * @param array $shortcodes Shortocdes to use in form of 'tag' => 'template_path' */ public function __construct( array $shortcodes ) { $this->shortcodes = $shortcodes; foreach ( $shortcodes as $shortcode => $template ){ if( is_file( $template ) && file_exists( $template ) ){ add_shortcode( $shortcode, [ $this, $shortcode ] ); } } } /** * Dynamically create callback for shortcodes * * @param string $name The shortcode tag * @param array $arguments Not used right now, but could be used for shortcode atts * * @return string */ public function __call( $name, $arguments ) { if( array_key_exists( $name, $this->shortcodes ) ){ return $this->genericHandler( $this->toCamelCase( $name ), $this->shortcodes[ $name ] ); } } /** * Render a shortcode using posts collection and its corresponding post collection * * @param string $collection_name * @param string $template * * @return string */ protected function genericHandler( string $collection_name, string $template ) { $posts = System::getInstance()->getContainer()->get( $collection_name )->getPosts(); ob_start(); include $template; return ob_get_clean(); } /** * Turn shortcode name into camelCased argument * * @param $string * * @return string */ protected function toCamelCase($string) : string { $string = str_replace('-', ' ', $string); $string = str_replace('_', ' ', $string); $string = ucwords(strtolower($string)); $string = str_replace(' ', '', $string); return $string; } }
This would be set up like this:
<?php $shortcodes = [ 'recent_posts' => __DIR__ . 'views/recent-posts.php', 'products' => __DIR__ . 'views/products.php' ]; new Shortcodes( $shortcodes );
As you can see, to add another shortcode, we can add one line to this array, with the tag and template path. That’s it. 3 shortcodes, 100 shortcodes, it’s the same and only actually using the shortcodes cause the Post class to be instantiated and the resulting queries to run. Those objects are reusable throughout the application.
To illustrate this point and show this same system used for dependency injection, let’s create one more implementation of the same container. In this example, we are going to bypass the WordPress theme system in select cases and just return one of our views instead on certain URLs.
<?php add_action( 'plugins_loaded', function(){ add_action( 'parse_request', function( \WP $wp ){ if( isset( $wp->query_vars[ 'name' ] ) ){ switch( $wp->query_vars[ 'name' ] ){ case 'recent-posts' : $route = 'RecentPosts'; break; case 'products' : $route = 'Products'; break; default: $route = null; break; } if( $route ){ status_header( 200 ); $controller = new Controller(); echo $controller->$route( System::getInstance()->getContainer()->get( $route ) ); exit; } } }); }); class Controller { public function recentPosts( Posts $posts ) { $posts = $posts->getPosts(); } public function prodcuts( Posts $posts ) { $posts = $posts->getPosts(); } }
In this example, I am hooking in at parse_request, a very early hook and seeing if any of our routes match and if so, calling a class called Controller to create the view. That class is more of a stub to show the dependency injection, but I will let you work with it to create a view.
If I was doing this, I would probably use FasteRoute for anything more complicated, but my simple router shows the concept. Also, I would be very suspicious of trying to make WordPress into an MVC framework, even though this isn’t a bad start.
Flexibility and Efficiency Achieved
I hope this article has helped you get a better understanding of dependency injection, as well as helped you understand the pitfalls and benefits of using it. Like all concepts of object-oriented programming (OOP) you shouldn’t use them because they are established principles. You should use them because they solve a problem, and you currently have that problem.
More importantly, I hope I’ve helped you think about code architecture in a way that will help you design code that needs minimal work. If you need another post collection in this system, you add a few lines of code to the container, but you don’t rewrite what it uses underneath. If you want to expand this container to do more than work with posts, you’ll need a bit more new code, but you shouldn’t have to make major changes to existing code.
That is what good OOP is really about — maintainable systems. Maintainable systems can be added to without any major refactoring and that’s what this provides.
1 Comment