WP + You + OOP

A lot of the programming associated with WordPress is inherently not object-oriented. Most of the theme mods or plugins that developers write for their blog(s) are one-off functions here or there, or a small set of filters and actions with associated callback functions. As WordPress matures into an “application framework” (everyone else’s words, not mine), the need for better code organization, greater maintainability, and the self-documenting powers of Object Oriented Programming become immediately apparent.

Because a majority of WordPress sites aren’t complex, the audience for discussions like this are small. Probably 99% of WordPress installs don’t need extra PHP code for them to work how their site owner(s) want. A majority of sites don’t use Multisite. A majority of sites have no need for Web Services, and when they do: they just install some Twitter widget or the like that does the heavy lifting for them. But I don’t think anyone involved with WordPress core wants that to be the future of WordPress. When people talk about the future of WordPress, they talk about how it can run any web application, but there aren’t a large number of compelling examples of WP doing that yet.

Almost by accident, I think eMusic has become a great example of how to not only run WordPress at scale, but how to write a site using WordPress as an application framework, and I have many examples of how we organize our code that can help anyone else who is struggling to make sense of a pile of code that an entire team needs to decipher and maintain.

Before we dig into how to write better OO code, we need to first figure out how we are going to organize our codebase.

Some Setups Tips

  • Run WordPress as an svn:external: This should almost be mandatory. You want your directory structure to look like so:
    /index.php
    /wp-config.php
    /wordpress
    /wp-content
    /wp-content/themes
    /wp-content/plugins
    /wp-content/mu-plugins
    /wp-content/requests
    /wp-content/site-configs
    /wp-content/sunrise.php
    
    // in the root of your site
    svn propedit svn:externals .
    
    // add this code
    wordpress http://core.svn.wordpress.org/branches/3.4/

    This is important so that you never overwrite core, and so you can’t check-in whatever hacks you have added while debugging core code.

    Because we are using an external, you need to add these lines to wp-config.php:

    define( 'WP_CONTENT_URL', 'http://' . DOMAIN_CURRENT_SITE . '/wp-content' );
    define( 'WP_CONTENT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/wp-content' );

    You also need to alter index.php to look like this:

    define( 'WP_USE_THEMES', true );
    
    /** Loads the WordPress Environment and Template */
    require( './wordpress/wp-blog-header.php' );
  • Use Sunrise:
    If you are using Multisite, Sunrise is the best super-early place to hook in and alter WordPress. In wp-config.php:

    define( 'SUNRISE', 1 );

    You then need to a file in wp-content called sunrise.php.

  • Use Site Configs:
    One of the main things we use sunrise.php for is site-specific configuration code. I made a folder in wp-content called site-configs that houses files like global.php (all sites), emusic.php (site #1), emusic-bbpress.php (site #2), etc
  • Separate HTTP Requests:
    I made a folder in wp-config called requests that houses site and page-specific HTTP logic. Because a big portion of our main site is dynamic and populates its data from Web Services, it makes sense to organize all of that logic in one place.
  • Use “Must Use” plugins:
    If you have classes or code that are mandatory for your application, you can autoload them by simply placing each file in your wp-content/mu-plugins folder. If your plugin requires a bunch of extra files: it is not a good candidate for mu-plugins.

Use classes to encapsulate plugins and theme configs

MOST plugins in the WordPress plugin repository are written using procedural code – meaning, a bunch of function and global variables (whadup, Akismet!). Hopefully you know enough about programming to know that is a bad idea. 1) Global variables suck and are easily over-writable and 2) PHP will throw a fatal error if you try to overload a function (declare a function with the same name twice).

Because you have to protect your function names against this, most procedural plugin authors namespace their functions by prepending an identifier to their function names:

function my_unique_plugin_name_woo_hoo( ) {
    return 'whatever';
}
// call me maybe
my_unique_plugin_name_woo_hoo( );

If you declare a bunch of function callbacks for actions and filters in your plugin, you can see how this would be gross:

function my_unique_plugin_name_alter_the_content() { ... }
add_filter( 'the_content', 'my_unique_plugin_name_alter_the_content' );

function my_unique_plugin_name_red() { ... }
add_filter( 'the_content', 'my_unique_plugin_name_red' );

function my_unique_plugin_name_green() { ... }
add_filter( 'the_content', 'my_unique_plugin_name_green' );

function my_unique_plugin_name_blue() { ... }
add_filter( 'the_content', 'my_unique_plugin_name_blue' );

If we instead use an OO approach, we can add all of our functions to a class as methods, ditch the namespacing, and group our filters and actions together into one “init” method.

class MyUniquePlugin {
    function init() {
        add_filter( 'the_content', array( $this, 'alter_the_content' ) );
        add_filter( 'the_content', array( $this, 'red' ) );
        add_filter( 'the_content', array( $this, 'green' ) );
        add_filter( 'the_content', array( $this, 'blue' ) );
    }

    function alter_the_content() { ... }
    function red() { ... }
    function green() { ... }
    function blue() { ... }
}

How we call this class is up for debate, and I will discuss this at length later. But for right now, let’s call it like this:

$my_unique_plugin = new MyUniquePlugin;
$my_unique_plugin->init();

The init method is used like a constructor, but it ditches any ambiguity between __construct() and class MyPlugin { function MyPlugin() {} }. Could you use __construct() in any of my examples throughout instead of init() now that we are all in PHP5 syntax heaven? Probably. However, we don’t really want to use either, because we don’t want to give anyone the impression that our plugin classes can be called at will. In almost every situation, plugin classes should only be called once, and this rule should be enforced in code. I’ll show you how.

The Singleton Pattern

The Singleton Pattern is one of the GoF (Gang of Four) Patterns. This particular pattern provides a method for limiting the number of instances of an object to just one.

class MySingletonClass {
    private static $instance;
    private function __construct() {}
    public static function get_instance() {
        if ( !isset( self::$instance ) )
            self::$instance = new MySingletonClass();

        return self::$instance;
    }
}

MySingletonClass::get_instance();

Why do we care about limiting the number of instances to one? Think about a class that encapsulates code used to connect to a database. If the database connection is made in the constructor, we should share that connection across all instances of the class, we shouldn’t try to open a connection every time we need to make a SQL query.

For WordPress plugin classes, we want to store all of our actions and filters in a constructor or a class’s init() method. We don’t want to register those filters and actions more than once. We also don’t need or want multiple instances of our plugin class. This makes a plugin class a perfect candidate to implement the Singleton pattern.

class MyUniquePlugin {
    private static $instance;
    private function __construct() {}
    public static function get_instance() {
        if ( !isset( self::$instance ) ) {
            $c = __CLASS__;
            self::$instance = new $c();
        }

        return self::$instance;
    }

    function init() {
        add_filter( 'the_content', array( $this, 'alter_the_content' ) );
        add_filter( 'the_content', array( $this, 'red' ) );
        add_filter( 'the_content', array( $this, 'green' ) );
        add_filter( 'the_content', array( $this, 'blue' ) );
    }

    function alter_the_content() { ... }
    function red() { ... }
    function green() { ... }
    function blue() { ... }
}

MyUniquePlugin::get_instance();

// or, store the value of the class invocation
// to call public methods later
$my_plugin = MyUniquePlugin::get_instance();

Ok, so great, we implemented Singleton and limited our plugin to only one instance. We want to do this for all of our plugins, but it would be great if there was a way to not repeat code in every plugin, namely all of the class members / methods needed to implement Singleton:

    private static $instance;
    private function __construct() {}
    public static function get_instance() {
        if ( !isset( self::$instance ) ) {
            $c = __CLASS__;
            self::$instance = new $c();
        }

        return self::$instance;
    }

To do so, we are going to need a base or intermediate class that can be extended. Here is a base class, but we have a few problems:

class BaseSingleton {
    // this won't work, since $instance will get overwritten
    // every time BaseSingleton is instantiated by a sub-class
    private static $instance;

    // this won't work, because the child class
    // needs to be able to call parent::__construct,
    // meaning the parent constructor has to be as visible
    // as the child - the child has to have >= visibility
    private function __construct() {}

    public static function get_instance() {
        if ( !isset( self::$instance ) ) {
            // this won't work, because __CLASS__ refers
            // to BaseSingleton,
            // not the class extending it at runtime
            $c = __CLASS__;
            self::$instance = new $c();
        }

        return self::$instance;
    }
}

Let’s try to fix is:

class BaseSingleton {
    // store __CLASS__ = (instance of class) as key => value pairs
    private static $instance = array();

    // let the extending class call the constructor
    protected function __construct() {}

    public static function get_instance( $c = '' ) {
        if ( empty( $c ) ) 
            die( 'Class name is required' );
        if ( !isset( self::$instance[$c] ) )
            self::$instance[$c] = new $c();

        return self::$instance[$c];
    }
}

We’re getting closer, but we have some work to do. We are going to use OO features of PHP and some new stuff in PHP 5.3 to make a base class that implements Singleton and works the way we want (we don’t want to do this hack: ClassName::get_instance( 'ClassName' ) ).

Abstract Classes

Abstract classes are kinda like Interfaces:

Classes defined as abstract may not be instantiated, and any class that contains at least one abstract method must also be abstract. Methods defined as abstract simply declare the method’s signature – they cannot define the implementation.

When inheriting from an abstract class, all methods marked abstract in the parent’s class declaration must be defined by the child; additionally, these methods must be defined with the same (or a less restricted) visibility.

Here’s an example:

abstract class BaseClass {
    protected function __construct() {}
    abstract public function init();
}

As we can see, BaseClass does nothing except provide a blueprint for how to write our extending class. Let’s alter it by adding our Singleton code:

abstract class BasePlugin {
    private static $instance = array();
    protected function __construct() {}

    public static function get_instance( $c = '' ) {
        if ( empty( $c ) ) 
            die( 'Class name is required' );
        if ( !isset( self::$instance[$c] ) )
            self::$instance[$c] = new $c();

        return self::$instance[$c];
    }

    abstract public function init(); 
}

Our base class now has the following properties:

  • Declared abstract, cannot be called directly
  • Encapsulates Singleton code
  • Stores class instances in key => value pairs (we’re not done with this)
  • Instructs the extending / child class to define an init() method
  • Hides the constructor, can only be called by itself or a child class

Here’s an example of a plugin extending BasePlugin:

class MyPlugin extends BasePlugin {
    protected function __construct() {
        // our parent class might
        // contain shared code in its constructor
        parent::__construct();
    }

    public function init() {
        // implemented, but does nothing
    }
}
// create the lone instance
MyPlugin::get_instance( 'MyPlugin' );

// store the instance in a variable to be retrieved later:
$my_plugin = MyPlugin::get_instance( 'MyPlugin' );

Here’s is what is happening:

  • MyPlugin is extending BasePlugin, inheriting all of its qualities
  • MyPlugin implements the required abstract function init()
  • MyPlugin cannot be instantiated with the new keyword, the constructor is protected
  • MyPlugin is instantiated with a static method, but because an instance is created, $this can be used throughout its methods

We’re almost done, but we want to call get_instance() without our hack (passing the class name).

Late Static Binding in PHP 5.3

get_called_class() is a function in PHP 5.3 that will give us the name of the child class that is calling a parent class’s method at runtime. The class name will not be resolved using the class where the method is defined but it will rather be computed using runtime information. It is also called a “static binding” as it can be used for (but is not limited to) static method calls.

Here’s an easy example to explain how this works:

class A {
    public static function name() {
        echo get_called_class();
    }
}

class B extends A {}

Class C extends B {}

C::name(); // outputs "C"

get_called_class() is new to PHP 5.3. WordPress only requires PHP 5.2, so you might need to upgrade to be able to implement this class like so:

abstract class BasePlugin {
    private static $instance = array();
    protected function __construct() {}
    public static function get_instance() {
        $c = get_called_class();
        if ( !isset( self::$instance[$c] ) ) {
            self::$instance[$c] = new $c();
            self::$instance[$c]->init();
        }

        return self::$instance[$c];
    }

    abstract public function init();
}

Now we can instantiate our plugin like so:

// create the lone instance
MyPlugin::get_instance();

// store the instance in a variable to be retrieved later:
$my_plugin = MyPlugin::get_instance();

Base Classes for Plugins and Themes

Just like it says: USE BASE CLASSES for plugins AND themes. If you aren’t using Multisite, you probably don’t have the problem of maintaining code across parent and child themes that might be shared for multiple sites. Each theme has a functions.php file, and that file, in my opinion, should encapsulate your theme config code (actions and filters, etc) in a class. Parent and Child themes are similar to base and sub classes.

Because we don’t want to repeat ourselves, code that would be copied / pasted into another theme to inherit functionality should instead be placed in a BaseTheme class. Our theme config classes should also implement Singleton.

One of the pieces of functionality we need to share across themes at eMusic is regionalization. Regionalization is accomplished by using a custom taxonomy “region” and some custom actions and filters. For a theme to be regionalized, it needs to override some class members and call BaseTheme::regionalize().

Here’s part of our BaseTheme class:

abstract class BaseTheme implements Singleton {
    var $regions_map;
    var $regions_tax_map;
    var $post_types = array( 'post' );

    private static $instance = array();

    public static function get_instance() {
        $c = get_called_class();
        if ( !isset( self::$instance[$c] ) ) {
            self::$instance[$c] = new $c();
            self::$instance[$c]->init();
        }

        return self::$instance[$c];
    }

    protected function __construct() {}
    abstract protected function init();

    protected function regionalize() {
	add_filter( 'manage_posts_columns', array( $this, 'manage_columns' ) );
	add_action( 'manage_posts_custom_column', array( $this, 'manage_custom_column' ), 10, 2 );
	add_filter( 'posts_clauses', array( $this, 'clauses' ), 10, 2 );
	add_filter( 'manage_edit-post_sortable_columns',array( $this, 'sortables' ) );
	add_filter( 'pre_get_posts', array( $this, 'pre_posts' ) );
    }

    ...
}

Here is a theme extending it in its functions.php file:

if ( ! isset( $content_width ) )
    $content_width = 448;

class Theme_17Dots extends BaseTheme {
    function __construct() {
        global $dots_regions_tax_map, $dots_regions_map;

        $this->regions_map = $dots_regions_map;
        $this->regions_tax_map = $dots_regions_tax_map;

        parent::__construct();
    }

    function init() {
        $this->regionalize();

        add_action( 'init', array( $this, 'register' ) );
        add_action( 'after_setup_theme', array( $this, 'setup' ) );
        add_action( 'add_meta_boxes_post', array( $this, 'boxes' ) );
        add_action( 'save_post', array( $this, 'save' ), 10, 2 );
        add_filter( 'embed_oembed_html', '_feature_youtube_add_wmode' );
    }

    ....
}

“Must Use” Plugins

Let’s assume we stored our BaseTheme and BasePlugin classes in files called class-base-theme.php and class-base-plugin.php. The next question is: where should these  files go? Probably the best place for them to go is wp-content/mu-plugins. The WordPress bootstrap routine will load files in that directory automatically. Because our BaseTheme class is not immediately invoked within the file, the loading of the file just makes the class available for our theme child class in functions.php to extend.

Sunrise

sunrise.php can be viewed as a functions.php for your whole network. Aside from letting you switch the context of $current_blog and $current_site, you can also start adding actions and filters.

In the eMusic sunrise.php, I take this opportunity to load “site configs,” mainly to filter which plugins are going to load for which site, and which plugins will load for the entire network of sites (plugins that load for every site). To accomplish this, we need a “global” config and then a config for each site like so:

require_once( 'site-configs/global.php' );

if ( get_current_blog_id() > 1 ) {
    switch ( get_current_blog_id() ) {
    case 2:
        require_once( 'site-configs/bbpress.php' );
	break;

    case 3:
        require_once( 'site-configs/dots.php' );
	break;

     case 5:
        require_once( 'site-configs/support.php' );
	break;
    }

    add_filter( 'pre_option_template', function () {
	return 'dark';
    } );
} else {
    require_once( 'site-configs/emusic.php' );
}

Site Configs

In our global site config, we want to filter our active network plugins. As I have said in previous posts and presentations, relying on active plugins being correct in the database is dangerous and hard to keep in sync across environments.

We can filter active network plugins by filtering the active_sitewide_plugins site option:

add_filter( 'pre_site_option_active_sitewide_plugins', function () {
    return array(
        'batcache/batcache.php'                         => 1,
        'akismet/akismet.php'                           => 1,
        'avatar/avatar.php'                             => 1,
        'bundle/bundle.php'                             => 1,
        'cloud/cloud.php'                               => 1,
        'download/download.php'                         => 1,
        'emusic-notifications/emusic-notifications.php' => 1,
        'emusic-ratings/emusic-ratings.php'             => 1,
        'emusic-xml-rpc/emusic-xml-rpc.php'             => 1,
        'johnny-cache/johnny-cache.php'                 => 1,
        'like-buttons/like-buttons.php'                 => 1,
        'members/members.php'                           => 1,
        'minify/minify.php'                             => 1,
        'movies/movies.php'                             => 1,
        'shuffle/shuffle.php'                           => 1,
        'apc-admin/apc-admin.php'                       => 1,
        //'debug-bar/debug-bar.php'                     => 1
    );
} );

Filtering plugins in this manner allows to do 2 main things:

  • We can turn plugins on and off by commenting / uncommenting
  • We can visually see what our base set of plugins is for our network without going to the admin or the database

We have a lot of other things in site-configs/global.php, but I mainly want to demonstrate how we can load classes in an organized manner. In a site-specific config, we will filter what plugins are active for that site only.

For the main eMusic theme, we use these plugins:

add_filter( 'pre_option_active_plugins', function () {
    return array(
        'artist-images/artist-images.php',
        'catalog-comments/catalog-comments.php',
        'emusic-post-types/emusic-post-types.php',
        'discography.php',
        'emusic-radio/emusic-radio.php',
        'gravityforms/gravityforms.php',
        'super-ghetto/super-ghetto.php'
        //,'theme-check/theme-check.php'
    );
} );

This is just another example of us not repeating ourselves. When we think about everything in an object-oriented manner, everything can be cleaner and more organized.

HTTP Requests

Another area where we need to organize our code is around HTTP requests. We have “pages” in WordPress that aren’t populated via content from the database, but from a combination of Web Service calls based on request variables.

We use the following files:

// base class
/wp-content/mu-plugins/request.php

// extended by theme classes implementing the Factory pattern
// "dark" is a theme
/wp-content/requests/dark.php

// the theme class, where applicable,
// loads a request class when required by a certain page
/wp-content/requests/home.php
/wp-content/requests/album.php
/wp-content/requests/artist.php
... etc ...

As an example, an album page loads AlbumRequest:

class AlbumRequest extends RequestMap { ... }
class RequestMap extends API { ... }

// the API class implements CURL
class API {}

// CURL implements the cURL PHP extension, curl_multi(), etc
class CURL {}

Our organization of HTTP files is example of OO principles in action, and includes ways to organize our codebase in a way that is not specified by WordPress out of the box.

This is also an example of the self-documenting nature of object-oriented programming. Rather than show you each line of AlbumRequest, we can assume that it deals with requests specific to the Album page, inheriting functionality from RequestMap, which calls methods available in API, which at its core is implementing cURL.

Conclusion

Hopefully after reading all of this, you can clearly see some of the benefits of organizing your code in an object-oriented manner. The basics of OOP are outside the scope of this post but are essential for all developers to learn, especially those who work on teams whose members have varying degrees of skill.

Organized and readable code is essential, and the patterns available to us from the history of computer programming so far should be used to help get it there.

29 thoughts on “WP + You + OOP

  1. Ive been using “WordPress as am application” as well, my project site is http://bmxraceevents.com/ its all one plugin.

    Te code structure is very MVC’ish with many rails influences.

    Interesting that you suggest singletons, and you do have a point with your reasoning.

  2. Pingback: Read: WP + You + OOP | Kevin Dees

  3. How do you deal with dependencies when everything subclasses a primary singleton abstract or uses a common interface?

    We don’t have access to sunrise or mu-plugins; one thing I’ve tried is using a “loader” that includes & inits the actual plugin class in a hook (say, after_setup_theme or init) and the class everything depends on gets included at the top of functions.php, but communicating that pattern is hard.

    Another possibility was to include_once ‘abstract.php’ at the top of every plugin, but that’s ugly and creates a lot of overhead.

    That’s when I started looking at autoloaders, which then felt like I was just going too far.

    • Load ALL of your classes wherever you can, with each callable class having a line at the bottom like so:

      add_action( ‘init’, array( ‘PluginName’, ‘get_instance’ ) );

      or

      add_action( ‘init’, function () {
      my_global()->plugin_name = PluginName::get_instance();
      } );

      • I’m still failry green with OOP, but I understand that I can really get what I want out of WP if I can set this up right. Thanks for the article. Trying to start up a theme…

        I have this in /inc/doghaus.php…
        abstract class DogHaus {
        public $post_types = array(‘post’);
        private static $iinstance = array();
        public static function get_instance() {
        $c = get_called_class();
        if( !isset( self::instance[$c] ) ) {
        self::instance[$c] = new $c;
        self::instance[$c]->init();
        }
        return self::instance[$c];
        }
        protected function __construct(){}
        abstract protected function init();
        }

        then in functions.php…
        require_once( DOGHAUS_INC . ‘/doghaus.php’ );

        global $content_width;
        if( !isset( $content_width ) ) $content_width = 1024;

        class DogHaus_Theme extends DogHaus {
        function __construct() {
        parent::__construct();
        }
        function init() {
        add_action( ‘after_setup_theme’, array( &$this, ‘setup’ ) );
        }
        public function setup(){
        add_editor_style();
        add_theme_support(‘automatic-feed-links’);
        add_theme_support(‘post-thumbnails’);;
        /* …etc,,, */
        }
        public function sing_banana_boat(){
        echo ‘DAAYYY-O!’;
        }
        }

        1. I see that I am supposed to instantiate each class with add_action(…, but I am foggy on the syntax for this example you gave:

        add_action( ‘init’, function () {
        my_global()->plugin_name = PluginName::get_instance();
        } );

        No comprende(!)

        I did this instead and got my index.php doing a great Belafonte imitation:
        add_action( ‘init’, function(){
        global $doghaus;
        $doghaus = DogHaus_Theme::get_instance();
        });

      • You want to call: add_action( ‘init’, array( ‘DogHaus_Theme’, ‘get_instance’ ) ). It looks like you spelled instance with 2 “i”s = $iinstance, that might be a problem.

      • HI. Thanks for replying. Yeah. I forgot to correct the typos in my post, but I got it working. Since I’m developing a theme and not a plugin, I can’t call my class with add_action( ‘init //…etc… ) becuase then my theme init() won’t do the stuff it’s supposed to do before WP init (‘after_setup_theme // etc… ), so I did this instead:

        (in functions.php)
        $doghaus = DogHaus_Theme::get_Instance();

        And that works OK. I suppose each of my classes will need to do that.

      • I can’t create the instance with ‘init’ because then I’ll miss the ‘after_setup_theme’ hook. I am also currently unable to use $this or self:: within any of my classes.

      • Ive tried calling get_instance a few different ways, but I am never able to access $this inside my class init() (or anywhere else except __set() and __get(), so putting something like “add_action( ‘init’, array( $this, ‘add_shortcodes’ )” kills everything. $this seems to be off-limits for calling class methods within the class (ditto self::). You do it in your theme example, so am I instantiating it incorrectly? Everything seems to work fine until I try to use $this
        Here’s the last thing I tried:
        add_action( ‘after_setup_theme’, function(){
        $doghaus = DogHaus_Theme::get_instance();
        });

      • Yeah. I’m not seeing the point of all this if you’re not allowed to use $this or self:: reliably. I set values of properties and get fatal errors when trying to use them in init(). There’s not much help for this except for how to create a singleton — not much about what you can actually do with them… looks like I’m going back to $doghaus = new DogHaus_Theme with the rest of the losers. 🙁

      • I was having the problem when extending my main theme class. I don’t know why, but I rewrote the child class and it works now. I’m looking at my old code and I still don’t see why it didn’t work, but whatever. Yay!. Thanks for your help.

    • You could always use http://www.php.net/manual/en/function.spl-autoload.php I ended up writing a custom one (and probably should of just used the spl approach).

      Personally I hate having to do require onces and all that all of the place, so they’re just done in that loop, note thats a snippet of my bootstrap.php

      here’s my gist https://gist.github.com/3099863

      It loads “everything”, based on the presence of the “controller” file, take the following example:

      controllers/events_controller.php
      models/events.php
      assets/stylesheets/events.css
      assets/javascripts/events.js

      That being said, take anyone of these
      bmxraceevents.com/tracks
      bmxraceevents.com/map
      bmxraceevents.com/faq
      bmxraceevents.com/login
      etc.

      Those all follow the same approach regarding loading, files are automatically created for me, I edit them and then they are loaded, they’re also pushed into the global js/css array and complied using closure and yui prior to going to the production site.

  4. This is a pretty awesome writeup!

    However, I’m confused by your example of the class that implements cURL requests. Surely you’re familiar with WP’s fantastic HTTP API that inteligently chooses cURL or some other transport available on your server.

    • I use curl_multi, which WordPress does not implement, because it’s not available in other transport mechanisms. I have spoken to Nacin about this, and there’s a Trac ticket for it somewhere, but it has yet to pick up any steam. I may post a patch soon that implements WP_Batch_Http as a pluggable class. 80% of the content on our site comes from Web Services, and making requests serially was not an option.

  5. Pingback: WordPress as an Application Framework | Marek's Blog

  6. Thanks for sharing this, there are so many articles about writing OOP plugins that are really ignorant of true OOP principles. It’s nice to see someone who really knows what they’re doing apply OOP in a WordPress context.

    I’ve been struggling with a lot of questions around this issue, but your post helped answer some of them for me. I’m going to follow your example and convert my static classes to singletons.

    Your article only focused on the bare class definitions, though, so that still leaves a lot of questions about how you interact with the WP API and implement domain logic in a OOP fashion. For instance, how would you go about creating a custom post type?

    • I will guess: extend the base theme class, which already has a public $post_type = array(‘post’); set up in (grandparent) singleton class.

      class MyCustomPostType extends BaseTheme {

      function __construct() {
      $this->post_type[] = ‘my_custom_post_type’;
      parent::__construct();
      }

      function init(){
      /* set up custom post type */
      }

      }
      $my_custom_post_type = MyCustomPostType::get_instance();

      Am I close?

  7. Pingback: Tutorials and Resources for Object Oriented WordPress Plugin Development : WPMayor

  8. Brilliant! One of the better OOP + WP articles I’ve found. Thank you!!

    Helpful, if not a bit over my head, at least for the moment 🙂 The pseudo-code examples are great. But, speaking for myself, I would find a working example (e.g., My Dolly OOP style) and/or a boilerplate “template” on Github even more useful. Trying to read, think and understand all at the same time can be a bit much. Ya dig?

    Thanks Scott. I hope you have a spare moment to reply.

  9. Pingback: WordPressとオブジェクト指向プログラミング | stein2nd

  10. Pingback: WP + You + OOP | Scott Taylor | Captain Bookmarks

  11. Pingback: 10 reasons why WordPress is a better starting point for a project than you thought, even though you’re an advanced PHP developer | Gabriel's Blog

  12. Pingback: How to create your own CORE in WordPress | JAFDIPJAFDIP

  13. great article, thanks for that.

    I have successfully created an oop wp plugin using your guide. I have come across another problem though. I need to instantiate the class in a theme template file, when I try to do that i get this error:

    Fatal error: Class ‘LcCareersExplorer’ not found in /home/sbeasley/Sites/lc-dev1/public/wp-content/themes/lc-bootstrap-child/single-course.php on line 18

    im sure that wp loads plugins before the theme so im not sure why the class is not available.

    heres my code:

    lc_careers_explorer.php (plugin)

    class LcCareersExplorer {

    public function __construct() {
    write_log ( “LcCareersExplorer __construct” );
    add_action( ‘init’, array( $this, ‘load’ ), 1 );
    }

    public function load(){
    write_log(“load called”);
    }
    }

    in the theme tpl file, I have tried

    $careers_explorer = new LcCareersExplorer;

    and

    LcCareersExplorer::load();

    and both result in the error

    thanks

    simon

Comments are closed.