Location: PHPKode > scripts > Event > event/Event.php
<?php 

/**
 * ACE - Advanced Content Environment Framework
 *
 * LICENSE
 *
 * This is a part of ACE - Advanced Content Environment Framework,
 * any usage without permission of the author is prohibited.
 * Licence can be obtained by sending email to hide@address.com
 *
 * @category   ACE
 * @package    ACE
 * @author     Michał Szpakowski
 * @copyright  Copyright (c) 2007 Michał Szpakowski
 * @license    Limited, email hide@address.com
 */

/**
 * @category   ACE
 * @package    Event
 * @copyright  Copyright (c) 2007 Michał Szpakowski
 * @license    Limited, email hide@address.com
 */
 
final class Event
{
	/**
	 * Public constants for signalling access of Event.
	 * Access is meant to restrict possibility to call the event
	 * via the {@link Event::call()} method.
	 * E_PRIVATE: event can only be called by the object passed to
	 * the constructor. Useful, when the caller is private or protected
	 * as var inside a class.
	 * E_PROTECTED: event can be called only by the object
	 * of specific class - for information purposes only, belonging
	 * of the event to the specific package, as there is no posiibility
	 * to define 'private' class in php/
	 * E_PUBLIC: event can be called by any instance of {@link EventCaller}
	 * or it's descendants.
	 *
	 */
	
	const E_PRIVATE = 0;
	const E_PROTECTED = 1;
	const E_PUBLIC = 2;
	
	/**
	 * Private static array to hold instances of Event
	 *
	 * @var array
	 * @access private
	 */
	private static $__events = array();
	
	/**
	 * Name of the event, same as key to {@link Event::$__events}
	 * array.
	 * 
	 * @var string
	 * @access private
	 */
	private $__eventname;
	
	/**
	 * Access flag: {@link Event::E_PRIVATE},
	 * {@link Event::E_PROTECTED}, {@link Event::E_PUBLIC}
	 * {@link Event::getAccess()}
	 * 
	 * @var int
	 * @access private
	 */
	private $__access;
	
	/**
	 * Instance of EventCaller or string classname;
	 * {@link Event::getCallerClass()}
	 *
	 * @var object|string
	 * @access private
	 */
	private $__caller;
	
	/**
	 * Dataset passed by *Call() methods
	 * {@link Event::getDataset()}
	 *
	 * @var string
	 * @access private
	 */
	private $__dataset;
	
	/**
	 * Flag - if to destroy the event after calling
	 * {@link Event::isDisposable()}
	 *
	 * @var bool
	 * @access private
	 */
	private $__disposable;
	
	
	/**
	 * Name of the interface implemented by listeners
	 * {@link Event::factory()}
	 * {@link Event::getEventInterface()}
	 *
	 * @var string
	 * @access private
	 */
	private $__eventinterface;
	
	/**
	 * Array of listeners
	 * {@link Event::__eventinterface}
	 * {@link Event::isListener()}
	 * {@link Event::addListener()}
	 * {@link Event::removeListener()}
	 *
	 * @var string
	 * @access private
	 */
	private $__listeners = array();
	
	
	/**
	 * Static private object of EventCaller, to call every time new event
	 * is created.
	 * {@link Event::init()}
	 *
	 * @var object
	 * @access private
	 */
	private static $__eventcreatedcaller;
	
	
	/**
	 * Flag if to rethrow the exceptions
	 * {@link Event::factory()}
	 * {@link Event::throwExceptions()}
	 *
	 * @var bool
	 * @access private
	 */
	private static $__throwexceptions = false;
	
