summaryrefslogtreecommitdiff
path: root/vendor/symfony/event-dispatcher/EventDispatcher.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/symfony/event-dispatcher/EventDispatcher.php')
-rw-r--r--vendor/symfony/event-dispatcher/EventDispatcher.php118
1 files changed, 95 insertions, 23 deletions
diff --git a/vendor/symfony/event-dispatcher/EventDispatcher.php b/vendor/symfony/event-dispatcher/EventDispatcher.php
index afc443b283..e68918c31c 100644
--- a/vendor/symfony/event-dispatcher/EventDispatcher.php
+++ b/vendor/symfony/event-dispatcher/EventDispatcher.php
@@ -11,6 +11,10 @@
namespace Symfony\Component\EventDispatcher;
+use Psr\EventDispatcher\StoppableEventInterface;
+use Symfony\Component\EventDispatcher\Debug\WrappedListener;
+use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
+
/**
* The EventDispatcherInterface is the central point of Symfony's event listener system.
*
@@ -30,18 +34,45 @@ class EventDispatcher implements EventDispatcherInterface
{
private $listeners = [];
private $sorted = [];
+ private $optimized;
+
+ public function __construct()
+ {
+ if (__CLASS__ === \get_class($this)) {
+ $this->optimized = [];
+ }
+ }
/**
* {@inheritdoc}
+ *
+ * @param string|null $eventName
*/
- public function dispatch($eventName, Event $event = null)
+ public function dispatch($event/*, string $eventName = null*/)
{
- if (null === $event) {
- $event = new Event();
+ $eventName = 1 < \func_num_args() ? \func_get_arg(1) : null;
+
+ if (\is_object($event)) {
+ $eventName = $eventName ?? \get_class($event);
+ } else {
+ @trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as first argument is deprecated since Symfony 4.3, pass it second and provide the event object first instead.', EventDispatcherInterface::class), E_USER_DEPRECATED);
+ $swap = $event;
+ $event = $eventName ?? new Event();
+ $eventName = $swap;
+
+ if (!$event instanceof Event) {
+ throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an instance of %s, %s given.', EventDispatcherInterface::class, Event::class, \is_object($event) ? \get_class($event) : \gettype($event)));
+ }
}
- if ($listeners = $this->getListeners($eventName)) {
- $this->doDispatch($listeners, $eventName, $event);
+ if (null !== $this->optimized && null !== $eventName) {
+ $listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
+ } else {
+ $listeners = $this->getListeners($eventName);
+ }
+
+ if ($listeners) {
+ $this->callListeners($listeners, $eventName, $event);
}
return $event;
@@ -86,11 +117,10 @@ class EventDispatcher implements EventDispatcherInterface
$listener[0] = $listener[0]();
}
- foreach ($this->listeners[$eventName] as $priority => $listeners) {
- foreach ($listeners as $k => $v) {
+ foreach ($this->listeners[$eventName] as $priority => &$listeners) {
+ foreach ($listeners as &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
$v[0] = $v[0]();
- $this->listeners[$eventName][$priority][$k] = $v;
}
if ($v === $listener) {
return $priority;
@@ -123,7 +153,7 @@ class EventDispatcher implements EventDispatcherInterface
public function addListener($eventName, $listener, $priority = 0)
{
$this->listeners[$eventName][$priority][] = $listener;
- unset($this->sorted[$eventName]);
+ unset($this->sorted[$eventName], $this->optimized[$eventName]);
}
/**
@@ -139,21 +169,17 @@ class EventDispatcher implements EventDispatcherInterface
$listener[0] = $listener[0]();
}
- foreach ($this->listeners[$eventName] as $priority => $listeners) {
- foreach ($listeners as $k => $v) {
+ foreach ($this->listeners[$eventName] as $priority => &$listeners) {
+ foreach ($listeners as $k => &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
$v[0] = $v[0]();
}
if ($v === $listener) {
- unset($listeners[$k], $this->sorted[$eventName]);
- } else {
- $listeners[$k] = $v;
+ unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
}
}
- if ($listeners) {
- $this->listeners[$eventName][$priority] = $listeners;
- } else {
+ if (!$listeners) {
unset($this->listeners[$eventName][$priority]);
}
}
@@ -201,7 +227,29 @@ class EventDispatcher implements EventDispatcherInterface
*
* @param callable[] $listeners The event listeners
* @param string $eventName The name of the event to dispatch
- * @param Event $event The event object to pass to the event handlers/listeners
+ * @param object $event The event object to pass to the event handlers/listeners
+ */
+ protected function callListeners(iterable $listeners, string $eventName, $event)
+ {
+ if ($event instanceof Event) {
+ $this->doDispatch($listeners, $eventName, $event);
+
+ return;
+ }
+
+ $stoppable = $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
+
+ foreach ($listeners as $listener) {
+ if ($stoppable && $event->isPropagationStopped()) {
+ break;
+ }
+ // @deprecated: the ternary operator is part of a BC layer and should be removed in 5.0
+ $listener($listener instanceof WrappedListener ? new LegacyEventProxy($event) : $event, $eventName, $this);
+ }
+ }
+
+ /**
+ * @deprecated since Symfony 4.3, use callListeners() instead
*/
protected function doDispatch($listeners, $eventName, Event $event)
{
@@ -215,22 +263,46 @@ class EventDispatcher implements EventDispatcherInterface
/**
* Sorts the internal list of listeners for the given event by priority.
- *
- * @param string $eventName The name of the event
*/
- private function sortListeners($eventName)
+ private function sortListeners(string $eventName)
{
krsort($this->listeners[$eventName]);
$this->sorted[$eventName] = [];
- foreach ($this->listeners[$eventName] as $priority => $listeners) {
+ foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as $k => $listener) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
$listener[0] = $listener[0]();
- $this->listeners[$eventName][$priority][$k] = $listener;
}
$this->sorted[$eventName][] = $listener;
}
}
}
+
+ /**
+ * Optimizes the internal list of listeners for the given event by priority.
+ */
+ private function optimizeListeners(string $eventName): array
+ {
+ krsort($this->listeners[$eventName]);
+ $this->optimized[$eventName] = [];
+
+ foreach ($this->listeners[$eventName] as &$listeners) {
+ foreach ($listeners as &$listener) {
+ $closure = &$this->optimized[$eventName][];
+ if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
+ $closure = static function (...$args) use (&$listener, &$closure) {
+ if ($listener[0] instanceof \Closure) {
+ $listener[0] = $listener[0]();
+ }
+ ($closure = \Closure::fromCallable($listener))(...$args);
+ };
+ } else {
+ $closure = $listener instanceof \Closure || $listener instanceof WrappedListener ? $listener : \Closure::fromCallable($listener);
+ }
+ }
+ }
+
+ return $this->optimized[$eventName];
+ }
}