2015-08-17 17:00:26 -07:00
< ? 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 .
*
2015-11-04 11:11:27 -08:00
* @ param \Symfony\Component\DependencyInjection\IntrospectableContainerInterface $container
2015-08-17 17:00:26 -07:00
* 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 ]));
}
}
}
}