Skip to content

Instantly share code, notes, and snippets.

@alanmcginnis
Forked from chrisguitarguy/wp-plugin-classes.php
Created January 14, 2020 01:49
Show Gist options
  • Save alanmcginnis/351b7d29f2d6934fc45dac0ac8d5287c to your computer and use it in GitHub Desktop.
Save alanmcginnis/351b7d29f2d6934fc45dac0ac8d5287c 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
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment