Single responsibility principle, WordPress and You

For WordPress developers, it can be hard to improve your PHP skills. You lack the resources or tutorials to drive home these concepts. This happened just a few weeks ago when Nathaniel asked for help with his Stack Overflow question.

He was looking for help applying the single responsibility principle with WordPress. I sent him a bunch of replies but closed off saying I needed to write a post about it. This article is a detailed explanation of my thoughts following that conversation.

As WordPress developer, you might have started working with or looking into object-oriented programming. Maybe you even heard of something called SOLID.

As a whole, SOLID can be an intimidating topic. But the single responsibility principle is just one part of it (it’s the ‘S’ in SOLID). That makes it a more manageable topic to discuss and help you with.

At the end of this article, you should have a better understanding of it. You can also use the provided code samples in your own plugins and themes.

Single Responsibility Principle explained

Before we get started, let’s go over a few things so that everyone is on the same page.

What is it?

The single responsibility principle can be explained in the following way. The class you created should do one thing and one thing only. It should also have all the information it needs internally to do what it needs to do. This means that you should not be using globals.

Why is it so hard to do with WordPress?

At its creation, WordPress wasn’t designed with objects in mind. Developers built WordPress by giving it simple APIs that were easy to use. These were great for new PHP developers. Maybe that’s how you got into PHP and even programming. That said, they can be confusing when you’re trying to apply object-oriented programming concepts. This is especially true for the single responsibility principle.

What WordPress tells you to do

Have you already started playing with object-oriented programming? If so, you might have done a plugin where all the code was in a class. It’s pretty common for developers to do that.

That’s because it makes it easy for you to namespace/prefix your code and prevent errors. It’s even recommended in the official WordPress documentation. But building your plugin this way is problematic. It leaves you with an enormous class containing all your functions.

Doing things this way doesn’t offer you any advantages over just using a function prefix. It also doesn’t follow the single responsibility principle. Your class has no purpose because it does everything.

Let’s apply the Single Responsibility Principle

Now that we covered some initial bases, let’s get into the meat of this. Let’s help you use the single responsibility principle to build a WordPress plugin. I am a big meme fan so we will creating a small WP Meme Shortcode plugin as an example.

You can find the complete code on GitHub.

Dividing your classes by responsibility

So to create this plugin, you need to think about the tasks your plugin needs to do. In this case, you don’t have to worry about too much. For this, we will divide the tasks into four parts.

An entry point

First of all, you need a class that acts as the entry point to your plugin. It’s the starting point for WordPress when it loads your plugin. It should handle the registration of all the components. It’s also in charge of fetching the information the plugin needs to function.

It’s important to point out that the job of the entry point is not to add all the hooks and filters for the entire plugin! This goes beyond the responsibility of this class. How to handle hooks and filters is important enough that it will get its own section further in the article.

I usually name this class Plugin. This might seem like a bizarre naming decision to you. But there’s a valid reason! The idea is that, if WordPress had to only be aware of one class, plugin would be the logical name to use in that context.

For now, our WPMemeShortcode_Plugin class is pretty much empty. We put a placeholder static method called load. It’ll load the plugin into WordPress for us.

Managing options

You want a single class to have the responsibility of retrieving and saving options. This removes the need for the other parts of your plugin to handle the storage of their options. All they need to do is interact with your class to get the option they need.

When you only have a few options, creating a class for options might seem like overkill. So, if you had to fold that responsibility into another class, it should be the Plugin class. You should still practice splitting it off into a separate class even for small plugins.

We designed our WPMemeShortcode_Options class around a specific idea. It’s that we’ll store all our options in a single array. All our methods interact with it.

An admin page

By using the settings API, it is quite straight forward to create a class to handle your admin page(s). The class would handle the registration of all the settings as well as the rendering.

If you have several admin pages, it would be appropriate for you to split each page into its own class. You could also use inheritance and put the code common to all admin pages in its own parent class.

The shortcode

In the same way as your admin page, your shortcode class will be interacting with the WordPress shortcode API. It will handle registering the shortcode and generating the output.

Handling hooks and filters

Whether you’re working on a WordPress theme or plugin, you have to use hooks and filters. It’s fundamental to WordPress itself. Now, if you’re trying to move forward in your quest for object-oriented supremacy, this is where you might be struggling. What do I do with hooks and filters when I use classes to contain my code?

What you normally see

The two common ways people deal with hooks and filters are by:

  1. Putting all the hooks and filters in the constructor.
  2. Creating a new object. Using that object and registering all your hooks and filters outside the class.

Neither options is that great. Creating an object shouldn’t add the hooks and filters by default. There could be situations (like for testing) where you want to create the object without adding the hooks. By having code outside the class for hooks and filters, you’re robbing the class of a key part of its responsibility.

Using static methods

The better way to solve this issue is by using a static method as a custom constructor. The function creates an instance of your class and uses it to register the appropriate hooks and filters. This allows you to separate the registration your hooks and filters from the construction of your object. I normally call the function ‘register’ or ‘load’.

Beyond hooks and filters, static methods are a great way to isolate your code from WordPress. They allow you to handle a lot of WordPress functionality beforehand. This also goes back to the idea that your class should have all the information it needs internally. It should not rely on outside information to work.

To be clear, you should use WordPress functions if they make sense inside your class. WordPress has tons of useful helper functions that you should make use off. That said, functions like ‘get_option’ don’t help your class do its job. That is why they are good candidates for extraction into a static method.

Why is this useful to you

So this is quite a lot to digest. Maybe you have a headache at this point? You might even be wondering if it is worth the trouble. So If you got this far, I will leave you with a few reasons to care about the single responsibility principle.

