ï»¿PK     ò¢!]V-§g  g    Dispatcher.phpnu &1i„        <?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;
	}
} 
PK     ò¢!]*t!óæ  æ    Observer.phpnu &1i„        <?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 ReflectionMethod;
use ReflectionObject;

class Observer
{
	/** @var   Observable  The object to observe */
	protected $subject;

	protected $events;

	/**
	 * Creates the observer and attaches it to the observable subject object
	 *
	 * @param   Observable  $subject  The observable object to attach the observer to
	 */
	function __construct(Observable &$subject)
	{
		// Attach this observer to the subject
		$subject->attach($this);

		// Store a reference to the subject object
		$this->subject = $subject;
	}

	/**
	 * Returns the list of events observable by this observer. Set the $this->events array manually for faster
	 * processing, or let this method use reflection to return a list of all public methods.
	 *
	 * @return  array
	 */
	public function getObservableEvents()
	{
		if (is_null($this->events))
		{
			// Assign an empty array to protect us from behaviours without any valid method
			$this->events = [];

			$reflection = new ReflectionObject($this);
			$methods    = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);

			foreach ($methods as $m)
			{
				if ($m->name == 'getObservableEvents')
				{
					continue;
				}

				if ($m->name == '__construct')
				{
					continue;
				}

				$this->events[] = $m->name;
			}
		}

		return $this->events;
	}
}
PK     ò¢!]»N„Á  Á    Observable.phpnu &1i„        <?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;

/**
 * Interface Observable
 *
 * @codeCoverageIgnore
 */
interface Observable
{
	/**
	 * Attaches an observer to the object
	 *
	 * @param Observer $observer The observer to attach
	 *
	 * @return  static  Ourselves, for chaining
	 */
	public function attach(Observer $observer);

	/**
	 * Detaches an observer from the object
	 *
	 * @param Observer $observer The observer to detach
	 *
	 * @return  static  Ourselves, for chaining
	 */
	public function detach(Observer $observer);

	/**
	 * 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;
} 
PK       ò¢!]V-§g  g                  Dispatcher.phpnu &1i„        PK       ò¢!]*t!óæ  æ              ¥  Observer.phpnu &1i„        PK       ò¢!]»N„Á  Á              Ç  Observable.phpnu &1i„        PK      è   Æ# 