	/**
	 * Private constructor, singleton pattern.
	 * The aim is to construct the object and save it in the
	 * {@link Event::$__events} array, giving only the key
	 * {@link Event::factory()}
	 *
	 * @param string $name
	 * @param int $access
	 * @param object|string $caller
	 * @param object|string $dataset
	 * @param bool $disposable
	 * @param string $eventinterface
	 */
	private function __construct($name, $access, $caller, $dataset, $disposable, $eventinterface)
	{
		if (is_string($name) AND $name != '' AND !isset(self::$__events[$name]))
		{
			switch ($access)
			{
				case self::E_PRIVATE :
					
					$this->__eventname = $name;
					
					if($caller instanceof EventCaller)
					{
						$this->__access = self::E_PRIVATE ;
						$this->__caller = $caller;
						if (!$caller->bind($name)) throw new ACE_Exception('No unique $caller given');
					}
					else throw new ACE_Exception('$caller has to be instance of EventCaller or one of it\'s descendants');
					
					if ($dataset === null) $this->__dataset = null;
					elseif (is_object($dataset)) $this->__dataset = get_class($dataset);
					elseif (is_string($dataset) AND class_exists($dataset)) $this->__dataset = $dataset;
					else throw new ACE_Exception('$dataset must be either null or an object or an existing class name');
					
					if (is_bool($disposable)) $this->__disposable = ($disposable ? true : false);
					else throw new ACE_Exception('$disposable must be boolean');

					if (interface_exists($eventinterface) AND in_array('on'.$name.'Call',get_class_methods($eventinterface),true) AND in_array('on'.$name.'Destroy',get_class_methods($eventinterface),true)) $this->__eventinterface = $eventinterface;
					else throw new ACE_Exception('$eventinterface has to be an existing interface and define the on'.$name.'Call and on'.$name.'Destroy method');
					
					if ($name != 'EventCreated') self::call('EventCreated',self::$__eventcreatedcaller,new EventInfo($name,$access,'EventCaller',$dataset,$disposable,$eventinterface));
					
					break;
										
				case self::E_PROTECTED :
					
					$this->__eventname = $name;
					
					if(is_object($caller) AND $caller instanceof EventCaller)
					{
						$this->__access = self::E_PROTECTED ;
						$this->__caller = get_class($caller);
					}
					elseif (is_string($caller) AND ($caller == 'EventCaller' OR is_subclass_of($caller,'EventCaller')))
					{
						$this->__access = self::E_PROTECTED ;
						$this->__caller = $caller;
					}
					else throw new ACE_Exception('$caller has to be either instance of EventCaller or it\'s descendatns or appropriate classname string');

					if ($dataset == null) $this->__dataset = null;
					elseif (is_object($dataset)) $this->__dataset = get_class($dataset);
					elseif (is_string($dataset) AND class_exists($dataset)) $this->__dataset = $dataset;
					else throw new ACE_Exception('$dataset must be either null or an object or an existing class name');
					
					if (is_bool($disposable)) $this->__disposable = ($disposable ? true : false);
					else throw new ACE_Exception('$disposable must be boolean');
					
					if (interface_exists($eventinterface) AND in_array('on'.$name.'Call',get_class_methods($eventinterface),true) AND in_array('on'.$name.'Destroy',get_class_methods($eventinterface),true)) $this->__eventinterface = $eventinterface;
					else throw new ACE_Exception('$eventinterface has to be existing interface and define the on.$name.Call and on.$name.Destroy method');
					
					if ($name != 'EventCreated') self::call('EventCreated',self::$__eventcreatedcaller,new EventInfo($name,$access,'EventCaller',$dataset,$disposable,$eventinterface));
					
					break;
					
				case self::E_PUBLIC :
					
					$this->__eventname = $name;
					
					$this->__caller = 'EventCaller';
					$this->__access = self::E_PUBLIC ;

					if ($dataset == null) $this->__dataset = null;
					elseif (is_object($dataset)) $this->__dataset = get_class($dataset);
					elseif (is_string($dataset) AND class_exists($dataset)) $this->__dataset = $dataset;
					else throw new ACE_Exception('$dataset must be either null or an object or an existing class name');
					
					if (is_bool($disposable)) $this->__disposable = ($disposable ? true : false);
					else throw new ACE_Exception('$disposable must be boolean');
					
					if (interface_exists($eventinterface) AND in_array('on'.$name.'Call',get_class_methods($eventinterface),true) AND in_array('on'.$name.'Destroy',get_class_methods($eventinterface),true)) $this->__eventinterface = $eventinterface;
					else throw new ACE_Exception('$eventinterface has to be existing interface and define the on.$name.Call and on.$name.Destroy method');
					
					if ($name != 'EventCreated') self::call('EventCreated',self::$__eventcreatedcaller,new EventInfo($name,$access,'EventCaller',$dataset,$disposable,$eventinterface));
					
					break;
					
				default:
					throw new ACE_Exception('$access must be Event::E_PRIVATE, E_PROTECTED or E_PUBLIC');
			}
			self::$__events[$name] = $this;
		}
		else throw new ACE_Exception('Event already exists or $name is not a string!');
	}