Your code is clearer

You ever look at your code (or worse someone else’s) and wonder:

  • Where do I put this function?
  • Where did I put this function!?
  • Oh God! What was I thinking!

You know you have! That’s why, if each class has only one job, it makes your code easier to understand and read for you and others. In most cases, you don’t have to wonder where you put a function or where you should put it. That decision is almost made for you.

Your code is stronger

By having your classes do only one thing, you’re making all your code more robust. For example, let’s say that every class is in charge of getting its own options. You decide that you have to many individual options, you want to use an array.

You would then have to track down all the code in your plugin and make the changes there. Each individual change could create a bug. Having a single class in charge of options means that you only need to make that change in one location.

Your code is easier to test

Unit testing is not something that a lot of WordPress developers do (yet!). That said, when your classes specialize in one thing, they are easy to test and mock. This makes testing a lot less painful if you ever decide to do it. Using a static method as a constructor to isolate some of the WordPress functionality also helps with this.

  • In this version you’re loading options and instantiating the objects regardless of whether they will be needed. Laziness can improve this. What do you think about registering static methods?

    • Absolutely! There is a lot of additional logic that could be added. I wanted to keep the example simple and understandable. This meant I had to leave out a lot of that stuff.

      What do you mean by registering static methods?

      • Instances make sense when a class needs to have more than one instance. You don’t need an instance of a class to add its methods to actions and filters. Static methods work just as well.

        Options shouldn’t be loaded unless they are needed. There’s also no need to instantiate the options class. The other classes can call its static methods directly. This has the same effect as passing object references around to constructors, but without so much unnecessary code.

        I’ve sketched this in

        • Ah I see what you mean. Using static functions everywhere isn’t any different from using a singleton pattern. A lot of people (myself included) see it as an anti-pattern. By using it, you create a weakness in the rest of your code.

          For example, now that all your functions are static, I can’t test my classes in isolation. You created a very strong coupling between your classes and the options class. That coupling is hard to break and test against.

          I agree that the classes themselves don’t require more than one instance, but using statics everywhere isn’t the solution. The example was meant to be good for a small plugin, but you will have some scaling issues for larger projects. I plan on addressing some of them in the future.

          For example, I did not address the problem of class overload that Nathaniel mentioned on stack overflow. This example had 4 classes so I side-stepped the issue.

          Loading everything up and removing boilerplate code is also necessary as things scale upward. This simply means the task of loading everything should be deferred to a separate class in charge of only that. It could also make sure that only one class is created.

          For the options themselves, lazy loading is a great performance goal. I use it a lot, but in this case it doesn’t serve any purpose since WordPress is autoloading the option (unless specified otherwise). The performance gain would be minimal unless your array ended up being huge.

          • I apologize for sidetracking. I enjoyed your discussion of Single Responsibility. Your post appeared while I was having a separate conversation about the massive amount of time wasted by plugins that use certain patterns blindly, copying examples without understanding, perhaps hoping to achieve “good code smell”. It’s like someone looked at an airplane cockpit and concluded that all canoes need altimeters. Especially common is initializing objects and data structures that tend to be thrown away unused.

            My objections spring from the fear that people will use these examples to create even more wasteful code: they will misconstrue the lightweight constructors and fill them with heavy code and then instantiate these classes on every page load because they believe they need $this for add_action. This is why I suggested registering static methods. However, there are other ways to prevent waste without diluting your message as my example did.

            By the way, did you notice the strong smell when you typed name=”wp_meme_shortcode[size]”? The setter in your options class doesn’t even work; some obscure core code catches the form submission and sanitizes and sets the option. How can you test this? WordPress has lured you away from your principle of loose coupling in favor of convenience, marginal safety, and maintaining compatibility. Until the great rewrite in the sky, WordPress will always force us to compromise between good software principles and getting things done.

          • Loving the discussion! I actually agree with what you said completely. Copy pasting is an issue in all languages. It is easy to copy code when blog posts, stack overflow questions and everything else being so accessible. Copy pasting without understanding is always going to be the lazy way out.

            My goal is really to teach these concepts and offer the best code possible for them to be understood. There is always going to be some weak spots because you just can’t cover everything at once without making it overwhelming.

            Your example is actually great for that. I was super annoyed that I hardcoded the name and id for that form. I decided that ultimately it was minor and would require a lot more code to be handled in a correct way. Forms and handling output to HTML are both large topics by themselves.

  • Martin Sotirov

    I love that you’re properly injecting your dependencies in your constructors. I rarely see WordPress examples from actual PHP professionals. You’ve earned your blog a new fan!

    • Glad you liked it Martin! There’s a ton of other stuff under the “library ” section 🙂

  • Hi Carl – First I saw this was older but then someone else commented 3 weeks ago. Question: Might you finish the WPMemeShortcode_Plugin class for this example. I presume that’s where the include()s would be. And I was hoping to see how you’d do it vs how I might do it (no as correct). Thanks.

    • I’ve added a hypothetical “index.php” in the gist. It doesn’t assume that you have an autoloader. That’s why all the classes are loaded by hand. That said, it should give you a better idea. 😀

  • Jull Weber

    Hi, @carlalexander:disqus Love all your tutorials! Keep them coming. I’m using your code and I can get everything to work except the options class. When I render the value of the settings input fiends, it doesn’t return the value saved, just the default one. Guessing it is not being set to the options array. I don’t see anywhere in your code where the set method is used. Do we have to hard code the settings in the load method? Where would we use the set method? Thanks!