Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
100.00% |
1 / 1 |
|
100.00% |
8 / 8 |
CRAP | |
100.00% |
65 / 65 |
ContainerAwareEventDispatcher | |
100.00% |
1 / 1 |
|
100.00% |
8 / 8 |
40 | |
100.00% |
65 / 65 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
dispatch | |
100.00% |
1 / 1 |
8 | |
100.00% |
16 / 16 |
|||
getListeners | |
100.00% |
1 / 1 |
9 | |
100.00% |
16 / 16 |
|||
hasListeners | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
addListener | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
removeListener | |
100.00% |
1 / 1 |
7 | |
100.00% |
11 / 11 |
|||
addSubscriber | |
100.00% |
1 / 1 |
7 | |
100.00% |
8 / 8 |
|||
removeSubscriber | |
100.00% |
1 / 1 |
6 | |
100.00% |
6 / 6 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher. | |
*/ | |
namespace Drupal\Component\EventDispatcher; | |
use Symfony\Component\DependencyInjection\IntrospectableContainerInterface; | |
use Symfony\Component\EventDispatcher\Event; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
/** | |
* A performance optimized container aware event dispatcher. | |
* | |
* This version of the event dispatcher contains the following optimizations | |
* in comparison to the Symfony event dispatcher component: | |
* | |
* <dl> | |
* <dt>Faster instantiation of the event dispatcher service</dt> | |
* <dd> | |
* Instead of calling <code>addSubscriberService</code> once for each | |
* subscriber, a precompiled array of listener definitions is passed | |
* directly to the constructor. This is faster by roughly an order of | |
* magnitude. The listeners are collected and prepared using a compiler | |
* pass. | |
* </dd> | |
* <dt>Lazy instantiation of listeners</dt> | |
* <dd> | |
* Services are only retrieved from the container just before invocation. | |
* Especially when dispatching the KernelEvents::REQUEST event, this leads | |
* to a more timely invocation of the first listener. Overall dispatch | |
* runtime is not affected by this change though. | |
* </dd> | |
* </dl> | |
*/ | |
class ContainerAwareEventDispatcher implements EventDispatcherInterface { | |
/** | |
* The service container. | |
* | |
* @var \Symfony\Component\DependencyInjection\IntrospectableContainerInterface; | |
*/ | |
protected $container; | |
/** | |
* Listener definitions. | |
* | |
* A nested array of listener definitions keyed by event name and priority. | |
* A listener definition is an associative array with one of the following key | |
* value pairs: | |
* - callable: A callable listener | |
* - service: An array of the form [service id, method] | |
* | |
* A service entry will be resolved to a callable only just before its | |
* invocation. | |
* | |
* @var array | |
*/ | |
protected $listeners; | |
/** | |
* Whether listeners need to be sorted prior to dispatch, keyed by event name. | |
* | |
* @var TRUE[] | |
*/ | |
protected $unsorted; | |
/** | |
* Constructs a container aware event dispatcher. | |
* | |
* @param \Symfony\Component\DependencyInjection\IntrospectableContainerInterface $container | |
* The service container. | |
* @param array $listeners | |
* A nested array of listener definitions keyed by event name and priority. | |
* The array is expected to be ordered by priority. A listener definition is | |
* an associative array with one of the following key value pairs: | |
* - callable: A callable listener | |
* - service: An array of the form [service id, method] | |
* A service entry will be resolved to a callable only just before its | |
* invocation. | |
*/ | |
public function __construct(IntrospectableContainerInterface $container, array $listeners = []) { | |
$this->container = $container; | |
$this->listeners = $listeners; | |
$this->unsorted = []; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function dispatch($event_name, Event $event = NULL) { | |
if ($event === NULL) { | |
$event = new Event(); | |
} | |
$event->setDispatcher($this); | |
$event->setName($event_name); | |
if (isset($this->listeners[$event_name])) { | |
// Sort listeners if necessary. | |
if (isset($this->unsorted[$event_name])) { | |
krsort($this->listeners[$event_name]); | |
unset($this->unsorted[$event_name]); | |
} | |
// Invoke listeners and resolve callables if necessary. | |
foreach ($this->listeners[$event_name] as $priority => &$definitions) { | |
foreach ($definitions as $key => &$definition) { | |
if (!isset($definition['callable'])) { | |
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
} | |
$definition['callable']($event, $event_name, $this); | |
if ($event->isPropagationStopped()) { | |
return $event; | |
} | |
} | |
} | |
} | |
return $event; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getListeners($event_name = NULL) { | |
$result = []; | |
if ($event_name === NULL) { | |
// If event name was omitted, collect all listeners of all events. | |
foreach (array_keys($this->listeners) as $event_name) { | |
$listeners = $this->getListeners($event_name); | |
if (!empty($listeners)) { | |
$result[$event_name] = $listeners; | |
} | |
} | |
} | |
elseif (isset($this->listeners[$event_name])) { | |
// Sort listeners if necessary. | |
if (isset($this->unsorted[$event_name])) { | |
krsort($this->listeners[$event_name]); | |
unset($this->unsorted[$event_name]); | |
} | |
// Collect listeners and resolve callables if necessary. | |
foreach ($this->listeners[$event_name] as $priority => &$definitions) { | |
foreach ($definitions as $key => &$definition) { | |
if (!isset($definition['callable'])) { | |
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
} | |
$result[] = $definition['callable']; | |
} | |
} | |
} | |
return $result; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function hasListeners($event_name = NULL) { | |
return (bool) count($this->getListeners($event_name)); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function addListener($event_name, $listener, $priority = 0) { | |
$this->listeners[$event_name][$priority][] = ['callable' => $listener]; | |
$this->unsorted[$event_name] = TRUE; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function removeListener($event_name, $listener) { | |
if (!isset($this->listeners[$event_name])) { | |
return; | |
} | |
foreach ($this->listeners[$event_name] as $priority => $definitions) { | |
foreach ($definitions as $key => $definition) { | |
if (!isset($definition['callable'])) { | |
if (!$this->container->initialized($definition['service'][0])) { | |
continue; | |
} | |
$definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
} | |
if ($definition['callable'] === $listener) { | |
unset($this->listeners[$event_name][$priority][$key]); | |
} | |
} | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function addSubscriber(EventSubscriberInterface $subscriber) { | |
foreach ($subscriber->getSubscribedEvents() as $event_name => $params) { | |
if (is_string($params)) { | |
$this->addListener($event_name, array($subscriber, $params)); | |
} | |
elseif (is_string($params[0])) { | |
$this->addListener($event_name, array($subscriber, $params[0]), isset($params[1]) ? $params[1] : 0); | |
} | |
else { | |
foreach ($params as $listener) { | |
$this->addListener($event_name, array($subscriber, $listener[0]), isset($listener[1]) ? $listener[1] : 0); | |
} | |
} | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function removeSubscriber(EventSubscriberInterface $subscriber) { | |
foreach ($subscriber->getSubscribedEvents() as $event_name => $params) { | |
if (is_array($params) && is_array($params[0])) { | |
foreach ($params as $listener) { | |
$this->removeListener($event_name, array($subscriber, $listener[0])); | |
} | |
} | |
else { | |
$this->removeListener($event_name, array($subscriber, is_string($params) ? $params : $params[0])); | |
} | |
} | |
} | |
} |