	/**
	 * Private cloner, singleton pattern.
	 */
        private function __clone()
        {}

	
	/**
	 * Public factory method to create event and save it in the 
	 * {@link Event::$__events} array.
	 * {@link Event::__construct}
	 *
	 * @param string $name
	 * @param int $access
	 * @param object|string $caller
	 * @param object|string $dataset
	 * @param bool $disposable
	 * @param string $eventinterface
	 * @return bool|string
	 */
	
	public static function factory($name, $access = self::E_PUBLIC, $caller = null, $dataset = null, $disposable = true, $eventinterface = null)
	{
		self::init();
		if ($eventinterface == null) $eventinterface = 'i'.$name.'Event';
		try {
			new Event($name,$access,$caller, $dataset, $disposable,$eventinterface);
		} catch (ACE_Exception $e) {
			if (self::$__throwexceptions == true) throw $e;
			return false;
		} return $name;
	}
	
	/**
	 * Static function to get access flag of event $name
	 * {@link Event::E_PRIVATE}, {@link Event::E_PROTECTED},
	 * {@link Event::E_PUBLIC}
	 * {@link Event::__access}
	 * 
	 * @param string $name
	 * @return int
	 */
	public static function getAccess($name)
	{
		return self::$__events[$name]->__access;
	}
	
	/**
	 * Static function to get caller class. If event is {@link Event::E_PRIVATE} returns
	 * null, else returns classname of caller.
	 * {@link Event::__caller}
	 *
	 * @param unknown_type $name
	 * @return null|string
	 */
	public static function getCallerClass($name)
	{
		if (is_string(self::$__events[$name]->__caller)) return self::$__events[$name]->__caller;
		return null;
	}
	
	/**
	 * Static function to determine if the $caller parameter is
	 * able to call event. If Event is {@link Event::E_PRIVATE},
	 * $caller has to be object
	 * {@link Event::__caller}
	 *
	 * @param string $name
	 * @param string|object $caller
	 * @return bool
	 */
	public static function isCaller($name,$caller)
	{
		if (isset(self::$__events[$name]))
		{
			if (is_object(self::$__events[$name]->__caller)) return (self::$__events[$name]->__caller === $caller);
			if (is_object($caller)) return ($caller instanceof self::$__events[$name]->__caller);
			return (self::$__events[$name]->__caller == $caller OR is_subclass_of($caller,self::$__events[$name]->__caller));
		}
		return false;
	}
	
	/**
	 * Static function to determine if event $name exists
	 * {@link Event::__events}
	 *
	 * @param string $name
	 * @return bool
	 */
	public static function eventExists($name)
	{
		if(isset(self::$__events[$name])) return true;
		return false;
	}
	
