Sfoglia il codice sorgente

[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
Fabien Potencier 14 anni fa
parent
commit
136b23ead4

+ 48 - 72
src/Symfony/Component/EventDispatcher/EventDispatcher.php

@@ -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];
-
-            uasort($this->listeners[$eventName], function ($a, $b) use ($p) {
-                $order = $p[spl_object_hash($b)] - $p[spl_object_hash($a)];
+        $this->sorted[$eventName] = array();
 
-                // 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]));
         }
     }
 }
- 

+ 1 - 1
src/Symfony/Component/EventDispatcher/EventDispatcherInterface.php

@@ -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);
 }

+ 3 - 15
tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php

@@ -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());