Simulating PHP Events
Events
Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the dispatcher and the classes that receive (or handle) the event are called event listeners.
One of the biggest drawbacks of php is the lack of events, however a basic framework can be constructed in order to enable event simulation. Having searched and read a few articles about it I decided to make my own implementation of an event handler.
The Event Class
First we need to create a simple object that will hold the name and the callback ( the function that it will call when an Event is called ), constraining these proprieties into a class will enable us later on to extend the class provide additional parameters. The Object class is there just to modify the __toString() function.
abstract class Object
{
public function __toString() { return '[object ' . get_class($this) . ']'; }
}
class Event extends Object
{
protected $name;
protected $callback;
public function __construct($event_name, $callback = null)
{
if($event_name === null)
throw new Exception('Event class must have a name.');
else if($callback !== null)
$this -> callback = $callback;
$this -> name = $event_name;
}
public function setCallback($callback)
{
$this -> callback = $callback;
}
public function getCallback() { return $this -> callback; }
public function getSender() { return $this -> callback[0]; }
public function getName() { return $this -> name; }
}
The EventCollection Class
The EventCollection class is just the holder for the events it enables as to add, verify and remove Events from the array, the Countable and SeekableIterator interfaces enable us to use count() and foreach() functions on it.
class EventCollection extends Object implements SeekableIterator, Countable
{
private $events;
public function __construct()
{
$this -> events = array();
}
public function addEvent(Event $eventObject)
{
if(!$this -> contains($eventObject -> getName()))
$this -> events[] = $eventObject;
}
public function contains($event_name)
{
foreach($this -> events as $event)
if($event -> getName() === $event_name)
return true;
return false;
}
public function removeEvent(Event $event)
{
for($i = 0; $i < count($events); $i++)
if($this -> events[$i] -> getName() === $event_name -> getName())
array_splice($this -> events, $i, 1);
}
public function seek($position)
{
if($position >= count($this -> events))
throw new OutOfBoundsException("invalid seek position ($position)");
$this -> rewind();
for($i = 0; $i <= $position; $i++)
next();
}
public function count() { return count($this -> events); }
public function rewind() { reset($this -> events); }
public function current(){ return current($this -> events); }
public function key() { key($this -> events); }
public function next() { return next($this -> events); }
public function valid() { return ($this -> current() !== false); }
}
The EventDispatcher Class
So far we’ve got an Event class and a holder class for it, we’re going to need a way to dispatch and listen to the events that will be available to any class that will inherit the EventDispatcher.
class EventDispatcher extends Object
{
protected $events;
protected function dispatchEvent(Event $event)
{
if(!($this -> events instanceof EventCollection))
$this -> events = new EventCollection();
foreach($this -> events as $e)
if($e -> getName() === $event -> getName())
if(is_callable($e -> getCallback()))
call_user_func($e -> getCallback(), $e);
else
throw new Exception('Non Valid Callback in ' . $e -> getName());
protected function addEventListener(Event $event, $callback)
{
if(!($this -> events instanceof EventCollection))
$this -> events = new EventCollection();
$event -> setCallback($callback);
$this -> events -> addEvent($event);
}
}
Usage
First in order for a class to make use of what we done so far it will need to extend the EventDispatcher class, this will allow it to listen as well as dispatch new events, myClass being the listener and myClass2 the dispatcher. We add an EventListener and bind it to myClass::event() function, when the dispatchEvent(‘event’) is fired (in the myClass2) the event function will be called.
class myClass extends EventDispatcher
{
public function __construct()
{
$c = new myClass2();
$c -> addEventListener(new Event('something'), array(&$this, 'event'));
$c -> doSomething();
}
public function event($event)
{
echo 'Event Dispatched';
}
}
class myClass2 extends EventDispatcher
{
public function doSomething()
{
$this -> dispatchEvent(new Event('something'));
}
}
$class = new myClass();
// Event Dispatched
Conclusion
I hope that this article has ‘simulated’ your interests in events and event handling, you are free to use this code as you wish. Be sure to let me know how it works out and post any nifty improvements in the comments thread of the article.
0 Comments
Trackbacks/Pingbacks