	/**
	 * Function to inform Event that caller is just to be destroyed.
	 * ALWAYS EXPLICITLY call this function if your event is {@link Event::E_PRIVATE}
	 * as the {@link Event::__caller} is a reference to the caller object, so 
	 * unsetting or $caller = null will not work for destroying object.
	 *
	 * @param unknown_type $caller
	 * @param unknown_type $name
	 * @return unknown
	 */
	public static function callerDestroyed($caller,$name)
	{
		if (isset(self::$__events[$name]) AND self::$__events[$name]->__access == self::E_PRIVATE AND self::$__events[$name]->__caller === $caller)
		{
			unset(self::$__events[$name]);
			return true;
		}
		return false;
	}
	
	/**
	 * Static function to determine dataset class.
	 * {@link Event::__dataset}
	 *
	 * @param string $name
	 * @return null|object
	 */
	public static function getDataset($name)
	{
		return self::$__events[$name]->__dataset;
	}
	
	/**
	 * Static function to determine if the event should be destroyed
	 * after {@link Event::call()}ing
	 * {@link Event::__disposable}
	 *
	 * @param string $name
	 * @return bool
	 */
	public static function isDisposable($name)
	{
		return self::$__events[$name]->__disposable;
	}

	/**
	 * Static function to determine the listener's interface.
	 * Only objects of classes that implement this interface can be
	 * added to {@link Event::__listeners} array.
	 * {@link Event::__eventinterface}
	 *
	 * @param string $name
	 * @return string
	 */
	public static function getEventInterface($name)
	{
		return self::$__events[$name]->__eventinterface;
	}

	/**
	 * Static function to determine if the provided object
	 * is a listener. Returns int if the object is a listener. 
	 * CAUTION! It CAN RETURN _0_ which means that object IS the listener
	 * of the event, and has index 0 (first element in array).
	 * USE === when comparing to null or false.
	 * If the $listener object IS NOT a listener, return null
	 * If the $name event doesn't exist, return false
	 * {@link Event::__listeners}
	 *
	 * @param string $name
	 * @param object $listener
	 * @return bool|null|int
	 */
	public function isListener($name,$listener)
	{
		if (isset(self::$__events[$name]) AND $listener instanceof self::$__events[$name]->__eventinterface)
		{
			foreach (self::$__events[$name]->__listeners as $key => $val)
			{
				if ($listener === $val) return $key;
			}
			return null;
		}
		return false;	
	}
	
	/**
	 * Static function to add listener to an object name.
	 * $listener class must implement the {@link Event::__eventinterface}
	 * interface.
	 * {@link Event::__listeners}
	 *
	 * @param string $name
	 * @param object $listener
	 * @return bool
	 */
	public static function addListener($name,$listener)
	{
		if (self::isListener($name,$listener) === null)
		{
			self::$__events[$name]->__listeners[] = $listener;
			return true;
		}
		return false;
	}
	
	/**
	 * Static function to remove listener from
	 * {@link Event::__listeners} array.
	 * {@link Event::__listeners}
	 *
	 * @param string $name
	 * @param object $listener
	 * @return true
	 */
	public static function removeListener($name,$listener)
	{
		$key = self::isListener($name,$listener);
		if (is_int($key))
		{
			unset(self::$__events[$name]->__listeners[$key]);
			return true;
		}
		return false;
	}
	
