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
OOP: Distilled
- OOP: A web of collaborating objects
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
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
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
WP + OOP
- Create a base class that all modules will extend
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
- The Case for Singletons in WordPress by Eric Mann
- The Case Against Singletons in WordPress by Mike Toppa
- My opinion: Nice, but probably unnecessary
* 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
- Implemented these ideas in my plugin skeleton
- Browse code
- Fork on GitHub
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 Full Effect by John Klein
- Object-Oriented PHP for Beginners by Jason Lengstorf
- Object Oriented PHP for Beginners at Killer PHP
- Learn Ruby on Rails by Patrick Lenz
OOP in WordPress
- Object-Oriented WordPress Plugin Development by Tom McFarlin
- WP + You + OOP by Scott Taylor
- WordPress: Community and Architecture by Mike Toppa
- Clean Code for WordPress Plugin Development by Mike Toppa
- Design Patterns in WordPress by Tom McFarlin
- Rethinking Object-Oriented WordPress Plugins by Jonathan Brinley
- To OOP or not to OOP by Nikolay Bachiyski
- OOP for Theme Development by Ben Doherty
- Object-Oriented Programming for WordPress Plugin Development by Mike Toppa
- Designing Object-Oriented Plugins for a Procedural Application by Ian Dunn
OOP criticism
- Your Code: OOP or POO? by Jeff Atwood
- The Best Tool For The Job: OO versus Procedural Programming in PHP by Cal Evans
- SimplePHPEasyPlus by Antoine Hérault
Starters / Examples
- OOP Plugin Template Solution by Daniel Convissor
- Sidecar by Mike Schinkel
- WordPress Plugin Boilerplate by Tom McFarlin
- Shashin by Mike Toppa
- Tagregator by Ian Dunn
- CampTix by Konstantin Kovshenin
- WordPress Plugin Skeleton by Ian Dunn
- WordPress Plugin Kickstarter by Vasyl Martyniuk
- PW_Archives by Phillip Walton