Rethinking Object-Oriented WordPress Plugins

Following common practice in WordPress plugin development, you create a class for you plugin, instantiate that class with a

$my_plugin = new My_Plugin();

in your main plugin file, and declare all your actions/filters in your class’s __construct() function, using something like:

add_filter('init', array($this, 'my_init_function'));

And then you go on to add your plugin’s JavaScript and CSS, register post types and taxonomies, look for query variables, etc.

I’m declaring that to be “wrong”, and I’m not going to do it anymore. Why? Because it’s not good objects-oriented design, and it seems to me that if you’re going to use objects, you should follow good object-oriented design patterns.

What problems come from the current practice?

  1. Lack of coherency. The class is basically being used as a plugin namespace. It’s laudable to try to avoid polluting the global namespace with all your function names, but using a consistent, unique prefix for your functions accomplishes the same thing. Instead you end up with data and methods that look like they’re related to each other, but really have the most tenuous connection of being part of the same plugin.
  2. Poorly implemented singletons. Every plugin class really needs to be a singleton. Having two instance of the class would cause a range or problems, from inefficiency (e.g., running the same query multiple times) to possible conflicts as the first instance of the plugin filters some data that is then re-filtered by the second instance. I’m not saying that singletons are inherently bad, but they are definitely overused (Wikipedia says so!, and, to quote the “Bible”, “Be suspicious of classes of which there is only one instance”), and when they are used, they should be used deliberately and with proper care taken to avoid multiple instances.
  3. Barriers to code reuse and abstraction. Classes created this way can’t really be sub-classed (although I’ve yet to see a final keyword in front of a class), partially because of the singleton problem mentioned above, partially because there’s no clear definition of what the class is, so it doesn’t make sense to make a more specific version of it. This gets in the way of creating abstractions to share common code among similar classes, and greatly increases the complexity of any plugin of reasonable size.

What do we do about it?

I’ll be the first to admit that I don’t know all the answers. I’m playing with some solutions right now that seem to be working well, and I welcome any suggestions for improving on these ideas.

  1. Distinguish between class members and instance members. PHP includes the static keyword, to let you declare a property or method to be a member of the class itself, rather then an instance of the class. This is the proper place for your configuration settings, registering post types, etc. Instead of instantiating the class to tell WordPress about this information, create a static init() function to call in your base plugin file. E.g., My_Class::init().
  2. Use different classes for different things. Does your plugin create two separate post types? Create a class to declare each of them. Throw in some instance methods for interacting with your custom post meta.

Example

class My_Custom_Post_Type {
  const POST_TYPE_NAME = 'my_post_type';
  const IMPORTANT_META = 'my_important_meta';
  private $post_id;
 
  public static function init() {
    add_action('init', array(get_class(), 'register_post_type'));
  }
  public static function register_post_type() {
    $my_post_type_args = array(
      // some args go here
    );
    register_post_type(self::POST_TYPE_NAME, $my_post_type_args);
  }
  public function __construct( $post_id ) {
    $this->post_id = $post_id;
  }
  public function get_important_meta_field() {
    return get_post_meta($this->post_id, self::IMPORTANT_META, TRUE);
  }
}

This lets you abstract away the implementation (e.g., is it a custom post type or a custom table?) and just work with your new object’s well-defined interface. You can start doing things like creating sub-classes, using polymorphism, setting up factory objects to give you different types of objects depending on taxonomy terms, and all sorts of other concepts based on object-oriented design patterns. Basically, it lets us grow up and use the amazing advances in computer science and the discipline of coding that we’ve seen over the last several decades.

I would love to get some feedback on this. It’s a nascent idea that could use some better minds to help form it.

9 Responses to Rethinking Object-Oriented WordPress Plugins

  1. Nick says:

    I think you’re missing the point. The reason I use class encapsulation is not for the benefits that a pure object oriented approach would give you (because that paradigm just doesn’t fit in with the rest of the WP ecosystem) but as a pseudo-namespace. It prevents function naming conflicts without having to prefix every action or filter callback with a stupid string and helps to keep things organizing.

    Anyone who says they’re doing object oriented development is WordPress is probably lying.

  2. I understand the idea of namespacing, but you can still achieve that using static class methods, while still leaving the option of creating sub-classes and instantiating the class to achieve models that do fit within an object-oriented paradigm.

  3. spammenot says:

    The OO paradigm is used to overcome the naming conflicts but the problem number 2 should be the main focus of the article in my opinion. I think the use of classes and one static initialization can be a very good solution.

    You can do object oriented development is WordPress if you don’t write only one big class with all your code in just to create a namespace for your functions.

  4. Kevin Perrine says:

    I like this approach as it cleans up the code nicely, but I’m a little confused on how to pass in the post_id when you instantiate the object. Where should the object be created so that it can see the global $post variable and access the current ID?

    • That would depend entirely on what you’re doing. E.g., if you’re wanting a template tag, you would still probably want to set that up as a function in the global namespace, but it could then create an instance of your object, passing it the global $post or ID.

  5. Really nice post. Plenty of plugins are using a purely OOP approach – Shopp comes to mind as one that does it quite extensively.

    Like Kevin I am struggling with ways to provide necessary WP objects to my classes but have found some success with passing Objects as variables as needed.

  6. Theo Ephraim says:

    I agree that doing it exactly the way you mention is bad practice, but I disagree about not using a class as a namespace. When you use a class-as-namespace approach, you don’t actually have to instantiate the class. Rather you move the add_action calls either outside of the class or put them all in another static function to be called at the top of the file. You can then declare all functions static if you feel the need to, or you can just be happy knowing they are only called in a static way. I think this is much cleaner than prefixing everything and having everything live on the global scope.

    The issue of then using OOP within your plugin is totally separate. Sure people should use more OOP to clean things up, and of course if they have two post types and want to use a class to represent them and pass them around, they should write two classes. But I think including the WordPress hook calls as static functions within those classes actually just makes things messier and more confusing than they need to be.

    My solution is to keep all the core plugin code (activation, action hooks, etc) in one namespacing class and to use other classes as regular classes within your plugin if you feel so inclined.


    MyPlugin :: registerHooksAndActions();

    class MyPlugin { // this is the namespace class. everything is used statically. declare them static if you want
    function registerHooksAndActions(){
    register_activation_hook( __FILE__ , array( "MyPlugin", "on_activate" ) );
    add_action( "admin_init" , array( "MyPlugin", "hook_admin_init" ) );
    ...
    }
    function on_activate(){
    //activation hook
    }
    function hook_admin_init(){
    //do stuff
    }
    ...
    }

    class MyPluginSpecificObject { //this is the regular old OOP class. Use it to keep things clean
    public function __construct( $post_id, $var2, ... ) { }
    }

  7. Ian Dunn says:

    I’ve been thinking about a lot of the same issues lately.

    Like nick said, most people who create plugins w/ OOP are really just doing “object oriented functions”, rather than implementing true OOP techniques like modularity and inheritance.

    I wrote a post on my blog about
    the problems I’ve encountered trying to use proper OO techniques in a WordPress plugin.

    I’ve also got a plugin skeleton on Github that represents my best effort so far, but I think it’s far from perfect. I’m considering converting it to use singletons for all of the classes, because some of the do have a bit of stateful data, but they would still mostly use static methods.