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.
Originally posted at x + 3

Jonathan Brinley