All about OOPS-WP, a small programming library for WordPress.

This past Monday, I once again had the esteemed honor to participate in our monthly 5 for the Future day at WebDevStudios, a time when our entire team gets to shift our focus away from client work to instead work on open-source, personal, or internal projects about which we’re passionate.

My most recent personal project has been a new WordPress plugin called Into the Void, which I eventually intend to use an alternative to Twitter. Into the Void registers a private post type and a set of REST API routes for posting and receiving shouts. Not satisfied with the idea of using WordPress as my main method of content entry, my goal for Into the Void is to develop a companion iOS app so I can post microupdates to this site from my phone. Of course, that means I need to learn Swift development, and as part of that effort, two weeks ago I signed up for a subscription to raywenderlich.com and have been working through the tutorial series there. If you’re interested in app development, I highly recommend this resource – check it out!

Ultimately, on Monday I decided that my little iOS microblogging + WordPress platform wasn’t in the true spirt of 5 for the Future, so I instead spent time documenting and releasing version 0.2.0 of one of my other passion projects that I mentioned in my last blog post (and the basis for some of my WordCamp presentation submissions this year), OOPS-WP.

What is OOPS-WP?

As the GitHub description so succinctly states, OOPS-WP is “a collection of abstract classes, interfaces, and traits to promote object-oriented programming practices in WordPress.” I started developing this library awhile back when I realized I was setting up a brand new Hookable interface for every project I worked on, because I’d established a particular approach to scaffolding my projects, and I wanted my IDE to understand that the objects my plugin was using had a register_hooks method. Over time, I noticed other design patterns that I followed from project to project, and figured it’d be useful to not only create a collection of abstract classes I could pull into a project to speed up my own workflows, but figured others might get some utility out of it as well.

The first release of OOPS-WP came with a handful of interfaces – the aforementioned Hookable, as well as Registerable, and Runnable – and a couple of abstract classes I found useful: ServiceRegistrar, Service, PostType, and EditorBlock, the last of which was a result of my Block Study exploration in January as I continue to wrestle with learning how to create Gutenberg blocks in WordPress.

In release 0.2.0, I added new structural types for Plugin (itself just an extension of the ServiceRegistrar class, in an effort to address some feedback that the terminology might be difficult to grok for beginners to object-oriented programming), Taxonomy (which now extends the same abstract ContentType that PostType does), and Shortcode. Because shortcodes are always rendered in WordPress, I also created a new Renderable interface, to ensure that a render method is always available on an object.

Having this library available from project to project has greatly streamlined the amount of scaffolding I’ve needed to do to get a new plugin up and running, and I’m looking forward to adding new object types in the future.

How do I use it?

The real beauty of OOPS-WP is that nobody has to use it. That said, the concepts behind the library are not much different from other WordPress object constructs you might already use. If you’ve ever created a new API route in WordPress, you’ve likely created your own class that extends WP_REST_Controller; if you’ve created a widget, then you’ve extended WP_Widget.

What WordPress lacks right now due to its support of PHP 5.2 (which is coincidentally going away once WordPress 5.2 is released later this month) are some language features that have been introduced since PHP 5.2 was first released over 12 years ago. That, mercifully, is finally changing this month, so we can maybe start to have nice things. Can you believe we’re almost celebrating the 10th anniversary since namespaces were introduced to PHP, but not a single file in WordPress core yet has one? Perhaps now as a community we can finally gain some traction toward establishing some SOLID principles in the codebase and make the application as rock-solid behind the scenes as it is nice to look at.

SOLID is what I’m hoping to achieve with OOPS-WP. I strive to create single-purpose classes with clearly-defined structures, to use dependency injection, and to type-hint against an interface so that when my application needs change in the future, I can do so with as minimal effort as possible. Like all of us, I’m still learning every day about what that means in my own work, but my aim with this library is to help make it so we can all learn together in this brave new world of still-outdated PHP. :)

For real, how do I use it?

Okay, okay, I digress…

A Hookable Example

Let’s say you want to get your feet wet with OOPS-WP. You want to create a new class that contains some code for a plugin or theme. That plugin or theme is going to have to register some hooks, right? Let’s make it happen!

<?php
namespace JMichaelWard\MyAmazingPlugin;

use WebDevStudios\OopsWP\Utility\Hookable;

/**
* Class to power my amazing new plugin!
*
* @author Jeremy Ward
*/
class MyAmazingPlugin implements Hookable {
    /**
    * All the hooks my object sets up, right in one place!
    */
    public function register_hooks() {
        // Put your hooks here!
        add_action( 'init', [ $this, 'do_some_callback' ] );
    }

    /**
    * My init callback.
    */
    public function do_some_callback() {
        // Some thing you want to do on init.
    }
}

Then, when you go to create that object, you know you can call the register_hooks method because the MyAmazingPlugin class indicates “you signed a contract that says you have to have one!”.

<?php
/**
* Plugin Name: My Amazing Plugin
* Author: Jeremy Ward
*/

use JMichaelWard\MyAmazingPlugin\MyAmazingPlugin;

$plugin = new MyAmazingPlugin();
$plugin->register_hooks();

That’s simple enough, but perhaps there may be some things you need to do on the object before the hooks are called. Object __construct methods should only be used for object initialization, otherwise writing unit tests can be difficult. But also, you want to keep your plugin file clean and only call the methods that you need to get the whole process started. What if we added in the Runnable interface?

A Runnable Example

<?php
namespace JMichaelWard\MyAmazingPlugin;

use WebDevStudios\OopsWP\Utility\Hookable;
use WebDevStudios\OopsWP\Utility\Runnable;

/**
* Class to power my amazing new plugin!
*
* @author Jeremy Ward
*/
class MyAmazingPlugin implements Hookable, Runnable {
    /**
    * Run my plugin.
    */
    public function run() {
        // Your custom logic could go here before you register hooks.
        $this->register_hooks();
    }

    /**
    * All the hooks my object sets up, right in one place!
    */
    public function register_hooks() {
        // Put your hooks here!
        add_action( 'init', [ $this, 'do_some_callback' ] );
    }

    /**
    * My init callback.
    */
    public function do_some_callback() {
        // Some thing you want to do on init.
    }
}

The plugin file only has to change a little:

<?php
/**
* Plugin Name: My Amazing Plugin
* Author: Jeremy Ward
*/

use JMichaelWard\MyAmazingPlugin\MyAmazingPlugin;

$plugin = new MyAmazingPlugin();
$plugin->run();

Or we can do it one better and wrap that in a hook of itself, so that the plugin only runs after all the plugins are loaded:

<?php
/**
* Plugin Name: My Amazing Plugin
* Author: Jeremy Ward
*/

use JMichaelWard\MyAmazingPlugin\MyAmazingPlugin;

add_action( 'plugins_loaded', [ new MyAmazingPlugin(), 'run' ] );

The Service class b/w what’s a ‘registrar’?

Now that you’ve got a main plugin class, chances are you don’t want to put all of the plugin logic in that one file, because you know quite well that it breaks the S in SOLID – single responsibility principle – or in other words, a class should do only one thing and do it well. You also write a lot of plugins and themes and don’t want to have to remember to write that full set of implements keywords every time you make a new plugin. What you need is some kind of way to differentiate the responsibilities of your plugin – creating a standalone part of the plugin that other parts of it might use. You might think of each of these features as a component or Service, and the object that creates those Service objects is the ServiceRegistrar.

Good news! The ServiceRegistrar class already implements the Runnable method, and the Service class implements both Hookable and Runnable. So, if you wanted to set up those different features, your plugin class might now look like this:

<?php
namespace JMichaelWard\MyAmazingPlugin;

use WebDevStudios\OopsWP\Structure\ServiceRegistrar;

/**
* Class to power my amazing new plugin!
*
* @author Jeremy Ward
*/
class MyAmazingPlugin extends ServiceRegistrar {
    /**
    * My plugin services.
    *
    * @var array
    */
    protected $services = [
        ContentRegistrar::class,
        ApiRegistrar::class,
        Logger::class,
    ];
}

That’s it?!?

That’s it. You’ve now split your plugin into three distinct services – one to handle setting up your custom post types and taxonomies, one to handle setting up your custom REST API routes, and one to handle error logs.

A Service is just a part of your plugin or theme that’s responsible for something. Again, the S in SOLID.

A Registrar is just a fancy way of saying “something that registers something”.

With that new-found knowledge, what I can tell you now is that the Plugin class in OOPS-WP is just a ServiceRegistrar. But it’s set up with its own PluginInterface because we might decide upon some methods that we’ll need every single time in the future. So let’s make that change to the plugin:

<?php
namespace JMichaelWard\MyAmazingPlugin;

use WebDevStudios\OopsWP\Structure\Plugin\Plugin;

/**
* Class to power my amazing new plugin!
*
* @author Jeremy Ward
*/
class MyAmazingPlugin extends Plugin {

Now let’s take a look at an example ContentRegistrar service:

<?php
namespace JMichaelWard\MyAmazingPlugin;

use WebDevStudios\OopsWP\Structure\Service;

/**
* Class to set up my custom post types.
*
* @author Jeremy Ward
*/
class ContentRegistrar extends Service {
    /**
    * My post types
    */
    private $post_types = [
        BoardGames::class,
        Records::class,
    ];

    /**
    * Register hooks with WordPress.
    */
    public function register_hooks() {
        add_action( 'init', [ $this, 'register_post_types' ] );
    }

    /**
    * Register my post types.
    */
    public function register_post_types() {
        foreach ( $this->post_types as $post_type ) {
            $post_type->register();
        }
    }
}

There’s a bit more going on in the above Service, but it’s important to know three things:

  1. The Service class implements both Hookable and Runnable. The run method is defined in the class, and all it does is call the register_hooks method. It’s up to you as a developer to add that method to your class and define the hooks that you need.
  2. The register_post_types method is not part of the Service. It’s unique to this ContentRegistrar. You can add as much logic as you need to a Service – all it cares about is that you tell it what hooks you have, and that it has a run method that can be called by other objects.
  3. The post types that get registered by the register_post_types method all have a register method. That is by design – their classes all extend the PostType abstract class provided by OOPS-WP, and that PostType class implements the Registerable interface, which forces the PostType object to define that method.

Man.

I know. If you don’t write object-oriented coded on a day-to-day basis, it’s a lot to take in. It might seem like you’re creating a lot of files, too, but note that these are pretty rudimentary examples. The power of OOPS-WP is that you can apply any of the interfaces that the library provides to any of your objects. If you have a class that has a bunch of WordPress actions and filters in it, have it implement Hookable and then put them all in that function so future developers can easily glance at what your class does. If it needs to be triggered by another object to start off a process, implement Runnable so that you know it’ll have a run method.

The joy of using interfaces in WordPress is that we can define what classes need to look like in order to function properly. The joy of defining abstract classes is that we can re-use code to keep ourselves from having to write the same methods over and over again. OOPS-WP is not strongly opinionated; rather, it provides abstract blueprints for some common WordPress structures so you can get all the dumb stuff out of the way and get to writing your next amazing plugin or theme.

If this is your first time hearing about OOPS-WP, I hope you’ll install it on your next project and give it a try. If you’re familiar with it but just aren’t quite sure how it’s supposed to help you write better code, hopefully this article will help. If you love OOPS-WP and want to continue using it down the line on all your future projects, I hope you now have a better understanding about why some of the design decisions were made, and know that I hope to never negatively impact the code you extend from it.

Mostly, I hope we all learn something from this journey together into a new future for WordPress. I’m glad we can have nice things in our open-source plugins and themes, and not just custom client projects, and I’m looking forward to seeing where the next few years will take us.

Onward!