[EventDispatcher] Replaced EventDispatcher by Doctrine's implementation

This commit is contained in:
Bernhard Schussek 2011-03-13 19:03:18 +01:00
parent 25931caeab
commit 699e046b4f
7 changed files with 440 additions and 507 deletions

View File

@ -1,136 +1,72 @@
<?php
/*
* This file is part of the Symfony package.
* $Id$
*
* (c) Fabien Potencier <fabien@symfony.com>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Symfony\Component\EventDispatcher;
/**
* Event.
* Event is the base class for classes containing event data.
*
* @author Fabien Potencier <fabien@symfony.com>
* This class contains no event data. It is used by events that do not pass
* state information to an event handler when an event is raised.
*
* You can call the method stopPropagation() to abort the execution of
* further listeners in your event listener.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @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>
*/
class Event implements EventInterface
class Event
{
protected $processed = false;
protected $subject;
protected $name;
protected $parameters;
/**
* @var Boolean Whether no further event listeners should be triggered
*/
private $propagationStopped = false;
/**
* Constructs a new Event.
* Returns whether further event listeners should be triggered.
*
* @param mixed $subject The subject
* @param string $name The event name
* @param array $parameters An array of parameters
* @see Event::stopPropagation
* @return Boolean Whether propagation was already stopped for this event.
*/
public function __construct($subject, $name, $parameters = array())
public function isPropagationStopped()
{
$this->subject = $subject;
$this->name = $name;
$this->parameters = $parameters;
return $this->propagationStopped;
}
/**
* Returns the subject.
* Stops the propagation of the event to further event listeners.
*
* @return mixed The subject
* If multiple event listeners are connected to the same event, no
* further event listener will be triggered once any trigger calls
* stopPropagation().
*/
public function getSubject()
public function stopPropagation()
{
return $this->subject;
}
/**
* Returns the event name.
*
* @return string The event name
*/
public function getName()
{
return $this->name;
}
/**
* Sets the processed flag to true.
*
* This method must be called by listeners when
* it has processed the event (it is only meaningful
* when the event has been notified with the notifyUntil()
* dispatcher method.
*/
public function setProcessed()
{
$this->processed = true;
}
/**
* Returns whether the event has been processed by a listener or not.
*
* This method is only meaningful for events notified
* with notifyUntil().
*
* @return Boolean true if the event has been processed, false otherwise
*/
public function isProcessed()
{
return $this->processed;
}
/**
* Returns the event parameters.
*
* @return array The event parameters
*/
public function all()
{
return $this->parameters;
}
/**
* Returns true if the parameter exists.
*
* @param string $name The parameter name
*
* @return Boolean true if the parameter exists, false otherwise
*/
public function has($name)
{
return array_key_exists($name, $this->parameters);
}
/**
* Returns a parameter value.
*
* @param string $name The parameter name
*
* @return mixed The parameter value
*
* @throws \InvalidArgumentException When parameter doesn't exists for this event
*/
public function get($name)
{
if (!array_key_exists($name, $this->parameters)) {
throw new \InvalidArgumentException(sprintf('The event "%s" has no "%s" parameter.', $this->name, $name));
}
return $this->parameters[$name];
}
/**
* Sets a parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
public function set($name, $value)
{
$this->parameters[$name] = $value;
$this->propagationStopped = true;
}
}

View File

@ -1,147 +1,202 @@
<?php
/*
* This file is part of the Symfony package.
* $Id$
*
* (c) Fabien Potencier <fabien@symfony.com>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Symfony\Component\EventDispatcher;
/**
* EventDispatcher implements a dispatcher object.
* The EventManager is the central point of Doctrine's event listener system.
* Listeners are registered on the manager and events are dispatched through the
* manager.
*
* @author Fabien Potencier <fabien@symfony.com>
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @version $Revision: 3938 $
* @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>
*/
class EventDispatcher implements EventDispatcherInterface
{
protected $listeners = array();
/**
* Map of registered listeners.
* <event> => (<objecthash> => <listener>)
*
* @var array
*/
private $listeners = array();
/**
* Connects a listener to a given event name.
* Map of priorities by the object hashes of their listeners.
* <event> => (<objecthash> => <priority>)
*
* Listeners with a higher priority are executed first.
* This property is used for listener sorting.
*
* @param string $name An event name
* @param mixed $listener A PHP callable
* @param integer $priority The priority (between -10 and 10 -- defaults to 0)
* @var array
*/
public function connect($name, $listener, $priority = 0)
private $priorities = array();
/**
* Stores which event listener lists are currently sorted.
* <event> => <sorted>
*
* @var array
*/
private $sorted = array();
/**
* @see EventDispatcherInterface::dispatchEvent
*/
public function dispatchEvent($eventName, Event $event = null)
{
if (!isset($this->listeners[$name][$priority])) {
if (!isset($this->listeners[$name])) {
$this->listeners[$name] = array();
if (isset($this->listeners[$eventName])) {
if (null === $event) {
$event = new Event();
}
$this->listeners[$name][$priority] = array();
}
$this->listeners[$name][$priority][] = $listener;
}
$this->sortListeners($eventName);
/**
* Disconnects one, or all listeners for the given event name.
*
* @param string $name An event name
* @param mixed|null $listener The listener to remove, or null to remove all
*
* @return void
*/
public function disconnect($name, $listener = null)
{
if (!isset($this->listeners[$name])) {
return;
}
foreach ($this->listeners[$eventName] as $listener) {
$this->triggerListener($listener, $eventName, $event);
if (null === $listener) {
unset($this->listeners[$name]);
return;
}
foreach ($this->listeners[$name] as $priority => $callables) {
foreach ($callables as $i => $callable) {
if ($listener === $callable) {
unset($this->listeners[$name][$priority][$i]);
if ($event->isPropagationStopped()) {
break;
}
}
}
}
/**
* Notifies all listeners of a given event.
* Triggers the listener method for an event.
*
* @param EventInterface $event An EventInterface instance
* This method can be overridden to add functionality that is executed
* for each listener.
*
* @param object $listener The event listener on which to invoke the listener method.
* @param string $eventName The name of the event to dispatch. The name of the event is
* the name of the method that is invoked on listeners.
* @param Event $event The event arguments to pass to the event handlers/listeners.
*/
public function notify(EventInterface $event)
protected function triggerListener($listener, $eventName, Event $event)
{
foreach ($this->getListeners($event->getName()) as $listener) {
call_user_func($listener, $event);
if ($listener instanceof \Closure) {
$listener->__invoke($event);
} else {
$listener->$eventName($event);
}
}
/**
* Notifies all listeners of a given event until one processes the event.
* Sorts the internal list of listeners for the given event by priority.
*
* @param EventInterface $event An EventInterface instance
* 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.
*
* @return mixed The returned value of the listener that processed the event
* @param string $event The name of the event.
*/
public function notifyUntil(EventInterface $event)
private function sortListeners($eventName)
{
foreach ($this->getListeners($event->getName()) as $listener) {
$ret = call_user_func($listener, $event);
if ($event->isProcessed()) {
return $ret;
if (!$this->sorted[$eventName]) {
$p = $this->priorities[$eventName];
uasort($this->listeners[$eventName], function ($a, $b) use ($p) {
return $p[spl_object_hash($b)] - $p[spl_object_hash($a)];
});
$this->sorted[$eventName] = true;
}
}
/**
* @see EventDispatcherInterface::getListeners
*/
public function getListeners($eventName = null)
{
if ($eventName) {
$this->sortListeners($eventName);
return $this->listeners[$eventName];
}
foreach ($this->listeners as $eventName => $listeners) {
$this->sortListeners($eventName);
}
return $this->listeners;
}
/**
* @see EventDispatcherInterface::hasListeners
*/
public function hasListeners($eventName)
{
return isset($this->listeners[$eventName]) && $this->listeners[$eventName];
}
/**
* @see EventDispatcherInterface::addEventListener
*/
public function addEventListener($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();
}
// Prevents duplicate listeners on same event (same instance only)
$this->listeners[$eventName][$hash] = $listener;
$this->priorities[$eventName][$hash] = $priority;
$this->sorted[$eventName] = false;
}
}
/**
* @see EventDispatcherInterface::removeEventListener
*/
public function removeEventListener($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]);
}
}
}
/**
* Filters a value by calling all listeners of a given event.
*
* @param EventInterface $event An EventInterface instance
* @param mixed $value The value to be filtered
*
* @return mixed The filtered value
* @see EventDispatcherInterface::addEventSubscriber
*/
public function filter(EventInterface $event, $value)
public function addEventSubscriber(EventSubscriberInterface $subscriber, $priority = 0)
{
foreach ($this->getListeners($event->getName()) as $listener) {
$value = call_user_func($listener, $event, $value);
}
return $value;
$this->addEventListener($subscriber->getSubscribedEvents(), $subscriber, $priority);
}
/**
* Returns true if the given event name has some listeners.
*
* @param string $name The event name
*
* @return Boolean true if some listeners are connected, false otherwise
*/
public function hasListeners($name)
{
return (Boolean) count($this->getListeners($name));
}
/**
* Returns all listeners associated with a given event name.
*
* @param string $name The event name
*
* @return array An array of listeners
*/
public function getListeners($name)
{
if (!isset($this->listeners[$name])) {
return array();
}
krsort($this->listeners[$name]);
return call_user_func_array('array_merge', $this->listeners[$name]);
}
}
}

View File

@ -12,81 +12,72 @@
namespace Symfony\Component\EventDispatcher;
/**
* EventDispatcherInterface describes an event dispatcher class.
* The EventManager is the central point of Doctrine's event listener system.
* Listeners are registered on the manager and events are dispatched through the
* manager.
*
* @see http://developer.apple.com/documentation/Cocoa/Conceptual/Notifications/index.html Apple's Cocoa framework
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Bernhard Schussek <bschussek@gmail.com>
*/
interface EventDispatcherInterface
{
/**
* Connects a listener to a given event name.
* Adds an event listener that listens on the specified events.
*
* Listeners with a higher priority are executed first.
*
* @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|array $eventNames The event(s) to listen on.
* @param object $listener The listener object.
* @param integer $priority The higher this value, the earlier an event
* listener will be triggered in the chain.
* Defaults to 0.
*/
function connect($name, $listener, $priority = 0);
function addEventListener($eventNames, $listener, $priority = 0);
/**
* Disconnects one, or all listeners for the given event name.
* Adds an event subscriber. The subscriber is asked for all the events he is
* interested in and added as a listener for these events.
*
* @param string $name An event name
* @param mixed|null $listener The listener to remove, or null to remove all
*
* @return void
* @param EventSubscriberInterface $subscriber The subscriber.
* @param integer $priority The higher this value, the earlier an event
* listener will be triggered in the chain.
* Defaults to 0.
*/
function disconnect($name, $listener = null);
function addEventSubscriber(EventSubscriberInterface $subscriber, $priority = 0);
/**
* Notifies all listeners of a given event.
* Removes an event listener from the specified events.
*
* @param EventInterface $event An EventInterface instance
* @param string|array $eventNames The event(s) to remove a listener from.
* @param object $listener The listener object to remove.
*/
function notify(EventInterface $event);
function removeEventListener($eventNames, $listener);
/**
* Notifies all listeners of a given event until one processes the event.
* Dispatches an event to all registered listeners.
*
* A listener tells the dispatcher that it has processed the event
* by calling the setProcessed() method on it.
*
* It can then return a value that will be forwarded to the caller.
*
* @param EventInterface $event An EventInterface instance
*
* @return mixed The returned value of the listener that processed the event
* @param string $eventName The name of the event to dispatch. The name of
* the event is the name of the method that is
* invoked on listeners.
* @param Event $event The event to pass to the event handlers/listeners.
* If not supplied, an empty Event instance is created.
*/
function notifyUntil(EventInterface $event);
function dispatchEvent($eventName, Event $event = null);
/**
* Filters a value by calling all listeners of a given event.
* Gets the listeners of a specific event or all listeners.
*
* @param EventInterface $event An EventInterface instance
* @param mixed $value The value to be filtered
* @param string $eventName The name of the event.
*
* @return mixed The filtered value
* @return array The event listeners for the specified event, or all event
* listeners by event name.
*/
function filter(EventInterface $event, $value);
function getListeners($eventName = null);
/**
* Returns true if the given event name has some listeners.
* Checks whether an event has any registered listeners.
*
* @param string $name The event name
* @param string $eventName The name of the event.
*
* @return Boolean true if some listeners are connected, false otherwise
* @return Boolean TRUE if the specified event has any listeners, FALSE
* otherwise.
*/
function hasListeners($name);
/**
* Returns all listeners associated with a given event name.
*
* @param string $name The event name
*
* @return array An array of listeners
*/
function getListeners($name);
}
function hasListeners($eventName);
}

