Learning the singleton design pattern for WordPress pluginsFebruary 3, 2017
When I started developing plugins for WordPress I basically knew these three things:
- The main plugin file will be executed by WordPress
- Doing something in WordPress normally means hooking into an action or filter.
- Using actions and filters requires you to define functions for them to call
So I did what I imagine everyone does when they write their first plugin:
- Create such a main plugin file
- Define functions that do what the plugin is supposed to do
- For each function, make an add_action or add_filter call.
It worked and I was happy. But it turns out that there are a couple of fundamental problems with this approach. The most obvious one is that as soon as your plugins get interesting, your code gets messy because it has zero structure to it. Another one is compatibility issues with other plugins and themes because your code is not encapsulated, but runs inside the global scope of WordPress. That means all variables, functions and classes defined within your plugin are shared with other plugins and themes which is definitely not a sensible thing to do. Uniquely prefixing functions per plugin is the easiest fix to this particular problem, but still does not help structuring your code at all. The more sophisticated approach is object oriented programming, which means using classes. If you look at the main file of any plugin, very often it will either define and/or instantiate a class. Then only this global class needs to be uniquely named and everything else is programmatically encapsulated. But there are still a few variants to implement this. One of them is the singleton pattern.
The concept of a singleton
In object oriented programming, code is organized in objects that interact with each other. Sometimes these object represent entities like a post or a page in WordPress. But they can also be more abstract and just house a bunch of functions under a common purpose. Each object is of a certain type, called a class. The class acts like a blueprint for all objects of this type and can be instantiated many times. And it usually makes sense to have multiple instances, just like with posts or pages in WordPress. But sometimes only one instance is needed and creating multiple objects of the same class will even break some parts of the code. One might argue that a class is the wrong tool for this job, but a singleton implements exactly this using a class. A singleton is a class of which only one instance at a time can exist in memory. This pattern is not restricted to WordPress or even PHP. There are many pros and cons to be considered, but I want to make my own experiences before forming an opinion about it.
Seeing the singleton in action: Easy Digital Downloads
Easy Digital Downloads is an established WordPress plugin for e-commerce. Its creator, Pippin Williamson is co-host of the podcast that introduced me to this topic 1. He advocates the use of singletons in WordPress plugins and uses EDD as an example, so I decided to use it as an example to learn how the pattern works. I read through their main plugin file and tried to extract the singleton pattern from it. I put the result into a GitHub repository. There are a few interesting parts to this that make it work:
- The instance is a private static attribute of the class. That means it is stored on the class and therefore the instance is also nested inside the classes scope.
- The instance method that is used to retrieve the object checks whether it already exists. If it does not exist yet it will create a new instance using the private constructor. This mechanism guarantees that at most one instance exists at a time.
- The PHP keyword self references the current class and combined with the scope operator2 allows access to static variables and methods of the current class.
To be honest, to me it feels like a hack. But I think there are two ways to look at this. From an idealistic point of view, this pattern seems to deny basic principles of object oriented programming and use the tools in inappropriate way. From a more pragmatic perspective, it is simply a solution to an existing problem. I do not really have a perspective on this yet, but I will play around with it. I guess the best way is to implement it in a few projects and experience advantages and disadvantages along the way.