<?php
/**
* Part of the Joomla Framework Event Package
*
* @copyright Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE
*/
namespace Joomla\Event;
/**
* Implementation of a DispatcherInterface supporting prioritized listeners.
*
* @since 1.0
*/
class Dispatcher implements DispatcherInterface
{
/**
* An array of registered events indexed by the event names.
*
* @var EventInterface[]
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
protected $events = [];
/**
* An array of ListenersPriorityQueue indexed by the event names.
*
* @var ListenersPriorityQueue[]
* @since 1.0
*/
protected $listeners = [];
/**
* Set an event to the dispatcher. It will replace any event with the same name.
*
* @param EventInterface $event The event.
*
* @return $this
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function setEvent(EventInterface $event)
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
$this->events[$event->getName()] = $event;
return $this;
}
/**
* Add an event to this dispatcher, only if it is not existing.
*
* @param EventInterface $event The event.
*
* @return $this
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function addEvent(EventInterface $event)
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
if (!isset($this->events[$event->getName()]))
{
$this->events[$event->getName()] = $event;
}
return $this;
}
/**
* Tell if the given event has been added to this dispatcher.
*
* @param EventInterface|string $event The event object or name.
*
* @return boolean True if the listener has the given event, false otherwise.
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function hasEvent($event)
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
if ($event instanceof EventInterface)
{
$event = $event->getName();
}
return isset($this->events[$event]);
}
/**
* Get the event object identified by the given name.
*
* @param string $name The event name.
* @param mixed $default The default value if the event was not registered.
*
* @return EventInterface|mixed The event of the default value.
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function getEvent($name, $default = null)
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
if (isset($this->events[$name]))
{
return $this->events[$name];
}
return $default;
}
/**
* Remove an event from this dispatcher. The registered listeners will remain.
*
* @param EventInterface|string $event The event object or name.
*
* @return $this
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function removeEvent($event)
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
if ($event instanceof EventInterface)
{
$event = $event->getName();
}
if (isset($this->events[$event]))
{
unset($this->events[$event]);
}
return $this;
}
/**
* Get the registered events.
*
* @return EventInterface[] The registered event.
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function getEvents()
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
return $this->events;
}
/**
* Clear all events.
*
* @return EventInterface[] The old events.
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function clearEvents()
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
$events = $this->events;
$this->events = [];
return $events;
}
/**
* Count the number of registered event.
*
* @return integer The numer of registered events.
*
* @since 1.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
public function countEvents()
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0.',
__METHOD__
);
return \count($this->events);
}
/**
* Attaches a listener to an event
*
* @param string $eventName The event to listen to.
* @param callable $callback A callable function
* @param integer $priority The priority at which the $callback executed
*
* @return boolean
*
* @since 1.0
*/
public function addListener(string $eventName, callable $callback, int $priority = 0): bool
{
if (!isset($this->listeners[$eventName]))
{
$this->listeners[$eventName] = new ListenersPriorityQueue;
}
$this->listeners[$eventName]->add($callback, $priority);
return true;
}
/**
* Get the priority of the given listener for the given event.
*
* @param string $eventName The event to listen to.
* @param callable $callback A callable function
*
* @return mixed The listener priority or null if the listener doesn't exist.
*
* @since 1.0
*/
public function getListenerPriority($eventName, callable $callback)
{
if (isset($this->listeners[$eventName]))
{
return $this->listeners[$eventName]->getPriority($callback);
}
}
/**
* Get the listeners registered to the given event.
*
* @param string|null $event The event to fetch listeners for or null to fetch all listeners
*
* @return callable[] An array of registered listeners sorted according to their priorities.
*
* @since 1.0
*/
public function getListeners(?string $event = null)
{
if ($event !== null)
{
if (isset($this->listeners[$event]))
{
return $this->listeners[$event]->getAll();
}
return [];
}
$dispatcherListeners = [];
foreach ($this->listeners as $registeredEvent => $listeners)
{
$dispatcherListeners[$registeredEvent] = $listeners->getAll();
}
return $dispatcherListeners;
}
/**
* Tell if the given listener has been added.
*
* If an event is specified, it will tell if the listener is registered for that event.
*
* @param callable $callback The callable to check is listening to the event.
* @param string $eventName An optional event name to check a listener is subscribed to.
*
* @return boolean True if the listener is registered, false otherwise.
*
* @since 1.0
*/
public function hasListener(callable $callback, ?string $eventName = null)
{
if ($eventName)
{
if (isset($this->listeners[$eventName]))
{
return $this->listeners[$eventName]->has($callback);
}
}
else
{
foreach ($this->listeners as $queue)
{
if ($queue->has($callback))
{
return true;
}
}
}
return false;
}
/**
* Removes an event listener from the specified event.
*
* @param string $eventName The event to remove a listener from.
* @param callable $listener The listener to remove.
*
* @return void
*
* @since 2.0.0
*/
public function removeListener(string $eventName, callable $listener): void
{
if (isset($this->listeners[$eventName]))
{
$this->listeners[$eventName]->remove($listener);
}
}
/**
* Clear the listeners in this dispatcher.
*
* If an event is specified, the listeners will be cleared only for that event.
*
* @param string $event The event name.
*
* @return $this
*
* @since 1.0
*/
public function clearListeners($event = null)
{
if ($event)
{
if (isset($this->listeners[$event]))
{
unset($this->listeners[$event]);
}
}
else
{
$this->listeners = [];
}
return $this;
}
/**
* Count the number of registered listeners for the given event.
*
* @param string $event The event name.
*
* @return integer
*
* @since 1.0
*/
public function countListeners($event)
{
return isset($this->listeners[$event]) ? \count($this->listeners[$event]) : 0;
}
/**
* Adds an event subscriber.
*
* @param SubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since 2.0.0
*/
public function addSubscriber(SubscriberInterface $subscriber): void
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params)
{
if (\is_array($params))
{
$this->addListener($eventName, [$subscriber, $params[0]], $params[1] ?? Priority::NORMAL);
}
else
{
$this->addListener($eventName, [$subscriber, $params]);
}
}
}
/**
* Removes an event subscriber.
*
* @param SubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since 2.0.0
*/
public function removeSubscriber(SubscriberInterface $subscriber): void
{
foreach ($subscriber->getSubscribedEvents() as $eventName => $params)
{
if (\is_array($params))
{
$this->removeListener($eventName, [$subscriber, $params[0]]);
}
else
{
$this->removeListener($eventName, [$subscriber, $params]);
}
}
}
/**
* Dispatches an event to all registered listeners.
*
* @param string $name The name of the event to dispatch.
* @param EventInterface $event The event to pass to the event handlers/listeners.
* If not supplied, an empty EventInterface instance is created.
* Note, not passing an event is deprecated and will be required as of 3.0.
*
* @return EventInterface
*
* @since 2.0.0
*/
public function dispatch(string $name, ?EventInterface $event = null): EventInterface
{
if (!($event instanceof EventInterface))
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'Not passing an event object to %s() is deprecated, as of 3.0 the $event argument will be required.',
__METHOD__
);
$event = $this->getDefaultEvent($name);
}
if (isset($this->listeners[$event->getName()]))
{
foreach ($this->listeners[$event->getName()] as $listener)
{
if ($event->isStopped())
{
return $event;
}
$listener($event);
}
}
return $event;
}
/**
* Trigger an event.
*
* @param EventInterface|string $event The event object or name.
*
* @return EventInterface The event after being passed through all listeners.
*
* @since 1.0
* @deprecated 3.0 Use dispatch() instead.
*/
public function triggerEvent($event)
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0, use %s::dispatch() instead.',
__METHOD__,
DispatcherInterface::class
);
if (!($event instanceof EventInterface))
{
$event = $this->getDefaultEvent($event);
}
return $this->dispatch($event->getName(), $event);
}
/**
* Get an event object for the specified event name
*
* @param string $name The event name to get an EventInterface object for
*
* @return EventInterface
*
* @since 2.0.0
* @deprecated 3.0 Default event objects will no longer be supported
*/
private function getDefaultEvent(string $name): EventInterface
{
if (isset($this->events[$name]))
{
return $this->events[$name];
}
return new Event($name);
}
}