changed the EventDispatcher and Event interfaces

The three notification methods do not return the Event instance anymore.

notify() does not return anything
notifyUntil() returns the returned value of the event that has processed the event
filter() returns the filtered value

Upgrading your listeners:
Listeners for notify() and filter() events: nothing to change
Listeners for notifyUntil() events:

Before:

    $event->setReturnValue('foo');
    return true;

After:

    $event->setProcessed();
    return 'foo';

If you notify events, the processing also need to be changed:

For filter() notifications: the filtered value is now available as
the returned value of the filter() method.

For notifyUntil() notifications:

Before:

    $event = $dispatcher->notifyUntil($event);
    if ($event->isProcessed()) {
        $ret = $event->getReturnValue();

        // do something with $ret
    }

After:

    $ret = $dispatcher->notifyUntil($event);
    if ($event->isProcessed()) {
        // do something with $ret
    }
This commit is contained in:
Fabien Potencier 2011-01-26 07:50:06 +01:00
parent a66d050bdb
commit 8b62df7247
19 changed files with 95 additions and 149 deletions

View File

@ -55,8 +55,6 @@ class EventDispatcher extends BaseEventDispatcher implements EventDispatcherTrac
call_user_func($listener, $event);
}
return $event;
}
/**
@ -71,7 +69,8 @@ class EventDispatcher extends BaseEventDispatcher implements EventDispatcherTrac
$this->addCall($event, $listener, 'notifyUntil');
if (call_user_func($listener, $event)) {
$ret = call_user_func($listener, $event);
if ($event->isProcessed()) {
if (null !== $this->logger) {
$this->logger->debug(sprintf('Listener "%s" processed the event "%s"', $this->listenerToString($listener), $event->getName()));
@ -81,12 +80,9 @@ class EventDispatcher extends BaseEventDispatcher implements EventDispatcherTrac
}
}
$event->setProcessed(true);
break;
return $ret;
}
}
return $event;
}
/**
@ -104,9 +100,7 @@ class EventDispatcher extends BaseEventDispatcher implements EventDispatcherTrac
$value = call_user_func($listener, $event, $value);
}
$event->setReturnValue($value);
return $event;
return $value;
}
/**

View File

@ -50,8 +50,6 @@ class EventDispatcher extends BaseEventDispatcher
}
call_user_func($listener, $event);
}
return $event;
}
/**
@ -63,13 +61,12 @@ class EventDispatcher extends BaseEventDispatcher
if (is_array($listener) && is_string($listener[0])) {
$listener[0] = $this->container->get($listener[0]);
}
if (call_user_func($listener, $event)) {
$event->setProcessed(true);
break;
$ret = call_user_func($listener, $event);
if ($event->isProcessed()) {
return $ret;
}
}
return $event;
}
/**
@ -84,8 +81,6 @@ class EventDispatcher extends BaseEventDispatcher
$value = call_user_func($listener, $event, $value);
}
$event->setReturnValue($value);
return $event;
return $value;
}
}

View File

@ -59,38 +59,24 @@ class Event implements EventInterface
}
/**
* Sets the return value for this event.
* Sets the processed flag to true.
*
* @param mixed $value The return value
* This method must be called by listeners when
* it has processed the event (it is only meaninful
* when the event has been notified with the notifyUntil()
* dispatcher method.
*/
public function setReturnValue($value)
public function setProcessed()
{
$this->value = $value;
}
/**
* Returns the return value.
*
* @return mixed The return value
*/
public function getReturnValue()
{
return $this->value;
}
/**
* Sets the processed flag.
*
* @param Boolean $processed The processed flag value
*/
public function setProcessed($processed)
{
$this->processed = (Boolean) $processed;
$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()

View File

@ -73,35 +73,29 @@ class EventDispatcher implements EventDispatcherInterface
* Notifies all listeners of a given event.
*
* @param EventInterface $event An EventInterface instance
*
* @return EventInterface The EventInterface instance
*/
public function notify(EventInterface $event)
{
foreach ($this->getListeners($event->getName()) as $listener) {
call_user_func($listener, $event);
}
return $event;
}
/**
* Notifies all listeners of a given event until one returns a non null value.
* Notifies all listeners of a given event until one processes the event.
*
* @param EventInterface $event An EventInterface instance
*
* @return EventInterface The EventInterface instance
* @return mixed The returned value of the listener that processed the event
*/
public function notifyUntil(EventInterface $event)
{
foreach ($this->getListeners($event->getName()) as $listener) {
if (call_user_func($listener, $event)) {
$event->setProcessed(true);
break;
$ret = call_user_func($listener, $event);
if ($event->isProcessed()) {
return $ret;
}
}
return $event;
}
/**
@ -110,7 +104,7 @@ class EventDispatcher implements EventDispatcherInterface
* @param EventInterface $event An EventInterface instance
* @param mixed $value The value to be filtered
*
* @return EventInterface The EventInterface instance
* @return mixed The filtered value
*/
public function filter(EventInterface $event, $value)
{
@ -118,9 +112,7 @@ class EventDispatcher implements EventDispatcherInterface
$value = call_user_func($listener, $event, $value);
}
$event->setReturnValue($value);
return $event;
return $value;
}
/**

View File

@ -45,17 +45,20 @@ interface EventDispatcherInterface
* Notifies all listeners of a given event.
*
* @param EventInterface $event An EventInterface instance
*
* @return EventInterface The EventInterface instance
*/
function notify(EventInterface $event);
/**
* Notifies all listeners of a given event until one returns a non null value.
* Notifies all listeners of a given event until one processes the event.
*
* 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 fowarded to the caller.
*
* @param EventInterface $event An EventInterface instance
*
* @return EventInterface The EventInterface instance
* @return mixed The returned value of the listener that processed the event
*/
function notifyUntil(EventInterface $event);
@ -65,7 +68,7 @@ interface EventDispatcherInterface
* @param EventInterface $event An EventInterface instance
* @param mixed $value The value to be filtered
*
* @return EventInterface The EventInterface instance
* @return mixed The filtered value
*/
function filter(EventInterface $event, $value);

View File

@ -33,29 +33,21 @@ interface EventInterface
function getName();
/**
* Sets the return value for this event.
* Sets the processed flag to true.
*
* @param mixed $value The return value
* This method must be called by listeners when
* it has processed the event (it is only meaninful
* when the event has been notified with the notifyUntil()
* dispatcher method.
*/
function setReturnValue($value);
/**
* Returns the return value.
*
* @return mixed The return value
*/
function getReturnValue();
/**
* Sets the processed flag.
*
* @param Boolean $processed The processed flag value
*/
function setProcessed($processed);
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();

View File

@ -81,10 +81,10 @@ class ExceptionListener
throw $exception;
}
$event->setReturnValue($response);
$event->setProcessed();
$handling = false;
return true;
return $response;
}
}

View File

@ -54,12 +54,12 @@ class HttpKernel implements HttpKernelInterface
// exception
$event = new Event($this, 'core.exception', array('request_type' => $type, 'request' => $request, 'exception' => $e));
$this->dispatcher->notifyUntil($event);
$response = $this->dispatcher->notifyUntil($event);
if (!$event->isProcessed()) {
throw $e;
}
$response = $this->filterResponse($event->getReturnValue(), $request, 'A "core.exception" listener returned a non response object.', $type);
$response = $this->filterResponse($response, $request, 'A "core.exception" listener returned a non response object.', $type);
}
return $response;
@ -82,9 +82,9 @@ class HttpKernel implements HttpKernelInterface
{
// request
$event = new Event($this, 'core.request', array('request_type' => $type, 'request' => $request));
$this->dispatcher->notifyUntil($event);
$response = $this->dispatcher->notifyUntil($event);
if ($event->isProcessed()) {
return $this->filterResponse($event->getReturnValue(), $request, 'A "core.request" listener returned a non response object.', $type);
return $this->filterResponse($response, $request, 'A "core.request" listener returned a non response object.', $type);
}
// load controller
@ -93,8 +93,7 @@ class HttpKernel implements HttpKernelInterface
}
$event = new Event($this, 'core.controller', array('request_type' => $type, 'request' => $request));
$this->dispatcher->filter($event, $controller);
$controller = $event->getReturnValue();
$controller = $this->dispatcher->filter($event, $controller);
// controller must be a callable
if (!is_callable($controller)) {
@ -109,9 +108,9 @@ class HttpKernel implements HttpKernelInterface
// view
$event = new Event($this, 'core.view', array('request_type' => $type, 'request' => $request));
$this->dispatcher->filter($event, $retval);
$response = $this->dispatcher->filter($event, $retval);
return $this->filterResponse($event->getReturnValue(), $request, sprintf('The controller must return a response (instead of %s).', is_object($event->getReturnValue()) ? 'an object of class '.get_class($event->getReturnValue()) : is_array($event->getReturnValue()) ? 'an array' : str_replace("\n", '', var_export($event->getReturnValue(), true))), $type);
return $this->filterResponse($response, $request, sprintf('The controller must return a response (instead of %s).', is_object($response) ? 'an object of class '.get_class($response) : is_array($response) ? 'an array' : str_replace("\n", '', var_export($response, true))), $type);
}
/**
@ -131,8 +130,7 @@ class HttpKernel implements HttpKernelInterface
throw new \RuntimeException($message);
}
$event = $this->dispatcher->filter(new Event($this, 'core.response', array('request_type' => $type, 'request' => $request)), $response);
$response = $event->getReturnValue();
$response = $this->dispatcher->filter(new Event($this, 'core.response', array('request_type' => $type, 'request' => $request)), $response);
if (!$response instanceof Response) {
throw new \RuntimeException('A "core.response" listener returned a non response object.');

View File

@ -85,13 +85,11 @@ class Firewall
}
// initiate the listener chain
$e = $this->dispatcher->notifyUntil(new Event($request, 'core.security', array('request' => $request)));
if ($e->isProcessed()) {
$event->setReturnValue($e->getReturnValue());
$ret = $this->dispatcher->notifyUntil($event = new Event($request, 'core.security', array('request' => $request)));
if ($event->isProcessed()) {
$event->setProcessed();
return true;
return $ret;
}
return;
}
}

View File

@ -101,9 +101,9 @@ class BasicAuthenticationListener implements ListenerInterface
return;
}
$event->setReturnValue($this->authenticationEntryPoint->start($request, $failed));
$event->setProcessed();
return true;
return $this->authenticationEntryPoint->start($request, $failed);
}
}
}

View File

@ -70,9 +70,9 @@ class ChannelListener implements ListenerInterface
$this->logger->debug('Redirecting to HTTPS');
}
$event->setReturnValue($this->authenticationEntryPoint->start($request));
$event->setProcessed();
return true;
return $this->authenticationEntryPoint->start($request);
}
if ('http' === $channel && $request->isSecure()) {
@ -80,9 +80,9 @@ class ChannelListener implements ListenerInterface
$this->logger->debug('Redirecting to HTTP');
}
$event->setReturnValue($this->authenticationEntryPoint->start($request));
$event->setProcessed();
return true;
return $this->authenticationEntryPoint->start($request);
}
}
}

View File

@ -131,9 +131,9 @@ class ExceptionListener implements ListenerInterface
return;
}
$event->setReturnValue($response);
$event->setProcessed();
return true;
return $response;
}
protected function startAuthentication(Request $request, AuthenticationException $reason)

View File

@ -100,9 +100,9 @@ abstract class FormAuthenticationListener
$response = $this->onFailure($event->getSubject(), $request, $failed);
}
$event->setReturnValue($response);
$event->setProcessed();
return true;
return $response;
}
protected function onFailure($kernel, Request $request, \Exception $failed)

View File

@ -96,8 +96,9 @@ class LogoutListener implements ListenerInterface
}
$this->securityContext->setToken(null);
$event->setReturnValue($response);
return true;
$event->setProcessed();
return $response;
}
}

View File

@ -103,9 +103,9 @@ class SwitchUserListener implements ListenerInterface
$request->server->set('QUERY_STRING', '');
$response->setRedirect($request->getUri(), 302);
$event->setReturnValue($response);
$event->setProcessed();
return true;
return $return;
}
/**

View File

@ -572,43 +572,41 @@ class HttpKernel implements HttpKernelInterface
throw $e;
}
$event = new Event($this, 'core.exception', array('request_type' => $type, 'request' => $request, 'exception' => $e));
$this->dispatcher->notifyUntil($event);
$response = $this->dispatcher->notifyUntil($event);
if (!$event->isProcessed()) {
throw $e;
}
$response = $this->filterResponse($event->getReturnValue(), $request, 'A "core.exception" listener returned a non response object.', $type);
$response = $this->filterResponse($response, $request, 'A "core.exception" listener returned a non response object.', $type);
}
return $response;
}
protected function handleRaw(Request $request, $type = self::MASTER_REQUEST)
{
$event = new Event($this, 'core.request', array('request_type' => $type, 'request' => $request));
$this->dispatcher->notifyUntil($event);
$response = $this->dispatcher->notifyUntil($event);
if ($event->isProcessed()) {
return $this->filterResponse($event->getReturnValue(), $request, 'A "core.request" listener returned a non response object.', $type);
return $this->filterResponse($response, $request, 'A "core.request" listener returned a non response object.', $type);
}
if (false === $controller = $this->resolver->getController($request)) {
throw new NotFoundHttpException(sprintf('Unable to find the controller for "%s", check your route configuration.', $request->getPathInfo()));
}
$event = new Event($this, 'core.controller', array('request_type' => $type, 'request' => $request));
$this->dispatcher->filter($event, $controller);
$controller = $event->getReturnValue();
$controller = $this->dispatcher->filter($event, $controller);
if (!is_callable($controller)) {
throw new \LogicException(sprintf('The controller must be a callable (%s).', var_export($controller, true)));
}
$arguments = $this->resolver->getArguments($request, $controller);
$retval = call_user_func_array($controller, $arguments);
$event = new Event($this, 'core.view', array('request_type' => $type, 'request' => $request));
$this->dispatcher->filter($event, $retval);
return $this->filterResponse($event->getReturnValue(), $request, sprintf('The controller must return a response (instead of %s).', is_object($event->getReturnValue()) ? 'an object of class '.get_class($event->getReturnValue()) : is_array($event->getReturnValue()) ? 'an array' : str_replace("\n", '', var_export($event->getReturnValue(), true))), $type);
$response = $this->dispatcher->filter($event, $retval);
return $this->filterResponse($response, $request, sprintf('The controller must return a response (instead of %s).', is_object($response) ? 'an object of class '.get_class($response) : is_array($response) ? 'an array' : str_replace("\n", '', var_export($response, true))), $type);
}
protected function filterResponse($response, $request, $message, $type)
{
if (!$response instanceof Response) {
throw new \RuntimeException($message);
}
$event = $this->dispatcher->filter(new Event($this, 'core.response', array('request_type' => $type, 'request' => $request)), $response);
$response = $event->getReturnValue();
$response = $this->dispatcher->filter(new Event($this, 'core.response', array('request_type' => $type, 'request' => $request)), $response);
if (!$response instanceof Response) {
throw new \RuntimeException('A "core.response" listener returned a non response object.');
}

View File

@ -55,7 +55,6 @@ class EventDispatcherTest extends \PHPUnit_Framework_TestCase
$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');
$this->assertEquals($event, $e, '->notify() returns the event object');
$listener->reset();
$dispatcher = new EventDispatcher();
@ -71,16 +70,15 @@ class EventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'listenToFoo'));
$dispatcher->connect('foo', array($listener, 'listenToFooBis'));
$e = $dispatcher->notifyUntil($event = new Event(new \stdClass(), 'foo'));
$this->assertEquals('listenToFoolistenToFooBis', $listener->getValue(), '->notifyUntil() notifies all registered listeners in order and stops if it returns true');
$this->assertEquals($event, $e, '->notifyUntil() returns the event object');
$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');
$listener->reset();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'listenToFooBis'));
$dispatcher->connect('foo', array($listener, 'listenToFoo'));
$e = $dispatcher->notifyUntil($event = new Event(new \stdClass(), 'foo'));
$this->assertEquals('listenToFooBis', $listener->getValue(), '->notifyUntil() notifies all registered listeners in order and stops if it returns true');
$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');
}
public function testFilter()
@ -89,16 +87,15 @@ class EventDispatcherTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'filterFoo'));
$dispatcher->connect('foo', array($listener, 'filterFooBis'));
$e = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo');
$this->assertEquals('-*foo*-', $e->getReturnValue(), '->filter() filters a value');
$this->assertEquals($event, $e, '->filter() returns the event object');
$ret = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo');
$this->assertEquals('-*foo*-', $ret, '->filter() returns the filtered value');
$listener->reset();
$dispatcher = new EventDispatcher();
$dispatcher->connect('foo', array($listener, 'filterFooBis'));
$dispatcher->connect('foo', array($listener, 'filterFoo'));
$e = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo');
$this->assertEquals('*-foo-*', $e->getReturnValue(), '->filter() filters a value');
$ret = $dispatcher->filter($event = new Event(new \stdClass(), 'foo'), 'foo');
$this->assertEquals('*-foo-*', $ret, '->filter() returns the filtered value');
}
}
@ -126,7 +123,7 @@ class Listener
{
$this->value .= 'listenToFooBis';
return true;
$event->setProcessed();
}
function getValue()

View File

@ -50,20 +50,12 @@ class EventTest extends \PHPUnit_Framework_TestCase
$event = new Event($this->subject, 'name', $this->parameters);
}
public function testSetGetReturnValue()
{
$event = $this->createEvent();
$event->setReturnValue('foo');
$this->assertEquals('foo', $event->getReturnValue(), '->getReturnValue() returns the return value of the event');
}
public function testSetIsProcessed()
{
$event = $this->createEvent();
$event->setProcessed(true);
$this->assertFalse($event->isProcessed(), '->isProcessed() returns false by default');
$event->setProcessed();
$this->assertTrue($event->isProcessed(), '->isProcessed() returns true if the event has been processed');
$event->setProcessed(false);
$this->assertFalse($event->isProcessed(), '->setProcessed() changes the processed status');
}
protected function createEvent()

View File

@ -44,9 +44,9 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$dispatcher->connect('core.exception', function ($event)
{
$event->setReturnValue(new Response($event->get('exception')->getMessage()));
$event->setProcessed();
return true;
return new Response($event->get('exception')->getMessage());
});
$kernel = new HttpKernel($dispatcher, $this->getResolver(function () { throw new \RuntimeException('foo'); }));
@ -59,9 +59,9 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase
$dispatcher = new EventDispatcher();
$dispatcher->connect('core.request', function ($event)
{
$event->setReturnValue(new Response('hello'));
$event->setProcessed();
return true;
return new Response('hello');
});
$kernel = new HttpKernel($dispatcher, $this->getResolver());