2010-01-04 14:26:20 +00:00
< ? php
/*
2011-08-21 13:32:21 +01:00
* This file is part of the Symfony package .
2010-04-07 02:07:59 +01:00
*
2011-08-21 13:32:21 +01:00
* ( c ) Fabien Potencier < fabien @ symfony . com >
*
* For the full copyright and license information , please view the LICENSE
* file that was distributed with this source code .
2010-01-04 14:26:20 +00:00
*/
2011-01-15 13:29:43 +00:00
namespace Symfony\Component\EventDispatcher ;
2019-03-25 16:04:58 +00:00
use Psr\EventDispatcher\StoppableEventInterface ;
2019-03-29 18:41:30 +00:00
use Symfony\Contracts\EventDispatcher\Event as ContractsEvent ;
2019-03-25 16:04:58 +00:00
2010-01-04 14:26:20 +00:00
/**
2011-03-17 14:27:42 +00:00
* The EventDispatcherInterface is the central point of Symfony ' s event listener system .
*
2011-03-13 18:03:18 +00:00
* Listeners are registered on the manager and events are dispatched through the
* manager .
2010-01-04 14:26:20 +00:00
*
2014-12-07 17:02:39 +00:00
* @ author Guilherme Blanco < guilhermeblanco @ hotmail . com >
* @ author Jonathan Wage < jonwage @ gmail . com >
* @ author Roman Borschel < roman @ code - factory . org >
* @ author Bernhard Schussek < bschussek @ gmail . com >
* @ author Fabien Potencier < fabien @ symfony . com >
* @ author Jordi Boggiano < j . boggiano @ seld . be >
* @ author Jordan Alliot < jordan . alliot @ gmail . com >
2017-06-01 10:24:09 +01:00
* @ author Nicolas Grekas < p @ tchwork . com >
2010-01-04 14:26:20 +00:00
*/
2011-01-25 13:23:23 +00:00
class EventDispatcher implements EventDispatcherInterface
2010-01-04 14:26:20 +00:00
{
2019-01-16 21:53:45 +00:00
private $listeners = [];
private $sorted = [];
2018-11-17 09:06:42 +00:00
private $optimized ;
public function __construct ()
{
if ( __CLASS__ === \get_class ( $this )) {
2019-01-16 21:53:45 +00:00
$this -> optimized = [];
2018-11-17 09:06:42 +00:00
}
}
2011-03-13 18:03:18 +00:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2018-10-18 21:44:28 +01:00
*
* @ param string | null $eventName
2011-03-13 18:03:18 +00:00
*/
2018-10-18 21:44:28 +01:00
public function dispatch ( $event /*, string $eventName = null*/ )
2010-01-04 14:26:20 +00:00
{
2018-10-18 21:44:28 +01:00
$eventName = 1 < \func_num_args () ? \func_get_arg ( 1 ) : null ;
2019-03-25 16:04:58 +00:00
if ( \is_object ( $event )) {
2018-10-18 21:44:28 +01:00
$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 )));
}
2011-03-18 08:01:19 +00:00
}
2011-03-13 18:03:18 +00:00
2018-11-17 09:06:42 +00:00
if ( null !== $this -> optimized && null !== $eventName ) {
2019-01-16 21:53:45 +00:00
$listeners = $this -> optimized [ $eventName ] ? ? ( empty ( $this -> listeners [ $eventName ]) ? [] : $this -> optimizeListeners ( $eventName ));
2018-11-17 09:06:42 +00:00
} else {
$listeners = $this -> getListeners ( $eventName );
}
if ( $listeners ) {
2019-03-31 21:04:08 +01:00
$this -> callListeners ( $listeners , $eventName , $event );
2012-03-10 04:10:57 +00:00
}
return $event ;
2010-01-04 14:26:20 +00:00
}
2010-05-06 12:25:53 +01:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2010-05-06 12:25:53 +01:00
*/
2015-10-11 09:30:48 +01:00
public function getListeners ( $eventName = null )
2010-01-04 14:26:20 +00:00
{
2011-03-18 08:01:19 +00:00
if ( null !== $eventName ) {
2017-06-01 10:24:09 +01:00
if ( empty ( $this -> listeners [ $eventName ])) {
2019-01-16 21:53:45 +00:00
return [];
2015-09-17 02:14:14 +01:00
}
2011-05-30 15:00:29 +01:00
if ( ! isset ( $this -> sorted [ $eventName ])) {
$this -> sortListeners ( $eventName );
}
return $this -> sorted [ $eventName ];
2011-03-13 18:03:18 +00:00
}
2015-04-09 15:55:17 +01:00
foreach ( $this -> listeners as $eventName => $eventListeners ) {
2011-05-30 15:00:29 +01:00
if ( ! isset ( $this -> sorted [ $eventName ])) {
$this -> sortListeners ( $eventName );
}
2010-05-06 12:25:53 +01:00
}
2014-07-25 14:59:30 +01:00
return array_filter ( $this -> sorted );
2010-01-04 14:26:20 +00:00
}
2015-10-11 09:30:48 +01:00
/**
2015-10-20 18:32:54 +01:00
* { @ inheritdoc }
2015-10-11 09:30:48 +01:00
*/
public function getListenerPriority ( $eventName , $listener )
{
2017-06-01 10:24:09 +01:00
if ( empty ( $this -> listeners [ $eventName ])) {
2015-10-11 09:30:48 +01:00
return ;
}
2018-07-26 09:45:46 +01:00
if ( \is_array ( $listener ) && isset ( $listener [ 0 ]) && $listener [ 0 ] instanceof \Closure ) {
2017-06-01 10:24:09 +01:00
$listener [ 0 ] = $listener [ 0 ]();
}
2018-11-17 09:06:42 +00:00
foreach ( $this -> listeners [ $eventName ] as $priority => & $listeners ) {
foreach ( $listeners as & $v ) {
2018-07-26 09:45:46 +01:00
if ( $v !== $listener && \is_array ( $v ) && isset ( $v [ 0 ]) && $v [ 0 ] instanceof \Closure ) {
2017-06-01 10:24:09 +01:00
$v [ 0 ] = $v [ 0 ]();
}
if ( $v === $listener ) {
return $priority ;
}
2015-10-11 09:30:48 +01:00
}
}
}
2010-05-06 12:25:53 +01:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2010-05-06 12:25:53 +01:00
*/
2011-03-18 08:01:19 +00:00
public function hasListeners ( $eventName = null )
2010-01-04 14:26:20 +00:00
{
2017-06-01 10:24:09 +01:00
if ( null !== $eventName ) {
return ! empty ( $this -> listeners [ $eventName ]);
}
foreach ( $this -> listeners as $eventListeners ) {
if ( $eventListeners ) {
return true ;
}
}
return false ;
2010-01-04 14:26:20 +00:00
}
2010-05-06 12:25:53 +01:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2010-05-06 12:25:53 +01:00
*/
2011-05-05 18:02:52 +01:00
public function addListener ( $eventName , $listener , $priority = 0 )
2010-01-04 14:26:20 +00:00
{
2011-05-05 18:02:52 +01:00
$this -> listeners [ $eventName ][ $priority ][] = $listener ;
2018-11-17 09:06:42 +00:00
unset ( $this -> sorted [ $eventName ], $this -> optimized [ $eventName ]);
2011-03-13 18:03:18 +00:00
}
2010-01-04 14:26:20 +00:00
2011-03-13 18:03:18 +00:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2011-03-13 18:03:18 +00:00
*/
2011-05-05 18:02:52 +01:00
public function removeListener ( $eventName , $listener )
2011-03-13 18:03:18 +00:00
{
2017-06-01 10:24:09 +01:00
if ( empty ( $this -> listeners [ $eventName ])) {
2011-05-05 18:02:52 +01:00
return ;
}
2011-03-18 08:01:19 +00:00
2018-07-26 09:45:46 +01:00
if ( \is_array ( $listener ) && isset ( $listener [ 0 ]) && $listener [ 0 ] instanceof \Closure ) {
2017-06-01 10:24:09 +01:00
$listener [ 0 ] = $listener [ 0 ]();
}
2018-11-17 09:06:42 +00:00
foreach ( $this -> listeners [ $eventName ] as $priority => & $listeners ) {
foreach ( $listeners as $k => & $v ) {
2018-07-26 09:45:46 +01:00
if ( $v !== $listener && \is_array ( $v ) && isset ( $v [ 0 ]) && $v [ 0 ] instanceof \Closure ) {
2017-06-01 10:24:09 +01:00
$v [ 0 ] = $v [ 0 ]();
}
if ( $v === $listener ) {
2018-11-17 09:06:42 +00:00
unset ( $listeners [ $k ], $this -> sorted [ $eventName ], $this -> optimized [ $eventName ]);
2017-06-01 10:24:09 +01:00
}
}
2018-11-17 09:06:42 +00:00
if ( ! $listeners ) {
2017-06-01 10:24:09 +01:00
unset ( $this -> listeners [ $eventName ][ $priority ]);
2011-03-13 18:03:18 +00:00
}
}
}
2010-08-26 13:08:42 +01:00
2011-03-13 18:03:18 +00:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2011-03-13 18:03:18 +00:00
*/
2011-06-14 13:40:27 +01:00
public function addSubscriber ( EventSubscriberInterface $subscriber )
2011-03-13 18:03:18 +00:00
{
2011-06-14 13:40:27 +01:00
foreach ( $subscriber -> getSubscribedEvents () as $eventName => $params ) {
2018-07-05 12:24:53 +01:00
if ( \is_string ( $params )) {
2019-01-16 21:53:45 +00:00
$this -> addListener ( $eventName , [ $subscriber , $params ]);
2018-07-05 12:24:53 +01:00
} elseif ( \is_string ( $params [ 0 ])) {
2019-01-16 21:53:45 +00:00
$this -> addListener ( $eventName , [ $subscriber , $params [ 0 ]], isset ( $params [ 1 ]) ? $params [ 1 ] : 0 );
2011-09-10 01:28:22 +01:00
} else {
foreach ( $params as $listener ) {
2019-01-16 21:53:45 +00:00
$this -> addListener ( $eventName , [ $subscriber , $listener [ 0 ]], isset ( $listener [ 1 ]) ? $listener [ 1 ] : 0 );
2011-09-10 01:28:22 +01:00
}
2011-06-14 13:40:27 +01:00
}
2011-05-05 18:02:52 +01:00
}
2011-03-17 14:27:42 +00:00
}
2011-03-18 08:00:58 +00:00
/**
2015-09-17 02:42:05 +01:00
* { @ inheritdoc }
2011-03-18 08:00:58 +00:00
*/
public function removeSubscriber ( EventSubscriberInterface $subscriber )
{
2011-08-23 21:41:08 +01:00
foreach ( $subscriber -> getSubscribedEvents () as $eventName => $params ) {
2018-07-05 12:24:53 +01:00
if ( \is_array ( $params ) && \is_array ( $params [ 0 ])) {
2011-09-10 01:28:22 +01:00
foreach ( $params as $listener ) {
2019-01-16 21:53:45 +00:00
$this -> removeListener ( $eventName , [ $subscriber , $listener [ 0 ]]);
2011-09-10 01:28:22 +01:00
}
} else {
2019-01-16 21:53:45 +00:00
$this -> removeListener ( $eventName , [ $subscriber , \is_string ( $params ) ? $params : $params [ 0 ]]);
2011-09-10 01:28:22 +01:00
}
2011-05-05 18:02:52 +01:00
}
2011-03-18 08:00:58 +00:00
}
2011-03-17 14:27:42 +00:00
/**
2011-05-05 08:15:48 +01:00
* Triggers the listeners of an event .
2011-03-17 14:27:42 +00:00
*
* This method can be overridden to add functionality that is executed
* for each listener .
*
2016-06-28 06:50:50 +01:00
* @ param callable [] $listeners The event listeners
* @ param string $eventName The name of the event to dispatch
2019-03-25 16:04:58 +00:00
* @ param object $event The event object to pass to the event handlers / listeners
*/
protected function callListeners ( iterable $listeners , string $eventName , $event )
{
2019-03-31 12:27:47 +01:00
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 ;
}
2019-03-31 21:04:08 +01:00
$listener ( $event instanceof Event ? $event : new WrappedEvent ( $event ), $eventName , $this );
2019-03-31 12:27:47 +01:00
}
2019-03-25 16:04:58 +00:00
}
/**
* @ deprecated since Symfony 4.3 , use callListeners () instead
2011-03-17 14:27:42 +00:00
*/
2011-05-05 08:15:48 +01:00
protected function doDispatch ( $listeners , $eventName , Event $event )
2011-03-17 14:27:42 +00:00
{
2011-05-05 08:15:48 +01:00
foreach ( $listeners as $listener ) {
2019-03-31 12:27:47 +01:00
if ( $event -> isPropagationStopped ()) {
2011-05-05 08:15:48 +01:00
break ;
}
2018-11-25 01:27:51 +00:00
$listener ( $event , $eventName , $this );
2011-03-17 14:27:42 +00:00
}
}
/**
* Sorts the internal list of listeners for the given event by priority .
*/
2018-11-17 09:06:42 +00:00
private function sortListeners ( string $eventName )
2011-03-17 14:27:42 +00:00
{
2015-09-17 02:14:14 +01:00
krsort ( $this -> listeners [ $eventName ]);
2019-01-16 21:53:45 +00:00
$this -> sorted [ $eventName ] = [];
2017-06-01 10:24:09 +01:00
2018-11-17 09:06:42 +00:00
foreach ( $this -> listeners [ $eventName ] as & $listeners ) {
2017-06-01 10:24:09 +01:00
foreach ( $listeners as $k => $listener ) {
2017-11-07 20:33:43 +00:00
if ( \is_array ( $listener ) && isset ( $listener [ 0 ]) && $listener [ 0 ] instanceof \Closure ) {
2017-06-01 10:24:09 +01:00
$listener [ 0 ] = $listener [ 0 ]();
}
$this -> sorted [ $eventName ][] = $listener ;
}
}
2010-05-06 12:25:53 +01:00
}
2018-11-17 09:06:42 +00:00
/**
* Optimizes the internal list of listeners for the given event by priority .
*/
private function optimizeListeners ( string $eventName ) : array
{
krsort ( $this -> listeners [ $eventName ]);
2019-01-16 21:53:45 +00:00
$this -> optimized [ $eventName ] = [];
2018-11-17 09:06:42 +00:00
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 : \Closure :: fromCallable ( $listener );
}
}
}
return $this -> optimized [ $eventName ];
}
2011-03-17 14:27:42 +00:00
}