<?php
/**
* Implements event handling: attaching Delegates to events and triggering them
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 12, 2005
*/
class EventHandler
{
/**
* @var array 2-dimensional IDelegate map
*/
protected $listeners = array ( );
/**
* @var mixed invocation arguments
*/
protected $arguments = array ( );
/**
* Attached listeners will be invoked using whatever arguments the
* EventHandler was constructed with.
*/
public function __construct ( )
{
$this-> arguments = func_get_args ( );
}
/**
* Attach a Delegate to an event
* @param string event
* @param object IDelegate
*/
public function attach ( $event, IDelegate $delegate )
{
$this-> listeners [ $event ] [ ] = $delegate;
}
/**
* Trigger event, invoking all attached listeners
* @param string event
* @return int number of listeners invoked
*/
public function trigger ( $event )
{
if ( isset ( $this-> listeners [ $event ] ) && is_array ( $this-> listeners [ $event ] ) )
{
foreach ( $this-> listeners [ $event ] as $delegate )
{
$delegate-> invoke ( $this-> arguments );
}
return count ( $this-> listeners [ $event ] );
}
return 0;
}
public function __set ( $event, $delegate )
{
$this-> attach ( $event, $delegate );
}
public function __call ( $event, $args )
{
$this-> trigger ( $event );
}
}
interface IDelegate
{
/**
* Invoke subordinate
* @param array arguments, optional
* @return mixed subordinate return value
*/
public function invoke ( $args = NULL );
}
/**
* Calls a method on an object upon invocation
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 12, 2005
*/
class Delegate implements IDelegate
{
protected $subordinate = NULL;
protected $method = NULL;
public function __construct ( $subordinate, $method )
{
$this-> subordinate = $subordinate;
$this-> method = $method;
}
public function invoke ( $args = NULL )
{
Handle:: resolve ( $this-> subordinate );
return call_user_func_array ( array ( $this-> subordinate, $this-> method ), ( array ) $args );
}
}
/**
* Calls a static method on a class upon invocation
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 14, 2005
*/
class StaticDelegate implements IDelegate
{
protected $class = NULL;
protected $method = NULL;
protected $file = NULL;
/**
* @param string class name
* @param string method name
* @param string file to be included before invocation, optional
*/
public function __construct ( $class, $method, $file = NULL )
{
$this-> class = $class;
$this-> method = $method;
$this-> file = $file;
}
public function invoke ( $args = NULL )
{
if (! empty ( $this-> file ) )
{
require_once ( $this-> file );
}
return call_user_func_array ( array ( $this-> class, $this-> method ), ( array ) $args );
}
}
/**
* "A Handle represents an uninstantiated object that takes the place of a
* given object and can be swapped out for the object.
* Implements lazy loading for composing object heirarchies."
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 12, 2005
* @see http://wact.sourceforge.net/index.php/ResolveHandle
*/
class Handle
{
/**
* @var string class name
*/
protected $class = NULL;
/**
* @var array class constructor arguments
*/
protected $args = array ( );
/**
* @var string class definition file
*/
protected $file = NULL;
/**
* @param string class name
* @param array class constructor arguments
* @param string class definition filename, optional
*/
public function __construct ( $class, $args, $file = NULL )
{
$this-> class = (string ) $class;
$this-> args = ( array ) $args;
$this-> file = (string ) $file;
}
/**
* Resolves a Handle; replaces a Handle instance with its identified class
* @param object passed by reference
*/
static public function resolve (& $handle )
{
if ( $handle instanceof self )
{
$class = $handle-> getClass ( );
$file = $handle-> getFile ( );
if (! class_exists ( $class ) && ! empty ( $file ) && is_readable ( $file ) )
{
require_once ( $file );
}
/**
* newInstance seems to be misdocumented: it's actually newInstance ( [mixed args [, ...]] ).
*/
$handle = call_user_func_array ( array ( new ReflectionClass ( $class ), 'newInstance' ), $handle-> getArgs ( ) );
}
}
public function getClass ( ) { return $this-> class; }
public function getFile ( ) { return $this-> file; }
public function getArgs ( ) { return $this-> args; }
}
/**
* Example follows
*/
/**
* A class that composes an EventHandler for attaching Observers
*/
class Observable
{
/**
* @var object EventHandler
*/
public $events = NULL;
public function __construct ( )
{
$this-> events = new EventHandler ( $this );
}
public function foo ( )
{
echo "Observable: method foo called /n<br />";
$this-> events-> onFoo ( ); // Triggers the event "onFoo"
}
public function bar ( )
{
echo "Observable: method bar called /n<br />";
$this-> events-> onBar ( ); // Triggers the event "onBar"
}
}
/**
* A matching Observer class for Observable
*/
class Observer
{
public function __construct (Observable $observable )
{
/**
* Setting a delegate to a property of an EventHandler is the same
* as calling attach() - a nice short cut. Note that you aren't limited
* to one listener per event, there can be several.
*/
$observable-> events-> onFoo = new Delegate ( $this, 'callback_foo' );
$observable-> events-> onBar = new Delegate ( $this, 'callback_bar' );
}
public function callback_foo (Observable $observable )
{
echo "Observer: event onFoo triggered /n<br />";
}
public function callback_bar (Observable $observable )
{
echo "Observer: event onBar triggered /n<br />";
}
}
/**
* Let's demonstrate a bit
*/
$observable = new Observable;
$observer = new Observer ( $observable );
$observable-> foo ( );
$observable-> bar ( );
/**
* Now, imagine you want an object to respond to an event, but you don't want
* to include it's definition or instantiate it before you really have to. This
* is lazy loading, just what Handle is for.
*
* Let's imagine there's a class named ColossalObserver that resides in
* ColossalObserver.php and has a method named `callback` - let's create a
* Handle and attach it.
*/
$colossal = new Handle ( 'ColossalObserver', NULL, 'ColossalObserver.php' );
$observable-> events-> onBar = new Delegate ( $colossal, 'callback' );
/**
* The rest is left as an exercise for the reader.
*/
?>
/**
* Implements event handling: attaching Delegates to events and triggering them
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 12, 2005
*/
class EventHandler
{
/**
* @var array 2-dimensional IDelegate map
*/
protected $listeners = array ( );
/**
* @var mixed invocation arguments
*/
protected $arguments = array ( );
/**
* Attached listeners will be invoked using whatever arguments the
* EventHandler was constructed with.
*/
public function __construct ( )
{
$this-> arguments = func_get_args ( );
}
/**
* Attach a Delegate to an event
* @param string event
* @param object IDelegate
*/
public function attach ( $event, IDelegate $delegate )
{
$this-> listeners [ $event ] [ ] = $delegate;
}
/**
* Trigger event, invoking all attached listeners
* @param string event
* @return int number of listeners invoked
*/
public function trigger ( $event )
{
if ( isset ( $this-> listeners [ $event ] ) && is_array ( $this-> listeners [ $event ] ) )
{
foreach ( $this-> listeners [ $event ] as $delegate )
{
$delegate-> invoke ( $this-> arguments );
}
return count ( $this-> listeners [ $event ] );
}
return 0;
}
public function __set ( $event, $delegate )
{
$this-> attach ( $event, $delegate );
}
public function __call ( $event, $args )
{
$this-> trigger ( $event );
}
}
interface IDelegate
{
/**
* Invoke subordinate
* @param array arguments, optional
* @return mixed subordinate return value
*/
public function invoke ( $args = NULL );
}
/**
* Calls a method on an object upon invocation
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 12, 2005
*/
class Delegate implements IDelegate
{
protected $subordinate = NULL;
protected $method = NULL;
public function __construct ( $subordinate, $method )
{
$this-> subordinate = $subordinate;
$this-> method = $method;
}
public function invoke ( $args = NULL )
{
Handle:: resolve ( $this-> subordinate );
return call_user_func_array ( array ( $this-> subordinate, $this-> method ), ( array ) $args );
}
}
/**
* Calls a static method on a class upon invocation
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 14, 2005
*/
class StaticDelegate implements IDelegate
{
protected $class = NULL;
protected $method = NULL;
protected $file = NULL;
/**
* @param string class name
* @param string method name
* @param string file to be included before invocation, optional
*/
public function __construct ( $class, $method, $file = NULL )
{
$this-> class = $class;
$this-> method = $method;
$this-> file = $file;
}
public function invoke ( $args = NULL )
{
if (! empty ( $this-> file ) )
{
require_once ( $this-> file );
}
return call_user_func_array ( array ( $this-> class, $this-> method ), ( array ) $args );
}
}
/**
* "A Handle represents an uninstantiated object that takes the place of a
* given object and can be swapped out for the object.
* Implements lazy loading for composing object heirarchies."
*
* @author Ezku (dmnEe0@gmail.com)
* @since Jul 12, 2005
* @see http://wact.sourceforge.net/index.php/ResolveHandle
*/
class Handle
{
/**
* @var string class name
*/
protected $class = NULL;
/**
* @var array class constructor arguments
*/
protected $args = array ( );
/**
* @var string class definition file
*/
protected $file = NULL;
/**
* @param string class name
* @param array class constructor arguments
* @param string class definition filename, optional
*/
public function __construct ( $class, $args, $file = NULL )
{
$this-> class = (string ) $class;
$this-> args = ( array ) $args;
$this-> file = (string ) $file;
}
/**
* Resolves a Handle; replaces a Handle instance with its identified class
* @param object passed by reference
*/
static public function resolve (& $handle )
{
if ( $handle instanceof self )
{
$class = $handle-> getClass ( );
$file = $handle-> getFile ( );
if (! class_exists ( $class ) && ! empty ( $file ) && is_readable ( $file ) )
{
require_once ( $file );
}
/**
* newInstance seems to be misdocumented: it's actually newInstance ( [mixed args [, ...]] ).
*/
$handle = call_user_func_array ( array ( new ReflectionClass ( $class ), 'newInstance' ), $handle-> getArgs ( ) );
}
}
public function getClass ( ) { return $this-> class; }
public function getFile ( ) { return $this-> file; }
public function getArgs ( ) { return $this-> args; }
}
/**
* Example follows
*/
/**
* A class that composes an EventHandler for attaching Observers
*/
class Observable
{
/**
* @var object EventHandler
*/
public $events = NULL;
public function __construct ( )
{
$this-> events = new EventHandler ( $this );
}
public function foo ( )
{
echo "Observable: method foo called /n<br />";
$this-> events-> onFoo ( ); // Triggers the event "onFoo"
}
public function bar ( )
{
echo "Observable: method bar called /n<br />";
$this-> events-> onBar ( ); // Triggers the event "onBar"
}
}
/**
* A matching Observer class for Observable
*/
class Observer
{
public function __construct (Observable $observable )
{
/**
* Setting a delegate to a property of an EventHandler is the same
* as calling attach() - a nice short cut. Note that you aren't limited
* to one listener per event, there can be several.
*/
$observable-> events-> onFoo = new Delegate ( $this, 'callback_foo' );
$observable-> events-> onBar = new Delegate ( $this, 'callback_bar' );
}
public function callback_foo (Observable $observable )
{
echo "Observer: event onFoo triggered /n<br />";
}
public function callback_bar (Observable $observable )
{
echo "Observer: event onBar triggered /n<br />";
}
}
/**
* Let's demonstrate a bit
*/
$observable = new Observable;
$observer = new Observer ( $observable );
$observable-> foo ( );
$observable-> bar ( );
/**
* Now, imagine you want an object to respond to an event, but you don't want
* to include it's definition or instantiate it before you really have to. This
* is lazy loading, just what Handle is for.
*
* Let's imagine there's a class named ColossalObserver that resides in
* ColossalObserver.php and has a method named `callback` - let's create a
* Handle and attach it.
*/
$colossal = new Handle ( 'ColossalObserver', NULL, 'ColossalObserver.php' );
$observable-> events-> onBar = new Delegate ( $colossal, 'callback' );
/**
* The rest is left as an exercise for the reader.
*/
?>