Skip to content

Instantly share code, notes, and snippets.

@phpnode
Created October 25, 2011 09:24
Show Gist options
  • Save phpnode/1312008 to your computer and use it in GitHub Desktop.
Save phpnode/1312008 to your computer and use it in GitHub Desktop.
Yii based event system with no dependencies
<?php
class Eventable {
/**
* The events attached to this class
* @var Event[]
*/
protected $_events = array();
/**
* Determines whether an event is defined.
* An event is defined if the class has a method named like 'onXXX'.
* Note, event name is case-insensitive.
* @param string $name the event name
* @return boolean whether an event is defined
*/
public function hasEvent($name)
{
return !strncasecmp($name,'on',2) && method_exists($this,$name);
}
/**
* Checks whether the named event has attached handlers.
* @param string $name the event name
* @return boolean whether an event has been attached one or several handlers
*/
public function hasEventHandler($name)
{
$name=strtolower($name);
return isset($this->_events[$name]) && count($this->_events[$name])>0;
}
/**
* Returns the list of attached event handlers for an event.
* @param string $name the event name
* @return array list of attached event handlers for the event
* @throws Exception if the event is not defined
*/
public function getEventHandlers($name)
{
if($this->hasEvent($name))
{
$name=strtolower($name);
if(!isset($this->_events[$name]))
$this->_events[$name]=array();
return $this->_events[$name];
}
else
throw new Exception('Event "'.get_class($this).'.'.$name.'" is not defined.');
}
/**
* Attaches an event handler to an event.
*
* An event handler must be a valid PHP callback, i.e., a string referring to
* a global function name, or an array containing two elements with
* the first element being an object and the second element a method name
* of the object.
*
* An event handler must be defined with the following signature,
* <pre>
* function handlerName($event) {}
* </pre>
* where $event includes parameters associated with the event.
*
* This is a convenient method of attaching a handler to an event.
* It is equivalent to the following code:
* <pre>
* $component->getEventHandlers($eventName)->add($eventHandler);
* </pre>
*
* Using {@link getEventHandlers}, one can also specify the excution order
* of multiple handlers attaching to the same event. For example:
* <pre>
* $component->getEventHandlers($eventName)->insertAt(0,$eventHandler);
* </pre>
* makes the handler to be invoked first.
*
* @param string $name the event name
* @param callback $handler the event handler
* @throws Exception if the event is not defined
* @see detachEventHandler
*/
public function attachEventHandler($name,$handler)
{
$name=strtolower($name);
if(!isset($this->_events[$name]))
$this->_events[$name]=array();
$this->_events[$name][] = $handler;
}
/**
* Detaches an existing event handler.
* This method is the opposite of {@link attachEventHandler}.
* @param string $name event name
* @param callback $handler the event handler to be removed
* @return boolean if the detachment process is successful
* @see attachEventHandler
*/
public function detachEventHandler($name,$handler)
{
$name = strtolower($name);
if($this->hasEventHandler($name)) {
foreach($this->_events[$name] as $n => $eventHandler){
if ($handler === $eventHandler) {
unset($this->_events[$name][$n]);
return true;
}
}
return false;
}
else
return false;
}
/**
* Raises an event.
* This method represents the happening of an event. It invokes
* all attached handlers for the event.
* @param string $name the event name
* @param Event $event the event parameter
* @throws Exception if the event is undefined or an event handler is invalid.
*/
public function raiseEvent($name,$event)
{
$name=strtolower($name);
if(isset($this->_events[$name]))
{
foreach($this->_events[$name] as $handler)
{
if(is_string($handler))
call_user_func($handler,$event);
else if(is_callable($handler,true))
{
if(is_array($handler))
{
// an array: 0 - object, 1 - method name
list($object,$method)=$handler;
if(is_string($object)) // static method call
call_user_func($handler,$event);
else if(method_exists($object,$method))
$object->$method($event);
else
throw new Exception(strtr('Event "{class}.{event}" is attached with an invalid handler "{handler}".',
array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1])));
}
else // PHP 5.3: anonymous function
call_user_func($handler,$event);
}
else
throw new Exception(strtr('Event "{class}.{event}" is attached with an invalid handler "{handler}".',
array('{class}'=>get_class($this), '{event}'=>$name, '{handler}'=>gettype($handler))));
// stop further handling if param.handled is set true
if(($event instanceof Event) && $event->handled)
return;
}
}
else if(!$this->hasEvent($name))
throw new Exception(strtr('Event "{class}.{event}" is not defined.',
array('{class}'=>get_class($this), '{event}'=>$name)));
}
}
class Event {
/**
* @var object the sender of this event
*/
public $sender;
/**
* @var boolean whether the event is handled. Defaults to false.
* When a handler sets this true, the rest of the uninvoked event handlers will not be invoked anymore.
*/
public $handled=false;
/**
* @var mixed additional event parameters.
*/
public $params;
/**
* Whether the event is valid or not
* @var boolean
*/
public $isValid = true;
/**
* Constructor.
* @param mixed $sender sender of the event
* @param mixed $params additional parameters for the event
*/
public function __construct($sender=null,$params=null)
{
$this->sender=$sender;
$this->params=$params;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment