primo commit

This commit is contained in:
2024-12-17 17:34:10 +01:00
commit e650f8df99
16435 changed files with 2451012 additions and 0 deletions

View File

@ -0,0 +1,242 @@
<?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;
use ArrayAccess;
use Countable;
use Serializable;
/**
* Implementation of EventInterface.
*
* @since 1.0
*/
abstract class AbstractEvent implements EventInterface, ArrayAccess, Serializable, Countable
{
/**
* The event name.
*
* @var string
* @since 1.0
*/
protected $name;
/**
* The event arguments.
*
* @var array
* @since 1.0
*/
protected $arguments;
/**
* A flag to see if the event propagation is stopped.
*
* @var boolean
* @since 1.0
*/
protected $stopped = false;
/**
* Constructor.
*
* @param string $name The event name.
* @param array $arguments The event arguments.
*
* @since 1.0
*/
public function __construct($name, array $arguments = [])
{
$this->name = $name;
$this->arguments = $arguments;
}
/**
* Get the event name.
*
* @return string The event name.
*
* @since 1.0
*/
public function getName()
{
return $this->name;
}
/**
* Get an event argument value.
*
* @param string $name The argument name.
* @param mixed $default The default value if not found.
*
* @return mixed The argument value or the default value.
*
* @since 1.0
*/
public function getArgument($name, $default = null)
{
if (isset($this->arguments[$name])) {
return $this->arguments[$name];
}
return $default;
}
/**
* Tell if the given event argument exists.
*
* @param string $name The argument name.
*
* @return boolean True if it exists, false otherwise.
*
* @since 1.0
*/
public function hasArgument($name)
{
return isset($this->arguments[$name]);
}
/**
* Get all event arguments.
*
* @return array An associative array of argument names as keys and their values as values.
*
* @since 1.0
*/
public function getArguments()
{
return $this->arguments;
}
/**
* Tell if the event propagation is stopped.
*
* @return boolean True if stopped, false otherwise.
*
* @since 1.0
*/
public function isStopped()
{
return $this->stopped === true;
}
/**
* Stops the propagation of the event to further event listeners.
*
* @return void
*
* @since 2.0.0
*/
public function stopPropagation(): void
{
$this->stopped = true;
}
/**
* Count the number of arguments.
*
* @return integer The number of arguments.
*
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function count()
{
return \count($this->arguments);
}
/**
* Serialize the event.
*
* @return string The serialized event.
*
* @since 1.0
*/
public function serialize()
{
return serialize($this->__serialize());
}
/**
* Serialize the event.
*
* @return array The data to be serialized
*
* @since 2.0.0
*/
public function __serialize()
{
return [
'name' => $this->name,
'arguments' => $this->arguments,
'stopped' => $this->stopped,
];
}
/**
* Unserialize the event.
*
* @param string $serialized The serialized event.
*
* @return void
*
* @since 1.0
*/
public function unserialize($serialized)
{
$this->__unserialize(unserialize($serialized));
}
/**
* Unserialize the event.
*
* @param array $data The serialized event.
*
* @return void
*
* @since 2.0.0
*/
public function __unserialize(array $data)
{
$this->name = $data['name'];
$this->arguments = $data['arguments'];
$this->stopped = $data['stopped'];
}
/**
* Tell if the given event argument exists.
*
* @param string $name The argument name.
*
* @return boolean True if it exists, false otherwise.
*
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function offsetExists($name)
{
return $this->hasArgument($name);
}
/**
* Get an event argument value.
*
* @param string $name The argument name.
*
* @return mixed The argument value or null if not existing.
*
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function offsetGet($name)
{
return $this->getArgument($name);
}
}

View File

@ -0,0 +1,196 @@
<?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\Command;
use Joomla\Console\Command\AbstractCommand;
use Joomla\Event\DispatcherAwareInterface;
use Joomla\Event\DispatcherAwareTrait;
use Joomla\Event\DispatcherInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Command listing information about the application's event dispatcher.
*
* @since 2.0.0
*/
class DebugEventDispatcherCommand extends AbstractCommand implements DispatcherAwareInterface
{
use DispatcherAwareTrait;
/**
* The default command name
*
* @var string
* @since 2.0.0
*/
protected static $defaultName = 'debug:event-dispatcher';
/**
* Instantiate the command.
*
* @param DispatcherInterface $dispatcher The application event dispatcher.
*
* @since 2.0.0
*/
public function __construct(DispatcherInterface $dispatcher)
{
$this->setDispatcher($dispatcher);
parent::__construct();
}
/**
* Configure the command.
*
* @return void
*
* @since 2.0.0
*/
protected function configure(): void
{
$this->setDescription("Displays information about the application's event dispatcher");
$this->addArgument('event', InputArgument::OPTIONAL, 'Show the listeners for a specific event');
$this->setHelp(<<<'EOF'
The <info>%command.name%</info> command lists all of the registered event handlers in an application's event dispatcher:
<info>php %command.full_name%</info>
To get specific listeners for an event, specify its name:
<info>php %command.full_name% application.before_execute</info>
EOF
);
}
/**
* Internal function to execute the command.
*
* @param InputInterface $input The input to inject into the command.
* @param OutputInterface $output The output to inject into the command.
*
* @return integer The command exit code
*
* @since 2.0.0
*/
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
if ($event = $input->getArgument('event')) {
$listeners = $this->dispatcher->getListeners($event);
if (empty($listeners)) {
$io->warning(sprintf('The event "%s" does not have any registered listeners.', $event));
return 0;
}
$io->title(sprintf('%s Registered Listeners for "%s" Event', $this->getApplication()->getName(), $event));
$this->renderEventListenerTable($listeners, $io);
return 0;
}
$listeners = $this->dispatcher->getListeners();
if (empty($listeners)) {
$io->comment('There are no listeners registered to the event dispatcher.');
return 0;
}
$io->title(sprintf('%s Registered Listeners Grouped By Event', $this->getApplication()->getName()));
ksort($listeners);
foreach ($listeners as $subscribedEvent => $eventListeners) {
$io->section(sprintf('"%s" event', $subscribedEvent));
$this->renderEventListenerTable($eventListeners, $io);
}
return 0;
}
/**
* Formats a callable resource to be displayed in the console output
*
* @param callable $callable A callable resource to format
*
* @return string
*
* @since 2.0.0
* @throws \ReflectionException
* @note This method is based on \Symfony\Bundle\FrameworkBundle\Console\Descriptor\TextDescriptor::formatCallable()
*/
private function formatCallable($callable): string
{
if (\is_array($callable)) {
if (\is_object($callable[0])) {
return sprintf('%s::%s()', \get_class($callable[0]), $callable[1]);
}
return sprintf('%s::%s()', $callable[0], $callable[1]);
}
if (\is_string($callable)) {
return sprintf('%s()', $callable);
}
if ($callable instanceof \Closure) {
$r = new \ReflectionFunction($callable);
if (strpos($r->name, '{closure}') !== false) {
return 'Closure()';
}
if (null !== $class = $r->getClosureScopeClass()) {
return sprintf('%s::%s()', $class->name, $r->name);
}
return $r->name . '()';
}
if (method_exists($callable, '__invoke')) {
return sprintf('%s::__invoke()', \get_class($callable));
}
throw new \InvalidArgumentException('Callable is not describable.');
}
/**
* Renders the table of listeners for an event
*
* @param array $eventListeners The listeners for an event
* @param SymfonyStyle $io The I/O helper
*
* @return void
*
* @since 2.0.0
*/
private function renderEventListenerTable(array $eventListeners, SymfonyStyle $io): void
{
$tableHeaders = ['Order', 'Callable'];
$tableRows = [];
foreach ($eventListeners as $order => $listener) {
$tableRows[] = [
sprintf('#%d', $order + 1),
$this->formatCallable($listener),
];
}
$io->table($tableHeaders, $tableRows);
}
}

View File

@ -0,0 +1,506 @@
<?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 number 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);
}
}

View File

@ -0,0 +1,29 @@
<?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;
/**
* Interface to be implemented by classes depending on a dispatcher.
*
* @since 1.0
*/
interface DispatcherAwareInterface
{
/**
* Set the dispatcher to use.
*
* @param DispatcherInterface $dispatcher The dispatcher to use.
*
* @return DispatcherAwareInterface This method is chainable.
*
* @since 1.0
*/
public function setDispatcher(DispatcherInterface $dispatcher);
}

View File

@ -0,0 +1,59 @@
<?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;
/**
* Defines the trait for a Dispatcher Aware Class.
*
* @since 1.2.0
*/
trait DispatcherAwareTrait
{
/**
* Event Dispatcher
*
* @var DispatcherInterface|null
* @since 1.2.0
*/
private $dispatcher;
/**
* Get the event dispatcher.
*
* @return DispatcherInterface
*
* @since 1.2.0
* @throws \UnexpectedValueException May be thrown if the dispatcher has not been set.
*/
public function getDispatcher()
{
if ($this->dispatcher) {
return $this->dispatcher;
}
throw new \UnexpectedValueException('Dispatcher not set in ' . __CLASS__);
}
/**
* Set the dispatcher to use.
*
* @param DispatcherInterface $dispatcher The dispatcher to use.
*
* @return $this
*
* @since 1.2.0
*/
public function setDispatcher(DispatcherInterface $dispatcher)
{
$this->dispatcher = $dispatcher;
return $this;
}
}