View File

@ -1,89 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (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.
*/
namespace Symfony\Component\EventDispatcher;
/**
* EventInterface.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface EventInterface
{
/**
* Returns the subject.
*
* @return mixed The subject
*/
function getSubject();
/**
* Returns the event name.
*
* @return string The event name
*/
function getName();
/**
* Sets the processed flag to true.
*
* This method must be called by listeners when
* it has processed the event (it is only meaningful
* when the event has been notified with the notifyUntil()
* dispatcher method.
*/
function setProcessed();
/**
* Returns whether the event has been processed by a listener or not.
*
* This method is only meaningful for events notified
* with notifyUntil().
*
* @return Boolean true if the event has been processed, false otherwise
*/
function isProcessed();
/**
* Returns the event parameters.
*
* @return array The event parameters
*/
function all();
/**
* Returns true if the parameter exists.
*
* @param string $name The parameter name
*
* @return Boolean true if the parameter exists, false otherwise
*/
function has($name);
/**
* Returns a parameter value.
*
* @param string $name The parameter name
*
* @return mixed The parameter value
*
* @throws \InvalidArgumentException When parameter doesn't exists for this event
*/
function get($name);
/**
* Sets a parameter.
*
* @param string $name The parameter name
* @param mixed $value The parameter value
*/
function set($name, $value);
}

View File

@ -0,0 +1,47 @@
<?php
/*
* $Id: EventListener.php 4653 2008-07-10 17:17:58Z romanb $
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Symfony\Component\EventDispatcher;
/**
* An EventSubscriber knows himself what events he is interested in.
* If an EventSubscriber is added to an EventManager, the manager invokes
* {@link getSubscribedEvents} and registers the subscriber as a listener for all
* returned events.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.org
* @since 2.0
* @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>
*/
interface EventSubscriberInterface
{
/**
* Returns an array of events this subscriber wants to listen to.
*
* @return array
*/
public static function getSubscribedEvents();
}

View File

@ -13,126 +13,187 @@ namespace Symfony\Tests\Component\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class EventDispatcherTest extends \PHPUnit_Framework_TestCase
{
public function testConnectAndDisconnect()
/* Some pseudo events */
const preFoo = 'preFoo';
const postFoo = 'postFoo';
const preBar = 'preBar';
const postBar = 'postBar';
private $dispatcher;
private $listener;
protected function setUp()
{
$dispatcher = new EventDispatcher();
$dispatcher->connect('bar', 'listenToBar');
$this->assertEquals(array('listenToBar'), $dispatcher->getListeners('bar'), '->connect() connects a listener to an event name');
$dispatcher->connect('bar', 'listenToBarBar');
$this->assertEquals(array('listenToBar', 'listenToBarBar'), $dispatcher->getListeners('bar'), '->connect() can connect several listeners for the same event name');
$dispatcher->connect('barbar', 'listenToBarBar');
$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');
$this->dispatcher = new EventDispatcher();
$this->listener = new TestEventListener();
}
public function testGetHasListeners()
public function testInitialState()
{
$dispatcher = new EventDispatcher();
$this->assertFalse($dispatcher->hasListeners('foo'), '->hasListeners() returns false if the event has no listener');
$dispatcher->connect('foo', 'listenToFoo');
$this->assertEquals(true, $dispatcher->hasListeners('foo'), '->hasListeners() returns true if the event has some listeners');
$dispatcher->disconnect('foo', 'listenToFoo');
$this->assertFalse($dispatcher->hasListeners('foo'), '->hasListeners() returns false if the event has no listener');
$dispatcher->connect('bar', 'listenToBar');
$this->assertEquals(array('listenToBar'), $dispatcher->getListeners('bar'), '->getListeners() returns an array of listeners connected to the given event name');
$this->assertEquals(array(), $dispatcher->getListeners('foobar'), '->getListeners() returns an empty array if no listener are connected to the given event name');
$this->assertEquals(array(), $this->dispatcher->getListeners());
$this->assertFalse($this->dispatcher->hasListeners(self::preFoo));
$this->assertFalse($this->dispatcher->hasListeners(self::postFoo));
}
public function testNotify()
public function testAddEventListener()
{
$listener = new Listener();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'listenToFoo'));
$dispatcher->connect('foo', array($listener, 'listenToFooBis'));
$e = $dispatcher->notify($event = new Event(new \stdClass(), 'foo'));
$this->assertEquals('listenToFoolistenToFooBis', $listener->getValue(), '->notify() notifies all registered listeners in order');
$listener->reset();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'listenToFooBis'));
$dispatcher->connect('foo', array($listener, 'listenToFoo'));
$dispatcher->notify(new Event(new \stdClass(), 'foo'));
$this->assertEquals('listenToFooBislistenToFoo', $listener->getValue(), '->notify() notifies all registered listeners in order');
$this->dispatcher->addEventListener(array('preFoo', 'postFoo'), $this->listener);
$this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
$this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
$this->assertEquals(1, count($this->dispatcher->getListeners(self::preFoo)));
$this->assertEquals(1, count($this->dispatcher->getListeners(self::postFoo)));
$this->assertEquals(2, count($this->dispatcher->getListeners()));
}
public function testNotifyUntil()
public function testGetListenersSortsByPriority()
{
$listener = new Listener();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'listenToFoo'));
$dispatcher->connect('foo', array($listener, 'listenToFooBis'));
$dispatcher->notifyUntil($event = new Event(new \stdClass(), 'foo'));
$this->assertEquals('listenToFoolistenToFooBis', $listener->getValue(), '->notifyUntil() notifies all registered listeners in order and stops when the event is processed');
$listener1 = new TestEventListener();
$listener2 = new TestEventListener();
$listener3 = new TestEventListener();
$listener->reset();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'listenToFooBis'));
$dispatcher->connect('foo', array($listener, 'listenToFoo'));
$dispatcher->notifyUntil($event = new Event(new \stdClass(), 'foo'));
$this->assertEquals('listenToFooBis', $listener->getValue(), '->notifyUntil() notifies all registered listeners in order and stops when the event is processed');
$this->dispatcher->addEventListener('preFoo', $listener1, -10);
$this->dispatcher->addEventListener('preFoo', $listener2);
$this->dispatcher->addEventListener('preFoo', $listener3, 10);
$expected = array(
spl_object_hash($listener3) => $listener3,
spl_object_hash($listener2) => $listener2,
spl_object_hash($listener1) => $listener1,
);
$this->assertSame($expected, $this->dispatcher->getListeners('preFoo'));
}
public function testFilter()
public function testGetAllListenersSortsByPriority()
{
$listener = new Listener();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'filterFoo'));
$dispatcher->connect('foo', array($listener, 'filterFooBis'));
$ret = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo');
$this->assertEquals('-*foo*-', $ret, '->filter() returns the filtered value');
$listener1 = new TestEventListener();
$listener2 = new TestEventListener();
$listener3 = new TestEventListener();
$listener4 = new TestEventListener();
$listener5 = new TestEventListener();
$listener6 = new TestEventListener();
$listener->reset();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'filterFooBis'));
$dispatcher->connect('foo', array($listener, 'filterFoo'));
$ret = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo');
$this->assertEquals('*-foo-*', $ret, '->filter() returns the filtered value');
$this->dispatcher->addEventListener('preFoo', $listener1, -10);
$this->dispatcher->addEventListener('preFoo', $listener2);
$this->dispatcher->addEventListener('preFoo', $listener3, 10);
$this->dispatcher->addEventListener('postFoo', $listener4, -10);
$this->dispatcher->addEventListener('postFoo', $listener5);
$this->dispatcher->addEventListener('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,
),
);
$this->assertSame($expected, $this->dispatcher->getListeners());
}
public function testDispatchEvent()
{
$this->dispatcher->addEventListener(array('preFoo', 'postFoo'), $this->listener);
$this->dispatcher->dispatchEvent(self::preFoo);
$this->assertTrue($this->listener->preFooInvoked);
$this->assertFalse($this->listener->postFooInvoked);
}
public function testDispatchEventForClosure()
{
$invoked = 0;
$listener = function () use (&$invoked) {
$invoked++;
};
$this->dispatcher->addEventListener(array('preFoo', 'postFoo'), $listener);
$this->dispatcher->dispatchEvent(self::preFoo);
$this->assertEquals(1, $invoked);
}
public function testStopEventPropagation()
{
$otherListener = new TestEventListener;
// postFoo() stops the propagation, so only one listener should
// be executed
// Manually set priority to enforce $this->listener to be called first
$this->dispatcher->addEventListener('postFoo', $this->listener, 10);
$this->dispatcher->addEventListener('postFoo', $otherListener);
$this->dispatcher->dispatchEvent(self::postFoo);
$this->assertTrue($this->listener->postFooInvoked);
$this->assertFalse($otherListener->postFooInvoked);
}
public function testDispatchByPriority()
{
$invoked = array();
$listener1 = function () use (&$invoked) {
$invoked[] = '1';
};
$listener2 = function () use (&$invoked) {
$invoked[] = '2';
};
$listener3 = function () use (&$invoked) {
$invoked[] = '3';
};
$this->dispatcher->addEventListener('preFoo', $listener1, -10);
$this->dispatcher->addEventListener('preFoo', $listener2);
$this->dispatcher->addEventListener('preFoo', $listener3, 10);
$this->dispatcher->dispatchEvent(self::preFoo);
$this->assertEquals(array('3', '2', '1'), $invoked);
}
public function testRemoveEventListener()
{
$this->dispatcher->addEventListener(array('preBar'), $this->listener);
$this->assertTrue($this->dispatcher->hasListeners(self::preBar));
$this->dispatcher->removeEventListener(array('preBar'), $this->listener);
$this->assertFalse($this->dispatcher->hasListeners(self::preBar));
}
public function testAddEventSubscriber()
{
$eventSubscriber = new TestEventSubscriber();
$this->dispatcher->addEventSubscriber($eventSubscriber);
$this->assertTrue($this->dispatcher->hasListeners(self::preFoo));
$this->assertTrue($this->dispatcher->hasListeners(self::postFoo));
}
}
class Listener
class TestEventListener
{
protected
$value = '';
public $preFooInvoked = false;
public $postFooInvoked = false;
function filterFoo(Event $event, $foo)
/* Listener methods */
public function preFoo(Event $e)
{
return "*$foo*";
$this->preFooInvoked = true;
}
function filterFooBis(Event $event, $foo)
public function postFoo(Event $e)
{
return "-$foo-";
}
$this->postFooInvoked = true;
function listenToFoo(Event $event)
{
$this->value .= 'listenToFoo';
}
function listenToFooBis(Event $event)
{
$this->value .= 'listenToFooBis';
$event->setProcessed();
}
function getValue()
{
return $this->value;
}
function reset()
{
$this->value = '';
$e->stopPropagation();
}
}
class TestEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array('preFoo', 'postFoo');
}
}