	/**
	 * Static function to call an event.
	 * {@link Event::__listeners}
	 *
	 * @param string $name
	 * @param object $caller
	 * @param object $dataset
	 * @return bool
	 */
	public static function call($name,$caller,$dataset = null)
	{
		if (isset(self::$__events[$name]))
		{
			if (self::$__events[$name]->__dataset != null and !($dataset instanceof self::$__events[$name]->__dataset)) throw new ACE_Exception('$dataset must be instance of '.self::$__events[$name]->__dataset);
			if (self::$__events[$name]->__dataset == null) $dataset = null;
			
			switch (self::$__events[$name]->__access)
			{
				case self::E_PRIVATE :
					if (self::$__events[$name]->__caller === $caller)
					{
						$call = 'on'.$name.'Call';
						$result = true;
						foreach (self::$__events[$name]->__listeners as $val) if ($val instanceof self::$__events[$name]->__eventinterface AND !($val->$call($dataset))) $result = false;
						if (self::$__events[$name]->__disposable) unset(self::$__events[$name]);
						return $result;
					}
					else throw new ACE_Exception('$caller is not a valid caller');
					break;
				case self::E_PROTECTED :
				case self::E_PUBLIC :
					if ($caller instanceof self::$__events[$name]->__caller)
					{
						$call = 'on'.$name.'Call';
						$result = true;
						foreach (self::$__events[$name]->__listeners as $val) if ($val instanceof self::$__events[$name]->__eventinterface AND !($val->$call($dataset))) $result = false;
						if (self::$__events[$name]->__disposable) unset(self::$__events[$name]);
						return $result;
					}
					else throw new ACE_Exception('$caller is not a valid caller');
					break;
			}
		}
		else throw new ACE_Exception('$name is not a valid event');
	}
	
	/**
	 * Initialisation function, initialises the EventCreated Event.
	 * Can also set wether to re-throw exceptions or not.
	 * {@link Event::__eventcreatedcaller}
	 *
	 * @param bool $te
	 * @return string|bool
	 */
	public static function init($te = null)
	{
		if (!isset(self::$__events['EventCreated']))
		{
			if (is_bool($te) AND $te == true) self::$__throwexceptions = true;
			self::$__eventcreatedcaller = new EventCaller();
			try {
				new Event('EventCreated',self::E_PRIVATE ,self::$__eventcreatedcaller,'EventInfo',false,'iEventCreatedEvent');
			} catch (ACE_Exception $e) {
				if (self::$__throwexceptions == true) throw $e;
				return false;
			} return $name;
		}
		return false;
	}
	
	/**
	 * Static function to set {@link Event::__throwexceptions} flag.
	 * {@link Event::__throwexceptions}
	 * 
	 * @param bool $te
	 * @return bool
	 */
	public static function throwExceptions($te = null)
	{
		if (is_bool($te))
		{
			self::$__throwexceptions = $te;
			return true;
		}
		return false;
	}
	
	/**
	 * Static function for Event destruction.
	 * {@link Event::E_PRIVATE} Events can be destroyed only by "their",
	 * proper callers. {@link Event::E_PROTECTED} by the object of a 
	 * {@link Event::__caller} class, or one of it's descendants.
	 * {@link Event::E_PUBLIC} events can be destroyed by EventCaller objects.
	 * See {@link Event::call()}, {@link Event::__destruct()}, {@link Event::callerDestroyed()}.
	 * 
	 * @param string $name
	 * @param object $caller
	 * @return bool
	 */
	public static function destroyEvent($name, $caller)
	{
		if (isset(self::$__events[$name]))
		{
			switch (self::$__events[$name]->__access)
			{
				case self::E_PRIVATE :
					if (self::$__events[$name]->__caller === $caller)
					{
						unset(self::$__events[$name]);
						return true;
					}
					return false;

				case self::E_PROTECTED :
				case self::E_PUBLIC :
					if ($caller instanceof self::$__events[$name]->__caller)
					{
						unset(self::$$__events[$name]);
						return true;
					}
					return false;
			}
		}
		return false;
	}
	
	/**
	 * Destructor class, called on unsetting event in
	 * {@link Event::__events} 
	 *
	 * @return unknown
	 */
	public function __destruct()
	{
		$call = 'on'.$this->__eventname.'Destroy';
		$result = true;
		foreach ($this->__listeners as $val) if ($val instanceof $this->__eventinterface AND !($val->$call())) $result = false;
		return $result;
	}
}

interface iEventCreatedEvent
{
	public function onEventCreatedCall($dataset);
	public function onEventCreatedDestroy();
}

?>
Return current item: Event