View File

@ -0,0 +1,128 @@
<?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;
/**
* Interface for event dispatchers.
*
* @since 1.0
*/
interface DispatcherInterface
{
/**
* 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;
/**
* 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 2.0.0
*/
public function addListener(string $eventName, callable $callback, int $priority = 0): bool;
/**
* 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 2.0.0
*/
public function clearListeners($event = null);
/**
* Count the number of registered listeners for the given event.
*
* @param string $event The event name.
*
* @return integer
*
* @since 2.0.0
*/
public function countListeners($event);
/**
* 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 2.0.0
*/
public function getListeners(?string $event = null);
/**
* 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|null $eventName An optional event name to check a listener is subscribed to.
*
* @return boolean True if the listener is registered, false otherwise.
*
* @since 2.0.0
*/
public function hasListener(callable $callback, ?string $eventName = null);
/**
* 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;
/**
* Adds an event subscriber.
*
* @param SubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since 2.0.0
*/
public function addSubscriber(SubscriberInterface $subscriber): void;
/**
* Removes an event subscriber.
*
* @param SubscriberInterface $subscriber The subscriber.
*
* @return void
*
* @since 2.0.0
*/
public function removeSubscriber(SubscriberInterface $subscriber): void;
}

View File

@ -0,0 +1,149 @@
<?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;
use InvalidArgumentException;
/**
* Default Event class.
*
* @since 1.0
*/
class Event extends AbstractEvent
{
/**
* Add an event argument, only if it is not existing.
*
* @param string $name The argument name.
* @param mixed $value The argument value.
*
* @return $this
*
* @since 1.0
*/
public function addArgument($name, $value)
{
if (!isset($this->arguments[$name])) {
$this->arguments[$name] = $value;
}
return $this;
}
/**
* Add argument to event.
*
* @param string $name Argument name.
* @param mixed $value Value.
*
* @return $this
*
* @since 1.0
*/
public function setArgument($name, $value)
{
$this->arguments[$name] = $value;
return $this;
}
/**
* Remove an event argument.
*
* @param string $name The argument name.
*
* @return mixed The old argument value or null if it is not existing.
*
* @since 1.0
*/
public function removeArgument($name)
{
$return = null;
if (isset($this->arguments[$name])) {
$return = $this->arguments[$name];
unset($this->arguments[$name]);
}
return $return;
}
/**
* Clear all event arguments.
*
* @return array The old arguments.
*
* @since 1.0
*/
public function clearArguments()
{
$arguments = $this->arguments;
$this->arguments = [];
return $arguments;
}
/**
* Stop the event propagation.
*
* @return void
*
* @since 1.0
* @deprecated 3.0 Use stopPropagation instead
*/
public function stop()
{
trigger_deprecation(
'joomla/event',
'2.0.0',
'%s() is deprecated and will be removed in 3.0, use %s::stopPropagation() instead.',
__METHOD__,
EventInterface::class
);
$this->stopPropagation();
}
/**
* Set the value of an event argument.
*
* @param string $name The argument name.
* @param mixed $value The argument value.
*
* @return void
*
* @since 1.0
* @throws InvalidArgumentException If the argument name is null.
*/
#[\ReturnTypeWillChange]
public function offsetSet($name, $value)
{
if ($name === null) {
throw new InvalidArgumentException('The argument name cannot be null.');
}
$this->setArgument($name, $value);
}
/**
* Remove an event argument.
*
* @param string $name The argument name.
*
* @return void
*
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function offsetUnset($name)
{
$this->removeArgument($name);
}
}

View File

@ -0,0 +1,102 @@
<?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;
use BadMethodCallException;
/**
* Implementation of an immutable Event.
* An immutable event cannot be modified after instantiation :
*
* - its propagation cannot be stopped
* - its arguments cannot be modified
*
* You may want to use this event when you want to ensure that
* the listeners won't manipulate it.
*
* @since 1.0
*/
final class EventImmutable extends AbstractEvent
{
/**
* A flag to see if the constructor has been
* already called.
*
* @var boolean
*/
private $constructed = false;
/**
* Constructor.
*
* @param string $name The event name.
* @param array $arguments The event arguments.
*
* @throws BadMethodCallException
*
* @since 1.0
*/
public function __construct($name, array $arguments = [])
{
if ($this->constructed) {
throw new BadMethodCallException(
sprintf('Cannot reconstruct the EventImmutable %s.', $this->name)
);
}
$this->constructed = true;
parent::__construct($name, $arguments);
}
/**
* Set the value of an event argument.
*
* @param string $name The argument name.
* @param mixed $value The argument value.
*
* @return void
*
* @since 1.0
* @throws BadMethodCallException
*/
public function offsetSet($name, $value)
{
throw new BadMethodCallException(
sprintf(
'Cannot set the argument %s of the immutable event %s.',
$name,
$this->name
)
);
}
/**
* Remove an event argument.
*
* @param string $name The argument name.
*
* @return void
*
* @throws BadMethodCallException
*
* @since 1.0
*/
public function offsetUnset($name)
{
throw new BadMethodCallException(
sprintf(
'Cannot remove the argument %s of the immutable event %s.',
$name,
$this->name
)
);
}
}

View File

@ -0,0 +1,58 @@
<?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;
/**
* Interface for events.
* An event has a name and its propagation can be stopped.
*
* @since 1.0
*/
interface EventInterface
{
/**
* Get an event argument value.
*
* @param string $name The argument name.
* @param mixed $default The default value if not found.
*
* @return mixed The argument value or the default value.
*
* @since 2.0.0
*/
public function getArgument($name, $default = null);
/**
* Get the event name.
*
* @return string The event name.
*
* @since 1.0
*/
public function getName();
/**
* Tell if the event propagation is stopped.
*
* @return boolean True if stopped, false otherwise.
*
* @since 1.0
*/
public function isStopped();
/**
* Stops the propagation of the event to further event listeners.
*
* @return void
*
* @since 2.0.0
*/
public function stopPropagation(): void;
}

View File

@ -0,0 +1,126 @@
<?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;
use Psr\Container\ContainerInterface;
/**
* Decorator for an event listener to be pulled from the service container.
*
* @since 2.0.0
*/
final class LazyServiceEventListener
{
/**
* The service container to load the service from
*
* @var ContainerInterface
* @since 2.0.0
*/
private $container;
/**
* The ID of the service from the container to be used
*
* @var string
* @since 2.0.0
*/
private $serviceId;
/**
* The method from the service to be called
*
* @var string
* @since 2.0.0
*/
private $method;
/**
* Constructor.
*
* @param ContainerInterface $container The service container to load the service from when it shall be executed
* @param string $serviceId The ID of the service from the container to be used
* @param string $method The method from the service to be called if necessary. If left empty, the service must be a callable;
* (i.e. have an `__invoke()` method on a class)
*
* @since 2.0.0
* @throws \InvalidArgumentException if the service ID is empty
*/
public function __construct(ContainerInterface $container, string $serviceId, string $method = '')
{
if (empty($serviceId)) {
throw new \InvalidArgumentException(
sprintf(
'The $serviceId parameter cannot be empty in %s',
self::class
)
);
}
$this->container = $container;
$this->serviceId = $serviceId;
$this->method = $method;
}
/**
* Load a service from the container to listen to an event.
*
* @param EventInterface $event The event to process
*
* @return void
*
* @since 2.0.0
* @throws \InvalidArgumentException if the constructor's $method parameter is empty when not executing a callable service
* @throws \RuntimeException if the service cannot be executed
*/
public function __invoke(EventInterface $event): void
{
if (!$this->container->has($this->serviceId)) {
throw new \RuntimeException(
sprintf(
'The "%s" service has not been registered to the service container',
$this->serviceId
)
);
}
$service = $this->container->get($this->serviceId);
// If the service is callable on its own, just execute it
if (\is_callable($service)) {
\call_user_func($service, $event);
return;
}
if (empty($this->method)) {
throw new \InvalidArgumentException(
sprintf(
'The $method argument is required when creating a "%s" to call a method from the "%s" service.',
self::class,
$this->serviceId
)
);
}
if (!method_exists($service, $this->method)) {
throw new \RuntimeException(
sprintf(
'The "%s" method does not exist on "%s" (from service "%s")',
$this->method,
\get_class($service),
$this->serviceId
)
);
}
\call_user_func([$service, $this->method], $event);
}
}

View File

@ -0,0 +1,155 @@
<?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;
/**
* A class containing an inner listeners priority queue that can be iterated multiple times.
*
* @since 1.0
* @internal
*/
final class ListenersPriorityQueue implements \IteratorAggregate, \Countable
{
/**
* The listeners for an event.
*
* @var array
* @since 2.0.0
*/
private $listeners = [];
/**
* Add a listener with the given priority only if not already present.
*
* @param callable $callback A callable function acting as an event listener.
* @param integer $priority The listener priority.
*
* @return $this
*
* @since 1.0
*/
public function add(callable $callback, int $priority): self
{
$this->listeners[$priority][] = $callback;
return $this;
}
/**
* Remove a listener from the queue.
*
* @param callable $callback A callable function acting as an event listener.
*
* @return $this
*
* @since 1.0
*/
public function remove(callable $callback): self
{
foreach ($this->listeners as $priority => $listeners) {
if (($key = array_search($callback, $listeners, true)) !== false) {
unset($this->listeners[$priority][$key]);
}
}
return $this;
}
/**
* Tell if the listener exists in the queue.
*
* @param callable $callback A callable function acting as an event listener.
*
* @return boolean True if it exists, false otherwise.
*
* @since 1.0
*/
public function has(callable $callback): bool
{
foreach ($this->listeners as $priority => $listeners) {
if (($key = array_search($callback, $listeners, true)) !== false) {
return true;
}
}
return false;
}
/**
* Get the priority of the given listener.
*
* @param callable $callback A callable function acting as an event listener.
* @param mixed $default The default value to return if the listener doesn't exist.
*
* @return mixed The listener priority if it exists or the specified default value
*
* @since 1.0
*/
public function getPriority(callable $callback, $default = null)
{
foreach ($this->listeners as $priority => $listeners) {
if (($key = array_search($callback, $listeners, true)) !== false) {
return $priority;
}
}
return $default;
}
/**
* Get all listeners contained in this queue, sorted according to their priority.
*
* @return callable[] An array of listeners.
*
* @since 1.0
*/
public function getAll(): array
{
if (empty($this->listeners)) {
return [];
}
krsort($this->listeners);
return \call_user_func_array('array_merge', $this->listeners);
}
/**
* Get the priority queue.
*
* @return \ArrayIterator
*
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this->getAll());
}
/**
* Count the number of listeners in the queue.
*
* @return integer The number of listeners in the queue.
*
* @since 1.0
*/
#[\ReturnTypeWillChange]
public function count()
{
$count = 0;
foreach ($this->listeners as $priority) {
$count += \count($priority);
}
return $count;
}
}

View File

@ -0,0 +1,83 @@
<?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;
/**
* An enumeration of priorities for event listeners that you are encouraged to use when adding them in the Dispatcher.
*
* @since 1.0
*/
final class Priority
{
/**
* Indicates the event listener should have a minimum priority.
*
* @var integer
* @since 1.0
*/
public const MIN = -3;
/**
* Indicates the event listener should have a low priority.
*
* @var integer
* @since 1.0
*/
public const LOW = -2;
/**
* Indicates the event listener should have a below normal priority.
*
* @var integer
* @since 1.0
*/
public const BELOW_NORMAL = -1;
/**
* Indicates the event listener should have a normal priority. This is the default priority.
*
* @var integer
* @since 1.0
*/
public const NORMAL = 0;
/**
* Indicates the event listener should have a above normal priority.
*
* @var integer
* @since 1.0
*/
public const ABOVE_NORMAL = 1;
/**
* Indicates the event listener should have a high priority.
*
* @var integer
* @since 1.0
*/
public const HIGH = 2;
/**
* Indicates the event listener should have a maximum priority.
*
* @var integer
* @since 1.0
*/
public const MAX = 3;
/**
* Disallow instantiation of this class
*
* @since 1.0
*/
private function __construct()
{
}
}

View File

@ -0,0 +1,37 @@
<?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;
/**
* Interface for event subscribers.
*
* @since 2.0.0
*/
interface SubscriberInterface
{
/**
* Returns an array of events this subscriber will listen to.
*
* The array keys are event names and the value can be:
*
* - The method name to call (priority defaults to 0)
* - An array composed of the method name to call and the priority
*
* For instance:
*
* * array('eventName' => 'methodName')
* * array('eventName' => array('methodName', $priority))
*
* @return array
*
* @since 2.0.0
*/
public static function getSubscribedEvents(): array;
}