Skip to content

Instantly share code, notes, and snippets.

@chrisguitarguy
Created September 29, 2012 03:38
Show Gist options
  • Save chrisguitarguy/3803077 to your computer and use it in GitHub Desktop.
Save chrisguitarguy/3803077 to your computer and use it in GitHub Desktop.
Using classes in WordPress plugins. Please comment!
<?php
/* Using Classes in WordPress Plugins */
/**
* The "normal" way. Actions are added in a constructor. Create a new instance
* of the class to "bootstrap" the plugin.
*
* Two ways to instantiate:
* 1. anonymous:
* new MyPlugin1;
* 2. assign it to a variable
* $myplugin1 = new MyPlugin;
*
* The first makes it nearly impossible to unhook things. The second doesn't.
*
* Benefits:
* - Easy to understand and implement
* - Most documentation on using classes in plugins does this
*
* Questionable:
* - Makes it harder to unhook things?
*/
class MyPlugin1
{
public function __construct()
{
add_action('plugins_loaded', array($this, 'loaded'));
}
public function loaded()
{
// do stuff
}
}
/**
* The "static" way. All methods are static. Functions that get hooked in
* are done so with an array containing the fully qualified class name and
* method name.
*
* eg.
* 1. add_action('some_action', array(__CLASS__, 'actor'));
* 2. add_action('some_action', array(get_class(), 'actor'));
* 3. add_action('some_action, array('MyPlugin2', 'actor'));
* 4. add_action('some_action', 'MyPlugin2::actor');
*
* Number 3 & 4 is more limited, especially if using namespaces. Change the
* class name and you'll need to change it everywhere.
*
* Actions are added in a central `init` method (or whatever) that pretty
* much acts as a constructor.
*
* Bootsrap the class and all all the actions and such:
* MyPlugin2::init();
*
* Benefits:
* - Very easy to unhook things
* - Relatively straightforward to implement and grok
*
* Questionable:
* - Seems more verbose?
* - Can't take advantage of object state in the same ways
* - PHP < 5.3 doesn't have late static binding: static properties
* behave in unexpected ways in subclasses.
*/
class MyPlugin2
{
public static function init()
{
add_action('plugins_loaded', array(__CLASS__, 'loaded'));
}
public static function loaded()
{
// do stuff
}
}
/**
* The "singleton" way. Use a few static methods for the singleton pattern,
* add the real hooks in the constructor.
*
* Bootstrap the class and all all the actions the same way as you would for
* the "static" route:
* MyPlugin3::init();
*
* Or just call the instance method to bootstrap things.
* MyPlugin3::instnace();
*
* Advantages:
* - Easy to unhook stuff:
* remove_action('plugins_loaded', array(MyPlugin3::instance(), 'loaded'), 20);
* - Take advantage of object state and more predictable class attributes
* - We really only want one instance of this object running around. Singleton
* pattern takes care of that.
*
* Questionable
* - Because of the weirdness of static methods, you'd basically write the
* same bolier plate on every plugin class. There are certain singleton
* pattners that could alleviate that. Example:
* https://github.com/habari/system/blob/master/classes/singleton.php
* - Overly complex?
*/
class MyPlugin3
{
private static $ins;
public static function init()
{
add_action('plugins_loaded', array(__CLASS__, 'instance'));
}
public static function instance()
{
is_null(self::$ins) && self::$ins = new self;
return self::$ins;
}
private function __construct()
{
add_action('plugins_loaded', array($this, 'loaded'), 20);
}
public function loaded()
{
// do stuff
}
}
/**
* A way to do the singleton pattern with a base class and PHP 5.3+
*
*/
abstract class Singleton
{
/**
* Container for the objects.
*
* @since 0.1
*/
private static $registry = array();
/**
* Get an instance of the current, called class.
*
* @since 0.1
* @access public
* @return object An instance of $cls
*/
public static function instance()
{
$cls = get_called_class();
!isset(self::$registry[$cls]) && self::$registry[$cls] = new $cls;
return self::$registry[$cls];
}
/**
* Init method that adds the `instance` method of the called class to the
* `plugins_loaded` hook.
*
* @since 0.1
* @uses add_action
* @return void
*/
public static function init()
{
add_action('plugins_loaded', array(get_called_class(), 'instance'));
}
/**
* Kill the __clone method.
*
* @since 0.1
*/
private final function __clone()
{
// empty
}
/**
* Subclasses must define construct, this should be where all the other
* actions/filters are added.
*
* @since 0.1
* @access protectect
*/
abstract protected function __construct();
}
class MyPlugin extends Singleton
{
protected function __construct()
{
add_action('init', array($this, 'do_stuff'));
}
public function do_stuff()
{
// do stuff
}
}
@thefuxia
Copy link

My answer as code: https://gist.github.com/3804204

Your first method is always anonymous.

$myplugin1 = new MyPlugin;

… does not create a global variable. $myplugin1 is not global when created in a plugin file.

The second method is not OOP, just poor namespacing. :)

The third method is very hard to test. There are no separate instances possible. Singletons are just bad.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment