From 1af0e369eec9b4034a50bb67efbce681063bb1bf Mon Sep 17 00:00:00 2001 From: Benjamin Zikarsky Date: Wed, 13 Oct 2010 02:56:04 +0200 Subject: [PATCH 1/3] Fixed bug in EventDispatcher::disconnect if the second argument is null or ommitted --- src/Symfony/Component/EventDispatcher/EventDispatcher.php | 5 +++++ .../Tests/Component/EventDispatcher/EventDispatcherTest.php | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 1906f01b6135f..75c5f8e5e847c 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -51,6 +51,11 @@ public function disconnect($name, $listener = null) return false; } + if (null === $listener) { + unset($this->listeners[$name]); + return; + } + foreach ($this->listeners[$name] as $priority => $callables) { foreach ($callables as $i => $callable) { if ($listener === $callable) { diff --git a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php index be6b7cf11e82d..8c37327216fd1 100644 --- a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php +++ b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php @@ -30,6 +30,9 @@ public function testConnectAndDisconnect() $this->assertEquals(array('listenToBarBar'), $dispatcher->getListeners('barbar'), '->disconnect() disconnects a listener for an event name'); $this->assertFalse($dispatcher->disconnect('foobar', 'listen'), '->disconnect() returns false if the listener does not exist'); + + $dispatcher->disconnect('bar'); + $this->assertEquals(array(), $dispatcher->getListeners('bar'), '->disconnect() without a listener disconnects all listeners of for an event name'); } public function testGetHasListeners() @@ -100,6 +103,7 @@ public function testFilter() $e = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo'); $this->assertEquals('*-foo-*', $e->getReturnValue(), '->filter() filters a value'); } + } class Listener From da8ed51a948cd8cc9069b91c0a0a53db65812d2e Mon Sep 17 00:00:00 2001 From: Benjamin Zikarsky Date: Wed, 13 Oct 2010 03:05:25 +0200 Subject: [PATCH 2/3] Added a test to verify that only the event's listeners get disconnected. --- .../Tests/Component/EventDispatcher/EventDispatcherTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php index 8c37327216fd1..e60c0586bd8e6 100644 --- a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php +++ b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php @@ -33,6 +33,7 @@ public function testConnectAndDisconnect() $dispatcher->disconnect('bar'); $this->assertEquals(array(), $dispatcher->getListeners('bar'), '->disconnect() without a listener disconnects all listeners of for an event name'); + $this->assertEquals(array('listenToBarBar'), $dispatcher->getListeners('barbar'), '->disconnect() without a listener disconnects all listeners of for an event name'); } public function testGetHasListeners() @@ -103,7 +104,6 @@ public function testFilter() $e = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo'); $this->assertEquals('*-foo-*', $e->getReturnValue(), '->filter() filters a value'); } - } class Listener From 5cd5655ceaa7d8ae6f25add4bb94bc43a0ec3ca0 Mon Sep 17 00:00:00 2001 From: Benjamin Zikarsky Date: Wed, 13 Oct 2010 03:53:58 +0200 Subject: [PATCH 3/3] Added support for "global" event listeners with NULL as a name --- .../EventDispatcher/EventDispatcher.php | 49 +++++++++++++------ .../EventDispatcher/EventDispatcherTest.php | 16 ++++++ 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/EventDispatcher/EventDispatcher.php b/src/Symfony/Component/EventDispatcher/EventDispatcher.php index 75c5f8e5e847c..ad6287ed8cfe5 100644 --- a/src/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -24,9 +24,9 @@ class EventDispatcher /** * Connects a listener to a given event name. * - * @param string $name An event name - * @param mixed $listener A PHP callable - * @param integer $priority The priority (between -10 and 10 -- defaults to 0) + * @param string|null $name An event name or null to make the listener listen to all events + * @param mixed $listener A PHP callable + * @param integer $priority The priority (between -10 and 10 -- defaults to 0) */ public function connect($name, $listener, $priority = 0) { @@ -40,12 +40,12 @@ public function connect($name, $listener, $priority = 0) /** * Disconnects a listener for a given event name. * - * @param string $name An event name - * @param mixed|null $listener A PHP callable or null to disconnect all listeners + * @param string|null $name An event name or null to disconnect a global listener + * @param mixed|null $listener A PHP callable or null to disconnect all listeners * * @return mixed false if listener does not exist, null otherwise */ - public function disconnect($name, $listener = null) + public function disconnect($name=null, $listener = null) { if (!isset($this->listeners[$name])) { return false; @@ -74,7 +74,7 @@ public function disconnect($name, $listener = null) */ public function notify(Event $event) { - foreach ($this->getListeners($event->getName()) as $listener) { + foreach ($this->getListeners($event->getName(), true) as $listener) { call_user_func($listener, $event); } @@ -90,7 +90,7 @@ public function notify(Event $event) */ public function notifyUntil(Event $event) { - foreach ($this->getListeners($event->getName()) as $listener) { + foreach ($this->getListeners($event->getName(), true) as $listener) { if (call_user_func($listener, $event)) { $event->setProcessed(true); break; @@ -110,7 +110,7 @@ public function notifyUntil(Event $event) */ public function filter(Event $event, $value) { - foreach ($this->getListeners($event->getName()) as $listener) { + foreach ($this->getListeners($event->getName(), true) as $listener) { $value = call_user_func($listener, $event, $value); } @@ -122,30 +122,47 @@ public function filter(Event $event, $value) /** * Returns true if the given event name has some listeners. * - * @param string $name The event name + * @param string $name The event name + * @param boolean $includeGlobals Flag whether the global listeners + * (name=null) should be included * * @return Boolean true if some listeners are connected, false otherwise */ - public function hasListeners($name) + public function hasListeners($name, $includeGlobals=false) { - return (Boolean) count($this->getListeners($name)); + return (Boolean) count($this->getListeners($name, $includeGlobals)); } /** * Returns all listeners associated with a given event name. * - * @param string $name The event name - * + * @param string $name The event name + * @param boolean $includeGlobals Flag whether the global listeners + * (name=null) should be included * @return array An array of listeners */ - public function getListeners($name) + public function getListeners($name, $includeGlobals=false) { if (!isset($this->listeners[$name])) { return array(); } - + $listeners = array(); $all = $this->listeners[$name]; + + // add global listeners if required and existing, but only if the + // it's not global collection which is requested + if (isset($this->listeners[null]) && $includeGlobals && $name != null) { + foreach ($this->listeners[null] as $prio => $globalListeners) { + if (isset($all[$prio])) { + $all[$prio] = array_merge($all[$prio], $globalListeners); + } else { + $all[$prio] = $globalListeners; + } + } + } + + // sort with respect to priority and flatten array ksort($all); foreach ($all as $l) { $listeners = array_merge($listeners, $l); diff --git a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php index e60c0586bd8e6..bc5f5a5c420d4 100644 --- a/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php +++ b/tests/Symfony/Tests/Component/EventDispatcher/EventDispatcherTest.php @@ -104,6 +104,22 @@ public function testFilter() $e = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo'); $this->assertEquals('*-foo-*', $e->getReturnValue(), '->filter() filters a value'); } + + public function testGlobalConnectAndDisconnect() + { + $dispatcher = new EventDispatcher(); + $dispatcher->connect('bar', 'listenToBar'); + $dispatcher->connect('barbar', 'listenToBar'); + $dispatcher->connect(null, 'listenToBarBar'); + $this->assertEquals(array('listenToBar', 'listenToBarBar'), $dispatcher->getListeners('bar', true), '->connect() with event name null connects a global listener (1)'); + $this->assertEquals(array('listenToBar', 'listenToBarBar'), $dispatcher->getListeners('barbar', true), '->connect() with event name null connects a global listener (2)'); + $this->assertEquals(array('listenToBar'), $dispatcher->getListeners('bar', false), '->connect() with event name null connects a global listener (3)'); + $this->assertEquals(array('listenToBar'), $dispatcher->getListeners('barbar', false), '->connect() with event name null connects a global listener (4)'); + + $dispatcher->disconnect(null, 'listenToBarBar'); + $this->assertEquals(array('listenToBar'), $dispatcher->getListeners('bar', true), '->disconnect() with event name null disconnects a global listener'); + } + } class Listener