primo commit
This commit is contained in:
291
libraries/fof40/Event/Dispatcher.php
Normal file
291
libraries/fof40/Event/Dispatcher.php
Normal file
@ -0,0 +1,291 @@
|
||||
<?php
|
||||
/**
|
||||
* @package FOF
|
||||
* @copyright Copyright (c)2010-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
||||
* @license GNU General Public License version 3, or later
|
||||
*/
|
||||
|
||||
namespace FOF40\Event;
|
||||
|
||||
defined('_JEXEC') || die;
|
||||
|
||||
use FOF40\Container\Container;
|
||||
|
||||
class Dispatcher implements Observable
|
||||
{
|
||||
/** @var Container The container this event dispatcher is attached to */
|
||||
protected $container;
|
||||
|
||||
/** @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(): Container
|
||||
{
|
||||
return $this->container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches an observer to the object
|
||||
*
|
||||
* @param Observer $observer The observer to attach
|
||||
*
|
||||
* @return static 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 static 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): bool
|
||||
{
|
||||
$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(string $className): bool
|
||||
{
|
||||
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(string $className): ?Observer
|
||||
{
|
||||
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(string $event, array $args = []): array
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
$result[] = $observer->{$event}(...$args);
|
||||
}
|
||||
|
||||
// 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(string $event, array $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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user