[EventDispatcher] refactored the code
* The array returned by getListeners() now removes the listener hash as the key (as this is an implementation detail) * The sort method now guarantees that a listener registered before another will stay in the same order even for the same priority (for BC) * Made various optimizations
This commit is contained in:
parent
69d324eca8
commit
136b23ead4
@ -34,33 +34,11 @@ namespace Symfony\Component\EventDispatcher;
|
||||
* @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>
|
||||
*/
|
||||
class EventDispatcher implements EventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Map of registered listeners.
|
||||
* <event> => (<objecthash> => <listener>)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $listeners = array();
|
||||
|
||||
/**
|
||||
* Map of priorities by the object hashes of their listeners.
|
||||
* <event> => (<objecthash> => <priority>)
|
||||
*
|
||||
* This property is used for listener sorting.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $priorities = array();
|
||||
|
||||
/**
|
||||
* Stores which event listener lists are currently sorted.
|
||||
* <event> => <sorted>
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $sorted = array();
|
||||
|
||||
/**
|
||||
@ -68,19 +46,19 @@ class EventDispatcher implements EventDispatcherInterface
|
||||
*/
|
||||
public function dispatch($eventName, Event $event = null)
|
||||
{
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
if (null === $event) {
|
||||
$event = new Event();
|
||||
}
|
||||
if (!isset($this->listeners[$eventName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->sortListeners($eventName);
|
||||
if (null === $event) {
|
||||
$event = new Event();
|
||||
}
|
||||
|
||||
foreach ($this->listeners[$eventName] as $listener) {
|
||||
$this->triggerListener($listener, $eventName, $event);
|
||||
foreach ($this->getListeners($eventName) as $listener) {
|
||||
$this->triggerListener($listener, $eventName, $event);
|
||||
|
||||
if ($event->isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
if ($event->isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,25 +68,34 @@ class EventDispatcher implements EventDispatcherInterface
|
||||
*/
|
||||
public function getListeners($eventName = null)
|
||||
{
|
||||
if ($eventName) {
|
||||
$this->sortListeners($eventName);
|
||||
if (null !== $eventName) {
|
||||
if (!isset($this->sorted[$eventName])) {
|
||||
$this->sortListeners($eventName);
|
||||
}
|
||||
|
||||
return $this->listeners[$eventName];
|
||||
return $this->sorted[$eventName];
|
||||
}
|
||||
|
||||
$sorted = array();
|
||||
foreach ($this->listeners as $eventName => $listeners) {
|
||||
$this->sortListeners($eventName);
|
||||
if (!isset($this->sorted[$eventName])) {
|
||||
$this->sortListeners($eventName);
|
||||
}
|
||||
|
||||
if ($this->sorted[$eventName]) {
|
||||
$sorted[$eventName] = $this->sorted[$eventName];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->listeners;
|
||||
return $sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see EventDispatcherInterface::hasListeners
|
||||
*/
|
||||
public function hasListeners($eventName)
|
||||
public function hasListeners($eventName = null)
|
||||
{
|
||||
return isset($this->listeners[$eventName]) && $this->listeners[$eventName];
|
||||
return (Boolean) count($this->getListeners($eventName));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,19 +103,17 @@ class EventDispatcher implements EventDispatcherInterface
|
||||
*/
|
||||
public function addListener($eventNames, $listener, $priority = 0)
|
||||
{
|
||||
// Picks the hash code related to that listener
|
||||
$hash = spl_object_hash($listener);
|
||||
|
||||
foreach ((array) $eventNames as $eventName) {
|
||||
if (!isset($this->listeners[$eventName])) {
|
||||
$this->listeners[$eventName] = array();
|
||||
$this->priorities[$eventName] = array();
|
||||
if (!isset($this->listeners[$eventName][$priority])) {
|
||||
if (!isset($this->listeners[$eventName])) {
|
||||
$this->listeners[$eventName] = array();
|
||||
}
|
||||
$this->listeners[$eventName][$priority] = array();
|
||||
}
|
||||
|
||||
// Prevents duplicate listeners on same event (same instance only)
|
||||
$this->listeners[$eventName][$hash] = $listener;
|
||||
$this->priorities[$eventName][$hash] = $priority;
|
||||
$this->sorted[$eventName] = false;
|
||||
$this->listeners[$eventName][$priority][$hash] = $listener;
|
||||
unset($this->sorted[$eventName]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,14 +122,16 @@ class EventDispatcher implements EventDispatcherInterface
|
||||
*/
|
||||
public function removeListener($eventNames, $listener)
|
||||
{
|
||||
// Picks the hash code related to that listener
|
||||
$hash = spl_object_hash($listener);
|
||||
|
||||
foreach ((array) $eventNames as $eventName) {
|
||||
// Check if actually have this listener associated
|
||||
if (isset($this->listeners[$eventName][$hash])) {
|
||||
unset($this->listeners[$eventName][$hash]);
|
||||
unset($this->priorities[$eventName][$hash]);
|
||||
if (!isset($this->listeners[$eventName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$hash = spl_object_hash($listener);
|
||||
foreach (array_keys($this->listeners[$eventName]) as $priority) {
|
||||
if (isset($this->listeners[$eventName][$priority][$hash])) {
|
||||
unset($this->listeners[$eventName][$priority][$hash], $this->sorted[$eventName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,26 +175,15 @@ class EventDispatcher implements EventDispatcherInterface
|
||||
/**
|
||||
* Sorts the internal list of listeners for the given event by priority.
|
||||
*
|
||||
* Calling this method multiple times will not cause overhead unless you
|
||||
* add new listeners. As long as no listener is added, the list for an
|
||||
* event name won't be sorted twice.
|
||||
*
|
||||
* @param string $event The name of the event.
|
||||
* @param string $eventName The name of the event.
|
||||
*/
|
||||
private function sortListeners($eventName)
|
||||
{
|
||||
if (!$this->sorted[$eventName]) {
|
||||
$p = $this->priorities[$eventName];
|
||||
$this->sorted[$eventName] = array();
|
||||
|
||||
uasort($this->listeners[$eventName], function ($a, $b) use ($p) {
|
||||
$order = $p[spl_object_hash($b)] - $p[spl_object_hash($a)];
|
||||
|
||||
// for the same priority, force the first registered one to stay first
|
||||
return 0 === $order ? 1 : $order;
|
||||
});
|
||||
|
||||
$this->sorted[$eventName] = true;
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
krsort($this->listeners[$eventName]);
|
||||
$this->sorted[$eventName] = array_values(call_user_func_array('array_merge', $this->listeners[$eventName]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,5 +86,5 @@ interface EventDispatcherInterface
|
||||
* @return Boolean TRUE if the specified event has any listeners, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
function hasListeners($eventName);
|
||||
function hasListeners($eventName = null);
|
||||
}
|
||||
|
@ -60,11 +60,7 @@ class EventDispatcherTest extends \PHPUnit_Framework_TestCase
|
||||
$this->dispatcher->addListener('preFoo', $listener2);
|
||||
$this->dispatcher->addListener('preFoo', $listener3, 10);
|
||||
|
||||
$expected = array(
|
||||
spl_object_hash($listener3) => $listener3,
|
||||
spl_object_hash($listener2) => $listener2,
|
||||
spl_object_hash($listener1) => $listener1,
|
||||
);
|
||||
$expected = array($listener3, $listener2, $listener1);
|
||||
|
||||
$this->assertSame($expected, $this->dispatcher->getListeners('preFoo'));
|
||||
}
|
||||
@ -86,16 +82,8 @@ class EventDispatcherTest extends \PHPUnit_Framework_TestCase
|
||||
$this->dispatcher->addListener('postFoo', $listener6, 10);
|
||||
|
||||
$expected = array(
|
||||
'preFoo' => array(
|
||||
spl_object_hash($listener3) => $listener3,
|
||||
spl_object_hash($listener2) => $listener2,
|
||||
spl_object_hash($listener1) => $listener1,
|
||||
),
|
||||
'postFoo' => array(
|
||||
spl_object_hash($listener6) => $listener6,
|
||||
spl_object_hash($listener5) => $listener5,
|
||||
spl_object_hash($listener4) => $listener4,
|
||||
),
|
||||
'preFoo' => array($listener3, $listener2, $listener1),
|
||||
'postFoo' => array($listener6, $listener5, $listener4),
|
||||
);
|
||||
|
||||
$this->assertSame($expected, $this->dispatcher->getListeners());
|
||||
|
Reference in New Issue
Block a user