Whenever object-oriented PHP is discussed in terms of WordPress development the subject of singletons often comes up. In fact, I’ve covered them twice here on Torque. In my article on design patterns and then with Carl Alexander in my article on leveling up as a PHP developer.
The singleton pattern is used in the design of a class to make it so only one single instance of that class will ever exist. Some people believe that singletons are bad or are an anti-pattern. I disagree. While I am suspicious of singletons and think they are way over-used in WordPress development this is not a problem with the singleton pattern, which is quite useful, rather it is a sign of a misuse of the singleton.
Singletons That Prevent Singletons
Many WordPress plugins have a main class that serves to bootstrap the plugin itself, which often involves instantiating other classes. Often times this class implements the singleton pattern. As a result, that instance of the class is the only instance.
A perfect example of this can be found in Easy Digital Downloads. If you read the main file of that plugin, you will see that it defines a class called “Easy_Digital_Downloads” that implements the singleton pattern and loads a lot of other classes, storing those instances in class properties.
This makes sense because for many of these classes such as those used for adding hooks or setting up the plugin, should only be done once. That said, the core plugin or add-on plugins might need to access the same instance of the classes that the main plugin class uses.
That problem could be solved by using the singleton pattern in every single one of those classes. But that would dramatically reduce the usefulness of each of those classes. That would be, objectively speaking, bad design.
If I need to use the class EDD_Emails, I can make my own instance of that class:
$emails = new EDD_Emails();
I can also access the instance of it that was created when the plugin was bootstrapped via the main plugin class like this:
$emails = Easy_Digital_Downloads::instance()->emails;
Which choice is better depends on what the goal of the add-on is. But because of this flexible architecture, the best option can be chosen.
Singletons are useful, but they reduce flexibility and therefore should be used sparingly. EDD is a great example. There is one main singleton that houses all the other “main” instances, of the other classes, without having to use a singleton on every one of those other classes.
What Problem Is The Singleton Solving?
Many times a plugin’s main class that implements the singleton pattern is responsible for loading other classes and for adding hooks. Whenever we have to use the word “and” in describing a class’s purpose we likely have a class that violates the single responsibility principle.
One challenge working with the WordPress plugins API, or hooks, is that when we hook to a class method, that hook can only be removed with the same instance of the same class. One way developers ensure their hooks can be removed is using a class that uses the singleton pattern to add the hooks.
For example, compare these two classes:
<?php class Shawn { protected static $instance; public static function get_instance(){ if( null == self::$instance ){ self::$instance = new self(); } return self::$instance; } protected function __construct(){ add_action( 'wp_header', array( $this, 'hi_shawn' ) ); } public function hi_shawn(){} } class Shawn2 { public function __construct(){ add_action( 'wp_header', array( $this, 'hi_shawn' ) ); } public function hi_shawn(){} }
The first one adds a hook that can be easily removed later, the second does not.
This works, but it can lead to forcing the singleton pattern on a class that does not otherwise need it. One of the major advantages of object-oriented programming is code reuse and singletons reduce the ability to reuse a class. Once a class uses a singleton it can’t be instantiated again to serve the same purpose.
Let’s consider why the first of the two examples above makes it so much easier to remove that hook then the other one. Using a singleton makes the class instance globally available. I can always use this to remove the hook:
<?php remove_action( 'wp_header', array( Shawn::get_instance(), 'hi_shawn' ) );
But, taking a look at design can make this solution unnecessary. I don’t need to be able to access the class instance for the class with that hook everywhere in my application. Only in the place I actually intend to use it. This is one reason we use dependency injection in classes.
Dependency injection is a very useful design principle to follow. It is a pattern where a class is provided with everything it needs to perform its task. I wrote about dependency injection recently in regards to creating database abstractions.
That “everything it needs” may include the object of a previously instantiated class that it might use to remove a hook. When we don’t use dependency injection, the problem of needing another object is often solved by using Singletons or global variables. It’s not a problem when we inject that instance into the class.
Be Skeptical And Be Stingy
Singletons have plenty of good uses. Just be careful not to misuse them. Don’t use it with a class with multiple responsibilities. If a class has many purposes and one of them requires a singleton, forcing that pattern on the other responsibilities is a bad idea. This can be solved by extracting these responsibilities into multiple, single-purpose classes.
I hope that this article has helped you understand why the singleton is useful but why it should generally be avoided. Remember good code architecture is about making it easier for you and other developers to work with your code in the future. The singleton’s purpose is to reduce flexibility, don’t make that decision lightly.
1 Comment