View File

@ -1,68 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (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.
*/
namespace Symfony\Tests\Component\EventDispatcher;
use Symfony\Component\EventDispatcher\Event;
class EventTest extends \PHPUnit_Framework_TestCase
{
protected $subject;
protected $parameters;
public function testGetSubject()
{
$event = $this->createEvent();
$this->assertEquals($this->subject, $event->getSubject(), '->getSubject() returns the event subject');
}
public function testGetName()
{
$this->assertEquals('name', $this->createEvent()->getName(), '->getName() returns the event name');
}
public function testParameters()
{
$event = $this->createEvent();
$this->assertEquals($this->parameters, $event->all(), '->all() returns the event parameters');
$this->assertEquals('bar', $event->get('foo'), '->get() returns the value of a parameter');
$event->set('foo', 'foo');
$this->assertEquals('foo', $event->get('foo'), '->set() changes the value of a parameter');
$this->assertTrue($event->has('foo'), '->has() returns true if the parameter is defined');
$this->assertFalse($event->has('oof'), '->has() returns false if the parameter is not defined');
try {
$event->get('foobar');
$this->fail('->get() throws an \InvalidArgumentException exception when the parameter does not exist');
} catch (\Exception $e) {
$this->assertInstanceOf('\InvalidArgumentException', $e, '->get() throws an \InvalidArgumentException exception when the parameter does not exist');
$this->assertEquals('The event "name" has no "foobar" parameter.', $e->getMessage(), '->get() throws an \InvalidArgumentException exception when the parameter does not exist');
}
$event = new Event($this->subject, 'name', $this->parameters);
}
public function testSetIsProcessed()
{
$event = $this->createEvent();
$this->assertFalse($event->isProcessed(), '->isProcessed() returns false by default');
$event->setProcessed();
$this->assertTrue($event->isProcessed(), '->isProcessed() returns true if the event has been processed');
}
protected function createEvent()
{
$this->subject = new \stdClass();
$this->parameters = array('foo' => 'bar');
return new Event($this->subject, 'name', $this->parameters);
}
}