Commit 5914d4fa authored by Romain Neutron's avatar Romain Neutron

TerminableInterface implementation in Application

It adds finish filters which are triggered after the response has been sent.
parent 25abd1a8
......@@ -352,11 +352,12 @@ object that is returned by the routing methods::
It only makes sense to name routes if you use providers that make use
of the ``RouteCollection``.
Before and after filters
------------------------
Before, after and finish filters
--------------------------------
Silex allows you to run code before and after every request. This happens
through ``before`` and ``after`` filters. All you need to do is pass a closure::
Silex allows you to run code before, after every request and even after the
response has been sent. This happens through ``before``, ``after`` and ``finish``
filters. All you need to do is pass a closure::
$app->before(function () {
// set up
......@@ -366,6 +367,10 @@ through ``before`` and ``after`` filters. All you need to do is pass a closure::
// tear down
});
$app->finish(function () {
// after response has been sent
});
The before filter has access to the current Request, and can short-circuit
the whole rendering by returning a Response::
......@@ -382,6 +387,12 @@ The after filter has access to the Request and the Response::
// tweak the Response
});
The finish filter has access to the Request and the Response::
$app->finish(function (Request $request, Response $response) {
// send e-mails ...
});
.. note::
The filters are only run for the "master" Request.
......
......@@ -13,12 +13,14 @@ namespace Silex;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\HttpKernel\Event\KernelEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\EventListener\ResponseListener;
use Symfony\Component\HttpKernel\EventListener\RouterListener;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
......@@ -49,7 +51,7 @@ use Silex\ControllerResolver;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Application extends \Pimple implements HttpKernelInterface, EventSubscriberInterface
class Application extends \Pimple implements HttpKernelInterface, EventSubscriberInterface, TerminableInterface
{
const VERSION = '@package_version@';
......@@ -254,6 +256,22 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
}, $priority);
}
/**
* Registers a finish filter.
*
* Finish filters are run after the response has been sent.
*
* @param mixed $callback Finish filter callback
* @param integer $priority The higher this value, the earlier an event
* listener will be triggered in the chain (defaults to 0)
*/
public function finish($callback, $priority = 0)
{
$this['dispatcher']->addListener(SilexEvents::FINISH, function (PostResponseEvent $event) use ($callback) {
call_user_func($callback, $event->getRequest(), $event->getResponse());
}, $priority);
}
/**
* Aborts the current request by sending a proper HTTP error.
*
......@@ -393,7 +411,9 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$request = Request::createFromGlobals();
}
$this->handle($request)->send();
$response = $this->handle($request);
$response->send();
$this->terminate($request, $response);
}
/**
......@@ -480,6 +500,17 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
}
}
/**
* Runs finish filters, TerminableInterface implementation
*
* @param Request $request
* @param Response $response
*/
public function terminate(Request $request, Response $response)
{
$this['dispatcher']->dispatch(SilexEvents::FINISH, new PostResponseEvent($this, $request, $response));
}
/**
* Executes registered error handlers until a response is returned,
* in which case it returns it to the client.
......
......@@ -20,5 +20,6 @@ final class SilexEvents
{
const BEFORE = 'silex.before';
const AFTER = 'silex.after';
const FINISH = 'silex.finish';
const ERROR = 'silex.error';
}
......@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\HttpKernelInterface;
/**
......@@ -264,6 +265,33 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array('before_triggered', 'middleware_triggered', 'route_triggered'), $middlewareTarget);
}
public function testFinishFilter()
{
$containerTarget = array();
$app = new Application();
$app->finish(function () use (&$containerTarget) {
$containerTarget[] = '4_filterFinish';
});
$app->get('/foo', function () use (&$containerTarget) {
$containerTarget[] = '1_routeTriggered';
return new StreamedResponse(function() use (&$containerTarget) {
$containerTarget[] = '3_responseSent';
});
});
$app->after(function () use (&$containerTarget) {
$containerTarget[] = '2_filterAfter';
});
$app->run(Request::create('/foo'));
$this->assertSame(array('1_routeTriggered', '2_filterAfter', '3_responseSent', '4_filterFinish'), $containerTarget);
}
/**
* @expectedException RuntimeException
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment