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

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.

namespace WPMemeShortcode;

class Plugin
{
    /**
     * Loads the plugin into WordPress.
     */
    public static function load()
    {
        // ...
    }
}

For now, our 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.

namespace WPMemeShortcode;

class Options
{
    /**
     * @var array
     */
    private $options;

    /**
     * Constructor.
     *
     * @param array $options
     */
    public function __construct(array $options = array())
    {
        $this->options = $options;
    }

    /**
     * Gets the option for the given name. Returns the default value if the
     * value does not exist.
     *
     * @param string $name
     * @param mixed  $default
     *
     * @return mixed
     */
    public function get($name, $default = null)
    {
        if (!$this->has($name)) {
            return $default;
        }

        return $this->options[$name];
    }

    /**
     * Checks if the option exists or not.
     *
     * @param string $name
     *
     * @return bool
     */
    public function has($name)
    {
        return isset($this->options[$name]);
    }

    /**
     * Sets an option. Overwrites the existing option if the name is already in use.
     *
     * @param string $name
     * @param mixed  $value
     */
    public function set($name, $value)
    {
        $this->options[$name] = $value;
    }
}

We designed our 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.

namespace WPMemeShortcode;

class AdminPage
{
    /**
     * @var Options
     */
    private $options;

    /**
     * Constructor.
     *
     * @param Options $options
     */
    public function __construct(Options $options)
    {
        $this->options = $options;
    }

    /**
     * Adds the admin page to the menu.
     */
    public function addAdminPage()
    {
        add_options_page(__('WordPress Meme Shortcode', 'wp_meme_shortcode'), __('Meme Shortcode', 'wp_meme_shortcode'), 'install_plugins', 'wp_meme_shortcode', array($this, 'render'));
    }

    /**
     * Configure the option page using the settings API.
     */
    public function configure()
    {
        // Register settings
        register_setting('wp_meme_shortcode', 'wp_meme_shortcode');

        // General Section
        add_settings_section('wp_meme_shortcode_general', __('General', 'wp_meme_shortcode'), array($this, 'renderGeneralSection'), 'wp_meme_shortcode');
        add_settings_field('wp_meme_shortcode_size', __('Default Image Size', 'wp_meme_shortcode'), array($this, 'renderSizeField'), 'wp_meme_shortcode', 'wp_meme_shortcode_general');
    }

    /**
     * Renders the admin page using the Settings API.
     */
    public function render()
    {
        ?>
        <div class="wrap" id="wp-meme-shortcode-admin">
            <h2><?php _e('WordPress Meme Shortcode', 'wp_meme_shortcode'); ?></h2>
            <form action="options.php" method="POST">
                <?php settings_fields('wp_meme_shortcode'); ?>
                <?php do_settings_sections('wp_meme_shortcode'); ?>
                <?php submit_button(); ?>
            </form>
        </div>
        <?php
    }

    /**
     * Renders the general section.
     */
    public function renderGeneralSection()
    {
        ?>
        <p><?php _e('Configure WordPress Meme Shortcode.', 'wp_meme_shortcode'); ?></p>
        <?php
    }

    /**
     * Renders the size field.
     */
    public function renderSizeField()
    {
        ?>
        <input id="wp_meme_shortcode_size" name="wp_meme_shortcode[size]" type="number" value="<?php echo $this->options->get('size', '500'); ?>" />
        <?php
    }
}

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.

namespace WPMemeShortcode;

class Shortcode
{
    /**
     * @var Options
     */
    private $options;

    /**
     * Constructor.
     *
     * @param Options $options
     */
    public function __construct(Options $options)
    {
        $this->options = $options;
    }

    /**
     * Handles the output of the shortcode.
     *
     * @param array  $attributes
     * @param string $content
     */
    public function handle(array $attributes, $content = null)
    {
        // Do nothing if no ID is given or it is not numeric
        if (!isset($attributes['id']) || !is_numeric($attributes['id'])) {
            return $content;
        }

        // If no size is given or it is not a numeric value, get default.
        if (!isset($attributes['size']) || !is_numeric($attributes['size'])) {
            $attributes['size'] = $this->options->get('size', '500');
        }

        return "<img src=\"http://cdn.memegenerator.net/instances/{$attributes['size']}x/{$attributes['id']}.jpg\" />";
    }
}

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’.

namespace WPMemeShortcode;

class AdminPage
{
    /**
     * Register the admin page class with all the appropriate WordPress hooks.
     *
     * @param Options $options
     */
    public static function register(Options $options)
    {
        $page = new self($options);

        add_action('admin_init', array($page, 'configure'));
        add_action('admin_menu', array($page, 'addAdminPage'));
    }

    // ...

}

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.

namespace WPMemeShortcode;

class Options
{
    /**
     * Load the plugin options from WordPress.
     *
     * @return Options
     */
    public static function load()
    {
        $options = get_option('wp_meme_shortcode', array());

        return new self($options);
    }

    // ...

}

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.

Creative Commons License