278 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			278 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @package     FrameworkOnFramework
 | |
|  * @subpackage  utils
 | |
|  * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba Ltd. All rights reserved.
 | |
|  * @license     GNU General Public License version 2 or later; see LICENSE.txt
 | |
|  */
 | |
| 
 | |
| defined('F0F_INCLUDED') or die;
 | |
| 
 | |
| /**
 | |
|  * Class to handle dispatching of events.
 | |
|  *
 | |
|  * This is the Observable part of the Observer design pattern
 | |
|  * for the event architecture.
 | |
|  *
 | |
|  * This class is based on JEventDispatcher as found in Joomla! 3.2.0
 | |
|  */
 | |
| class F0FUtilsObservableDispatcher extends F0FUtilsObject
 | |
| {
 | |
|     /**
 | |
|      * An array of Observer objects to notify
 | |
|      *
 | |
|      * @var    array
 | |
|      */
 | |
|     protected $_observers = array();
 | |
| 
 | |
|     /**
 | |
|      * The state of the observable object
 | |
|      *
 | |
|      * @var    mixed
 | |
|      */
 | |
|     protected $_state = null;
 | |
| 
 | |
|     /**
 | |
|      * A multi dimensional array of [function][] = key for observers
 | |
|      *
 | |
|      * @var    array
 | |
|      */
 | |
|     protected $_methods = array();
 | |
| 
 | |
|     /**
 | |
|      * Stores the singleton instance of the dispatcher.
 | |
|      *
 | |
|      * @var    F0FUtilsObservableDispatcher
 | |
|      */
 | |
|     protected static $instance = null;
 | |
| 
 | |
|     /**
 | |
|      * Returns the global Event Dispatcher object, only creating it
 | |
|      * if it doesn't already exist.
 | |
|      *
 | |
|      * @return  F0FUtilsObservableDispatcher  The EventDispatcher object.
 | |
|      */
 | |
|     public static function getInstance()
 | |
|     {
 | |
|         if (self::$instance === null)
 | |
|         {
 | |
|             self::$instance = new static;
 | |
|         }
 | |
| 
 | |
|         return self::$instance;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the state of the F0FUtilsObservableDispatcher object
 | |
|      *
 | |
|      * @return  mixed    The state of the object.
 | |
|      */
 | |
|     public function getState()
 | |
|     {
 | |
|         return $this->_state;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Registers an event handler to the event dispatcher
 | |
|      *
 | |
|      * @param   string  $event    Name of the event to register handler for
 | |
|      * @param   string  $handler  Name of the event handler
 | |
|      *
 | |
|      * @return  void
 | |
|      *
 | |
|      * @throws  InvalidArgumentException
 | |
|      */
 | |
|     public function register($event, $handler)
 | |
|     {
 | |
|         // Are we dealing with a class or callback type handler?
 | |
|         if (is_callable($handler))
 | |
|         {
 | |
|             // Ok, function type event handler... let's attach it.
 | |
|             $method = array('event' => $event, 'handler' => $handler);
 | |
|             $this->attach($method);
 | |
|         }
 | |
|         elseif (class_exists($handler))
 | |
|         {
 | |
|             // Ok, class type event handler... let's instantiate and attach it.
 | |
|             $this->attach(new $handler($this));
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             throw new InvalidArgumentException('Invalid event handler.');
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Triggers an event by dispatching arguments to all observers that handle
 | |
|      * the event and returning their return values.
 | |
|      *
 | |
|      * @param   string  $event  The event to trigger.
 | |
|      * @param   array   $args   An array of arguments.
 | |
|      *
 | |
|      * @return  array  An array of results from each function call.
 | |
|      */
 | |
|     public function trigger($event, $args = array())
 | |
|     {
 | |
|         $result = array();
 | |
| 
 | |
|         /*
 | |
|          * If no arguments were passed, we still need to pass an empty array to
 | |
|          * the call_user_func_array function.
 | |
|          */
 | |
|         $args = (array) $args;
 | |
| 
 | |
|         $event = strtolower($event);
 | |
| 
 | |
|         // Check if any plugins are attached to the event.
 | |
|         if (!isset($this->_methods[$event]) || empty($this->_methods[$event]))
 | |
|         {
 | |
|             // No Plugins Associated To Event!
 | |
|             return $result;
 | |
|         }
 | |
| 
 | |
|         // Loop through all plugins having a method matching our event
 | |
|         foreach ($this->_methods[$event] as $key)
 | |
|         {
 | |
|             // Check if the plugin is present.
 | |
|             if (!isset($this->_observers[$key]))
 | |
|             {
 | |
|                 continue;
 | |
|             }
 | |
| 
 | |
|             // Fire the event for an object based observer.
 | |
|             if (is_object($this->_observers[$key]))
 | |
|             {
 | |
|                 $args['event'] = $event;
 | |
|                 $value = $this->_observers[$key]->update($args);
 | |
|             }
 | |
|             // Fire the event for a function based observer.
 | |
|             elseif (is_array($this->_observers[$key]))
 | |
|             {
 | |
|                 $value = call_user_func_array($this->_observers[$key]['handler'], $args);
 | |
|             }
 | |
| 
 | |
|             if (isset($value))
 | |
|             {
 | |
|                 $result[] = $value;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $result;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Attach an observer object
 | |
|      *
 | |
|      * @param   object  $observer  An observer object to attach
 | |
|      *
 | |
|      * @return  void
 | |
|      */
 | |
|     public function attach($observer)
 | |
|     {
 | |
|         if (is_array($observer))
 | |
|         {
 | |
|             if (!isset($observer['handler']) || !isset($observer['event']) || !is_callable($observer['handler']))
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             // Make sure we haven't already attached this array as an observer
 | |
|             foreach ($this->_observers as $check)
 | |
|             {
 | |
|                 if (is_array($check) && $check['event'] == $observer['event'] && $check['handler'] == $observer['handler'])
 | |
|                 {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             $this->_observers[] = $observer;
 | |
|             end($this->_observers);
 | |
|             $methods = array($observer['event']);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             if (!($observer instanceof F0FUtilsObservableEvent))
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             // Make sure we haven't already attached this object as an observer
 | |
|             $class = get_class($observer);
 | |
| 
 | |
|             foreach ($this->_observers as $check)
 | |
|             {
 | |
|                 if ($check instanceof $class)
 | |
|                 {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             $this->_observers[] = $observer;
 | |
| 
 | |
|             // Required in PHP 7 since foreach() doesn't advance the internal array counter, see http://php.net/manual/en/migration70.incompatible.php
 | |
|             end($this->_observers);
 | |
| 
 | |
|             $methods = array();
 | |
| 
 | |
|             foreach(get_class_methods($observer) as $obs_method)
 | |
|             {
 | |
|                 // Magic methods are not allowed
 | |
|                 if(strpos('__', $obs_method) === 0)
 | |
|                 {
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 $methods[] = $obs_method;
 | |
|             }
 | |
| 
 | |
|             //$methods = get_class_methods($observer);
 | |
|         }
 | |
| 
 | |
|         $key = key($this->_observers);
 | |
| 
 | |
|         foreach ($methods as $method)
 | |
|         {
 | |
|             $method = strtolower($method);
 | |
| 
 | |
|             if (!isset($this->_methods[$method]))
 | |
|             {
 | |
|                 $this->_methods[$method] = array();
 | |
|             }
 | |
| 
 | |
|             $this->_methods[$method][] = $key;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Detach an observer object
 | |
|      *
 | |
|      * @param   object  $observer  An observer object to detach.
 | |
|      *
 | |
|      * @return  boolean  True if the observer object was detached.
 | |
|      */
 | |
|     public function detach($observer)
 | |
|     {
 | |
|         $retval = false;
 | |
| 
 | |
|         $key = array_search($observer, $this->_observers);
 | |
| 
 | |
|         if ($key !== false)
 | |
|         {
 | |
|             unset($this->_observers[$key]);
 | |
|             $retval = true;
 | |
| 
 | |
|             foreach ($this->_methods as &$method)
 | |
|             {
 | |
|                 $k = array_search($key, $method);
 | |
| 
 | |
|                 if ($k !== false)
 | |
|                 {
 | |
|                     unset($method[$k]);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $retval;
 | |
|     }
 | |
| }
 |