One of the nice features of WordPress is the plugin system, where ‘hooks’ allow custom functionality to be added without monkeying around with the core functionality.
I haven’t played around with this myself yet, but from what I could gather from the documentation, it does this using functions which are all defined in the global scope.
I was wondering today whether similar functionality could be achieved using a more object-oriented approach.
I wanted to be able to extend core functionality of one object by registering ‘hook’ methods on other objects at specific execution points. So I threw together the following code as a demo:
/**
* Binding class - represents a single event binding
*/
class Binding {
var $oObj;
var $sMethod;
/**
* Constructor
* @param object &$oObj the object on which we wish to call a method, passed by referenct
* @param string $sMethod the method to call
*/
function __construct(&$oObj, $sMethod) {
$this->oObj = $oObj;
$this->sMethod = $sMethod;
}
/**
* Trigger the event
* @param $aArgs an array of arguments for the bound function
*/
function trigger($aArgs = array()) {
call_user_func_array(array($this->oObj, $this->sMethod), $aArgs);
}
}
/**
* Singleton class to hold all event bindings
*/
class Hook {
/**
* Stores the static instance of this object - singleton design pattern
* @param object
*/
private static $instance;
/**
* Array of hooks
*/
private $aHooks = array();
/**
* Constructor
*/
private function __construct() {
}
/**
* get instance of this object
*/
public static function getInstance() {
if (!self::$instance)
{
self::$instance = new Hook();
}
return self::$instance;
}
/**
* Bind functionality
* @param string $sEvent the name of the event to bind
* @param object &$oObj the object whose method we wish to call
* @param string $sMethod the method to call
*/
function bind($sEvent, &$oObj, $sMethod) {
$this->aHooks[$sEvent][] = new Binding($oObj, $sMethod);
}
/**
* Trigger a bound event
* @param string $sEvent the event to trigger
* @param array $aArgs an array of arguments to pass to the bound function
*/
function trigger($sEvent, $aArgs = array()) {
if(array_key_exists($sEvent, $this->aHooks)) {
foreach($this->aHooks[$sEvent] as $key => $Binding) {
$Binding->trigger($aArgs);
}
}
}
}
class Extend {
function __construct() {
$Hook = Hook::getInstance();
$Hook->bind('pre', $this, 'beforeDoSomething');
$Hook->bind('post',$this, 'afterDoSomething');
}
function beforeDoSomething($arg) {
echo '<li>This before... the calling function wanted to say"' . $arg . '"';
}
function afterDoSomething($arg) {
echo '<li>The calling function said "' . $arg . '"';
}
}
class Extend2 {
function __construct() {
$Hook = Hook::getInstance();
$Hook->bind('post', $this, 'beforeDoSomething');
}
function beforeDoSomething() {
echo '<li>This is done in a second extended object';
}
}
/**
* Class providing base functionality
*/
class Base {
function __construct() {
}
function doSomething() {
$Hook = Hook::getInstance();
$Hook->trigger('pre', array("I'm about to start doing something..."));
echo '<li>doing the core functionality...';
$Hook->trigger('post', array("Ok I'm all done now!"));
}
}
$Base = new Base();
$Extend = new Extend();
$Extend2 = new Extend2();
$Base->doSomething();
Hopefully someone will find it useful
