Creating Object-Oriented WordPress Plugins

Follow along at:
http://iandunn.name/wp-oop

Intended Audience

  • Mid-level plugin developers.
  • Many principles/techniques can be used in theme development too.

Interaction

  • Ask questions if anything's unclear
  • Jump in with comments, tips, etc

Overview

  • Intro to OOP
  • Implementing it in WordPress plugins
  • Browsing WordPress Plugin Skeleton
  • Closing
  • Q&A at the end of each section

OOP: Distilled

  • Procedural programming: functions running in a linear fashion
Procedural

Image: A Survey of Programming Techniques

OOP: Distilled

  • OOP: A web of collaborating objects
A web of objects

Image: Growing Object-Oriented Software, Guided by Tests

OOP: Distilled

  • OOP: A web of collaborating objects
$database = new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST );
$user     = new WP_User( $id );

$post          = new WP_Post();
$post->author  = $user->id;
$post->title   = 'In China, Human Costs Are Built Into an iPad';
$post->content = 'The explosion ripped through Building A5 on a Friday evening last May, an eruption of fire and noise that twisted metal pipes as if they were discarded straws...';

$database->insert( $database->posts_table, $post );

OOP: Why Should I Care?

  • Organization, intuitive design
  • Increased code re-use
  • Changes to components are isolated
  • Get your own namespace
  • More extensible
  • Easier to maintain
  • Culture of using design patterns
  • Programming is a craft

OOP: What Is It?

  • A software design paradigm
  • Classes modeling the problem domain
  • Stateful objects interacting with each other

OOP: What Does it Look Like?

Modeling the WordPress domain

  • User
  • Theme
  • Plugin
  • Widget
  • Content
    • Post
    • Page
    • Menu
    • Media
    • Comment

OOP: What Does it Look Like?

Object interaction

Object interaction

Image: Learn Ruby on Rails

OOP: What Does it Look Like?

Simple class:

class Post {
	private $id, $title;
	
	public function save() {
		// do stuff
	}
	
	private function validate_record() {
		// do stuff
	}
}				

OOP: What Does it Look Like?

Objects are instances of classes

// Save new post to database
$post         = new Post();
$post->title  = 'Spammers, Evildoers, and Opportunists';
$post->author = 'Derek Powazek';
$post->save();

// Delete post from database
$post = new Post( 12 );
$post->delete();

OOP: Objects vs Classes

  • Classes are abstract
  • Objects are tangible
Object interaction

Image: Learn Ruby on Rails

OOP: Core Principles

  • Modularity
  • Inheritence
  • Polymorphism
  • Encapsulation / data abstraction / information hiding
  • Strong cohesion, loose coupling

* OOP: Static Methods

Class Post {
	public function save() {   // changes state
		$this->last_modified = time();
		$this->slug = Post::sanitize_title( $this->title );
		$database->update( $this );
	}
	
	public static function sanitize_title( $title ) {   // doesn't change state
		$search  = array( ' ', '.' );
		$replace = array( '-', '' );
		
		return str_replace( $search, $replace, $title );
	}
}

$post->save();	// needs instance
$slug = Post::sanitize_title( 'Hello World.' );    // don't need instance

OOP: Interfaces

  • One way to implement polymorphism
  • Only contains method signatures
  • Classes that implement must provide all methods
interface Custom_Post_Type {
	public static function create_post_type();
	public static function create_taxonomies();
	public static function add_meta_boxes();
	public static function markup_meta_boxes( $post, $box );
	public static function save_post( $post_id, $revision );
}

OOP: Interfaces

Class Band Implements Custom_Post_Type {
	public static function create_post_type() {
		register_post_type( 'bands', $params );
	}
	
	// etc
}

Class Album Implements Custom_Post_Type {
	public static function create_post_type() {
		register_post_type( 'albums', $params );
	}
	
	// etc
}

OOP: Perspectives

  • Not always better than procedural programming
  • Fits some programs better than others
  • Also a matter of preference
  • ...and opinion

OOP

Any questions / Comments / Tips?

WP + OOP: Introduction

  • Now we can move on to implementing OOP in plugins
  • Increasingly popular in the WordPress community

WP + OOP

  • Break functions into logical groups (modules)
  • Make them classes
  • OOP principles: modularity, cohesion, data abstraction

WP + OOP

WordPress Plugin Skeleton modules

WP + OOP

  • Create a base class that all modules will extend Base Module Diagram

WP + OOP

  • Make it an abstract class

WP + OOP

abstract class WPPS_Module {
	private static $instances = array();
	
	public function __get( $variable ) {
		$module = get_called_class();
		
		if( in_array( $variable, $module::$readable_properties ) )
			return $this->$variable;
		else
			throw new Exception( __METHOD__ . " error: $". $variable ." doesn't exist or isn't readable." );
	}
	
	abstract protected function __construct();
	abstract public function activate( $network_wide );
	abstract public function register_hook_callbacks();
	abstract public function init();
	abstract public function upgrade( $db_version = 0 );
} // end WPPS_Module

WP + OOP

class WPPS_Cron extends WPPS_Module {
	protected static $readable_properties = array();
	
	protected function __construct() {
		$this->register_hook_callbacks();
	}
	
	public function register_hook_callbacks() {
		add_action( 'init', array( $this, 'init' ) );
		add_filter( 'cron_schedules', __CLASS__ . '::add_custom_cron_intervals' );
	}
	
	public function deactivate() {
		wp_clear_scheduled_hook( WordPress_Plugin_Skeleton::PREFIX . 'timed_jobs' );
		wp_clear_scheduled_hook( WordPress_Plugin_Skeleton::PREFIX . 'example_job' );
	}
} // end WPPS_Cron

WP + OOP

  • Create interfaces for repeatable modules
  • OOP Principle: Polymorphism

WP + OOP

interface WPPS_Custom_Post_Type {
	public static function create_post_type();
	public static function create_taxonomies();
	public static function add_meta_boxes();
	public static function markup_meta_boxes( $post, $box );
	public static function save_post( $post_id, $revision );
} // end WPPS_Custom_Post_Type

WP + OOP

class WPPS_CPT_Example extends WPPS_Module implements WPPS_Custom_Post_Type {
	public static function create_post_type() {
		$post_type = register_post_type(
			self::POST_TYPE_SLUG,
			$post_type_params
		);
	}
	
	public static function create_taxonomies() {
		register_taxonomy(
			self::TAG_SLUG,
			self::POST_TYPE_SLUG,
			$tax_params
		);
	}
}

* WP + OOP

  • Use the singleton pattern?
class Database {
	private static $instances = array();
	
	public static function get_instance() {
		$module = get_called_class();
		
		if( !isset( self::$instances[ $module ] ) )
			self::$instances[ $module ] = new $module();
		
		return self::$instances[ $module ];
	}
}

$a = Database::get_instance();
$b = Database::get_instance();	// $b == $a 

* WP + OOP

* WP + OOP

  • Use magic accessors/mutators to safely share info between classes
  • Assign proper visibility to methods (public / private / protected)
  • OOP Principles: Encapsulation / Abstraction / Information Hiding

* WP + OOP

class Foo {
	public function __get( $variable ) {
		$module = get_called_class();
		
		if( in_array( $variable, $module::$readable_properties ) )
			return $this->$variable;
		else
			throw new Exception( __METHOD__ . " error: $". $variable ." doesn't exist or isn't readable." );
	}
}

$foo = new Foo();
$bar = $foo->bar;	// calls Foo->__get( 'bar' );

* WP + OOP

class Post {
	public function get_raw_text() {
		$text = self::strip_html( $this->post_content );
		$text = self::strip_special_chars( $text );
		return $text;
	}
	
	protected static function strip_html( $html ) {
		// do stuff
		return $text;
	}
}

$post = new Post( 12 );
$raw_text = $post->get_raw_text();

* WP + OOP

Use traits to share methods horizontally

WP + OOP

Any questions / Comments / Tips?

WP + OOP: Plugin Skeleton

WP + OOP

Any questions / Comments / Tips?

WP + OOP: Closing

  • This is not the One True Way
  • Start the conversation
  • Becoming a better programmer
  • Fork the plugin skeleton

Takeaways: OOP

  • Create classes to model entities in the problem domain
  • Remember the core principles
    • Break code into smaller functions
    • Group similar functions together into classes
    • Inherit shared code from parent classes
    • Objects are black boxes

Takeaways: WP + OOP

  • Build classes as modules
  • Each module inherits from base module
  • Interfaces for repeatable objects (CPTs, etc)
  • Getters/setters

Additional Resources

OOP in General

OOP in WordPress

OOP criticism

Starters / Examples

Q&A

Any questions / Comments / Tips?