Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Use a spacebar or arrow keys to navigate

OOP for Theme Development

Presented by Ben Doherty @ Oomph, Inc.

ben@oomphinc.com

Skype: bdoherty.oomphinc

Github: http://github.com/bendoh/

Follow along with me

About Oomph


Located right here in sunny Providence, Rhode Island!

Ben Doherty

Why OOP?

Why OOP in WordPress?

1. Namespacing

2. Code Re-Use

3. Consistency

4. Boilerplate-Ready

Coding Principals

KISS

KISS!

DRY

DRY!

Do-Not-Depend

Newer Isn't Always Better

// These two features are available as of PHP 5.3, 
// but WordPress can run on PHP 5.2.4

// Cool new operator... Short-circuit ternary?
$the_value = $_POST['value'] ?: "default value"; 

// Anonymous functions. 
add_filter( 'the_content', function( $content ) {
	return $content . " filtered!";
});

I don't know it all!

Code Organization

theme/functions.php
Top-level functions available for all theme files
theme/functions/
Additional function groups and code modules
theme/functions/template.php
Template tags. Functions that emit markup.
theme/functions/class-*.php
Class files. Lower-case, prefixed with class.
Example: theme/functions/class-my-sweet-post.php

Workflow

In your wp-config.php:

define( 'WP_DEBUG', true );
Turn on WordPress debugging. This will turn on various PHP Warnings and Errors.
define( 'WP_DEBUG_DISPLAY', false );
Don't dump debug messages to the screen.
define( 'WP_DEBUG_LOG', true );
Log to wp-content/debug.log
Watch wp-content/debug.log like a HAWK! PHP Warnings are a bad thing and can indicate bugs in your code.
Plugins: Debug Bar, Debug Bar Extender

Now for some code

Namespacing

Code Sample OOP Code Sample

Code Re-Use

OOP Code Sample: My_Sweet_Meta_Box OOP Code Sample: Your_Sweet_Meta_Box

Instantiation

Singletons

Singleton: Static Methods

class My_Singleton {
	static function init() {
		add_action( 'save_post', array( get_class(), 'action_save_post' ) );
	}

	static function action_save_post( $post_id ) {
	  ...
	}
}
My_Singleton::init();

Auto-Enforced Singleton

class My_Singleton {
	private function __construct() {
		add_action( 'save_post', array( $this, 'action_save_post' ) );
	}

	private function __clone() { } // Disallow clone() of object

	private static $instance = false;

	public static function get_instance() {
		if( !$self::instance )
			$self::instance = new My_Singleton;

		return $self::instance;
	}

	static function action_save_post( $post_id ) {
	  ...
	}
}
My_Singleton::get_instance();

Singleton Base Class

class Singleton {
	protected static $instances;

	protected function __construct() { }

	final private function __clone() { }

	public static function get_instance() {
		$class = get_called_class();

		if (!isset(self::$instances[$class])) {
			self::$instances[$class] = new $class;
		}
		return self::$instances[$class];
	}
}

Singleton Base Class in Action

class My_Singleton extends Singleton {
	function My_Singleton() {
		add_action( 'save_post', array( $this, 'action_save_post' ) );
	}

	function action_save_post( $post_id ) {
	  ...
	}
}
My_Singleton::get_instance();

Singleton Factory

function my_singleton_factory( $class ) {
	static $instances;

	if( !isset( $instances[$class] ) )
		$instances[$class] = new $class;
		
	return $instances[$class];
}

Singleton Factory with Placeholder


/*
 * Placeholder class that simulates a real object, but does nothing.
 * Code depending on a missing class won't cause fatal errors, 
 * Messages will get printed to error log when accessed
 */
class My_Placeholder {
	private $placeholder_class = '';
	private $reason = '';

	function __construct( $class, $reason ) {
		$this->placeholder_class = $class;
		$this->reason = $reason;
		error_log( "Attempted to create non-existent class singleton in class '$this->placeholder_class' (reason=$reason)" );
	}
	function __call( $method, $args ) {
		error_log( "Attempted to call method '$method' on missing class '$this->placeholder_class'; args", $args );
		return null;
	}
	function __get( $name ) {
		error_log( "Attempted to access object property '$name' on missing class '$this->placeholder_class'" );
		return null;
	}
	function __set( $name, $value ) {
		error_log( "Attempted to set object property '$name' to '$value' on missing class '$this->placeholder_class'" );
	}
}

Singleton Factory with Placeholder (Continued)

function my_singleton_factory( $class ) {
	static $instances;

	if( !class_exists( $class ) ) 
		return new My_Placeholder( $class, "Class does not exist" );

	if( !isset( $instances[$class] ) )
		$instances[$class] = new $class;

	return $instances[$class];
}

Where does it go?

Classes should ALWAYS be put in their own class file

UNLESS the classes are very tightly related and co-dependant


Include class in functions.php, instantiate separately:

include( __DIR__ . '/functions/class-my-sweet-post.php' );

Object Heirarchy

Wide vs. Deep Object Graphs

Deep Object Graph

Deep Class Heirarchy

Deep Object Graph: Pros and Cons

Shallow Object Graph

Shallow Class Heirarchy

Shallow Object Graph: Pros and Cons

Object Communication

Updated Sweet Post Meta Box

Revised sweet post meta box object code

Get Sweetness in Theme

Naive accessor Accessor via filter

Render Output — Naive

functions/class-my-event.php

Naive Event Class

single-event.php

Naive Event Rendering

Render Output with Actions!

functions/class-my-event.php

Action Event Class

single-event.php

Action Event Rendering

Actions + Filters Remove Dependencies

Super Class: Widget

My_Widget_Base Class

Wrap-Up