318 lines
7.0 KiB
PHP
318 lines
7.0 KiB
PHP
<?php
|
|
/**
|
|
* @package FOF
|
|
* @copyright Copyright (c)2010-2021 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU General Public License version 2, or later
|
|
*/
|
|
|
|
namespace FOF30\Event;
|
|
|
|
defined('_JEXEC') || die;
|
|
|
|
use FOF30\Container\Container;
|
|
|
|
class Dispatcher implements Observable
|
|
{
|
|
/** @var Container The container this event dispatcher is attached to */
|
|
protected $container = null;
|
|
|
|
/** @var array The observers attached to the dispatcher */
|
|
protected $observers = [];
|
|
|
|
/** @var array Maps events to observers */
|
|
protected $events = [];
|
|
|
|
/**
|
|
* Public constructor
|
|
*
|
|
* @param Container $container The container this event dispatcher is attached to
|
|
*/
|
|
public function __construct(Container $container)
|
|
{
|
|
$this->container = $container;
|
|
}
|
|
|
|
/**
|
|
* Returns the container this event dispatcher is attached to
|
|
*
|
|
* @return Container
|
|
*/
|
|
public function getContainer()
|
|
{
|
|
return $this->container;
|
|
}
|
|
|
|
/**
|
|
* Attaches an observer to the object
|
|
*
|
|
* @param Observer $observer The observer to attach
|
|
*
|
|
* @return Dispatcher Ourselves, for chaining
|
|
*/
|
|
public function attach(Observer $observer)
|
|
{
|
|
$className = get_class($observer);
|
|
|
|
// Make sure this observer is not already registered
|
|
if (isset($this->observers[$className]))
|
|
{
|
|
return $this;
|
|
}
|
|
|
|
// Attach observer
|
|
$this->observers[$className] = $observer;
|
|
|
|
// Register the observable events
|
|
$events = $observer->getObservableEvents();
|
|
|
|
foreach ($events as $event)
|
|
{
|
|
$event = strtolower($event);
|
|
|
|
if (!isset($this->events[$event]))
|
|
{
|
|
$this->events[$event] = [$className];
|
|
}
|
|
else
|
|
{
|
|
$this->events[$event][] = $className;
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Detaches an observer from the object
|
|
*
|
|
* @param Observer $observer The observer to detach
|
|
*
|
|
* @return Dispatcher Ourselves, for chaining
|
|
*/
|
|
public function detach(Observer $observer)
|
|
{
|
|
$className = get_class($observer);
|
|
|
|
// Make sure this observer is already registered
|
|
if (!isset($this->observers[$className]))
|
|
{
|
|
return $this;
|
|
}
|
|
|
|
// Unregister the observable events
|
|
$events = $observer->getObservableEvents();
|
|
|
|
foreach ($events as $event)
|
|
{
|
|
$event = strtolower($event);
|
|
|
|
if (isset($this->events[$event]))
|
|
{
|
|
$key = array_search($className, $this->events[$event]);
|
|
|
|
if ($key !== false)
|
|
{
|
|
unset($this->events[$event][$key]);
|
|
|
|
if (empty($this->events[$event]))
|
|
{
|
|
unset ($this->events[$event]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detach observer
|
|
unset($this->observers[$className]);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Is an observer object already registered with this dispatcher?
|
|
*
|
|
* @param Observer $observer The observer to check if it's attached
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function hasObserver(Observer $observer)
|
|
{
|
|
$className = get_class($observer);
|
|
|
|
return $this->hasObserverClass($className);
|
|
}
|
|
|
|
/**
|
|
* Is there an observer of the specified class already registered with this dispatcher?
|
|
*
|
|
* @param string $className The observer class name to check if it's attached
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function hasObserverClass($className)
|
|
{
|
|
return isset($this->observers[$className]);
|
|
}
|
|
|
|
/**
|
|
* Returns an observer attached to this behaviours dispatcher by its class name
|
|
*
|
|
* @param string $className The class name of the observer object to return
|
|
*
|
|
* @return null|Observer
|
|
*/
|
|
public function getObserverByClass($className)
|
|
{
|
|
if (!$this->hasObserverClass($className))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return $this->observers[$className];
|
|
}
|
|
|
|
/**
|
|
* Triggers an event in the attached observers
|
|
*
|
|
* @param string $event The event to attach
|
|
* @param array $args Arguments to the event handler
|
|
*
|
|
* @return array
|
|
*/
|
|
public function trigger($event, array $args = [])
|
|
{
|
|
$event = strtolower($event);
|
|
|
|
$result = [];
|
|
|
|
// Make sure the event is known to us, otherwise return an empty array
|
|
if (!isset($this->events[$event]) || empty($this->events[$event]))
|
|
{
|
|
return $result;
|
|
}
|
|
|
|
foreach ($this->events[$event] as $className)
|
|
{
|
|
// Make sure the observer exists.
|
|
if (!isset($this->observers[$className]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get the observer
|
|
$observer = $this->observers[$className];
|
|
|
|
// Make sure the method exists
|
|
if (!method_exists($observer, $event))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Call the event handler and add its output to the return value. The switch allows for execution up to 2x
|
|
// faster than using call_user_func_array
|
|
switch (count($args))
|
|
{
|
|
case 0:
|
|
$result[] = $observer->{$event}();
|
|
break;
|
|
case 1:
|
|
$result[] = $observer->{$event}($args[0]);
|
|
break;
|
|
case 2:
|
|
$result[] = $observer->{$event}($args[0], $args[1]);
|
|
break;
|
|
case 3:
|
|
$result[] = $observer->{$event}($args[0], $args[1], $args[2]);
|
|
break;
|
|
case 4:
|
|
$result[] = $observer->{$event}($args[0], $args[1], $args[2], $args[3]);
|
|
break;
|
|
case 5:
|
|
$result[] = $observer->{$event}($args[0], $args[1], $args[2], $args[3], $args[4]);
|
|
break;
|
|
default:
|
|
$result[] = call_user_func_array([$observer, $event], $args);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Return the observers' result in an array
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Asks each observer to handle an event based on the provided arguments. The first observer to return a non-null
|
|
* result wins. This is a *very* simplistic implementation of the Chain of Command pattern.
|
|
*
|
|
* @param string $event The event name to handle
|
|
* @param array $args The arguments to the event
|
|
*
|
|
* @return mixed Null if the event can't be handled by any observer
|
|
*/
|
|
public function chainHandle($event, $args = [])
|
|
{
|
|
$event = strtolower($event);
|
|
|
|
$result = null;
|
|
|
|
// Make sure the event is known to us, otherwise return an empty array
|
|
if (!isset($this->events[$event]) || empty($this->events[$event]))
|
|
{
|
|
return $result;
|
|
}
|
|
|
|
foreach ($this->events[$event] as $className)
|
|
{
|
|
// Make sure the observer exists.
|
|
if (!isset($this->observers[$className]))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Get the observer
|
|
$observer = $this->observers[$className];
|
|
|
|
// Make sure the method exists
|
|
if (!method_exists($observer, $event))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Call the event handler and add its output to the return value. The switch allows for execution up to 2x
|
|
// faster than using call_user_func_array
|
|
switch (count($args))
|
|
{
|
|
case 0:
|
|
$result = $observer->{$event}();
|
|
break;
|
|
case 1:
|
|
$result = $observer->{$event}($args[0]);
|
|
break;
|
|
case 2:
|
|
$result = $observer->{$event}($args[0], $args[1]);
|
|
break;
|
|
case 3:
|
|
$result = $observer->{$event}($args[0], $args[1], $args[2]);
|
|
break;
|
|
case 4:
|
|
$result = $observer->{$event}($args[0], $args[1], $args[2], $args[3]);
|
|
break;
|
|
case 5:
|
|
$result = $observer->{$event}($args[0], $args[1], $args[2], $args[3], $args[4]);
|
|
break;
|
|
default:
|
|
$result = call_user_func_array([$observer, $event], $args);
|
|
break;
|
|
}
|
|
|
|
if (!is_null($result))
|
|
{
|
|
return $result;
|
|
}
|
|
}
|
|
|
|
// Return the observers' result in an array
|
|
return $result;
|
|
}
|
|
}
|