Commit c9dc3f49 authored by Fabien Potencier's avatar Fabien Potencier

merged branch romainneutron/Terminable (PR #314)

Commits
-------

5914d4fa TerminableInterface implementation in Application

Discussion
----------

TerminableInterface implementation in Application

It adds finish filters which are triggered after the response has been sent.

---------------------------------------------------------------------------

by igorw at 2012-04-30T22:19:43Z

Cool feature, I can see how this can be useful. Thanks for implementing this!

---------------------------------------------------------------------------

by romainneutron at 2012-04-30T22:24:05Z

Corrections done :)

---------------------------------------------------------------------------

by igorw at 2012-04-30T22:38:36Z

Did not test it, but looks good.

---------------------------------------------------------------------------

by fabpot at 2012-05-01T05:18:06Z

Can you add a note in the CHANGELOG as well?
parents 25abd1a8 5914d4fa
...@@ -352,11 +352,12 @@ object that is returned by the routing methods:: ...@@ -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 It only makes sense to name routes if you use providers that make use
of the ``RouteCollection``. 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 Silex allows you to run code before, after every request and even after the
through ``before`` and ``after`` filters. All you need to do is pass a closure:: response has been sent. This happens through ``before``, ``after`` and ``finish``
filters. All you need to do is pass a closure::
$app->before(function () { $app->before(function () {
// set up // set up
...@@ -366,6 +367,10 @@ through ``before`` and ``after`` filters. All you need to do is pass a closure:: ...@@ -366,6 +367,10 @@ through ``before`` and ``after`` filters. All you need to do is pass a closure::
// tear down // tear down
}); });
$app->finish(function () {
// after response has been sent
});
The before filter has access to the current Request, and can short-circuit The before filter has access to the current Request, and can short-circuit
the whole rendering by returning a Response:: the whole rendering by returning a Response::
...@@ -382,6 +387,12 @@ The after filter has access to the Request and the Response:: ...@@ -382,6 +387,12 @@ The after filter has access to the Request and the Response::
// tweak 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:: .. note::
The filters are only run for the "master" Request. The filters are only run for the "master" Request.
......
...@@ -13,12 +13,14 @@ namespace Silex; ...@@ -13,12 +13,14 @@ namespace Silex;
use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\HttpKernel\Event\KernelEvent; use Symfony\Component\HttpKernel\Event\KernelEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\HttpKernel\EventListener\ResponseListener; use Symfony\Component\HttpKernel\EventListener\ResponseListener;
use Symfony\Component\HttpKernel\EventListener\RouterListener; use Symfony\Component\HttpKernel\EventListener\RouterListener;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
...@@ -49,7 +51,7 @@ use Silex\ControllerResolver; ...@@ -49,7 +51,7 @@ use Silex\ControllerResolver;
* *
* @author Fabien Potencier <fabien@symfony.com> * @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@'; const VERSION = '@package_version@';
...@@ -254,6 +256,22 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -254,6 +256,22 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
}, $priority); }, $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. * Aborts the current request by sending a proper HTTP error.
* *
...@@ -393,7 +411,9 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -393,7 +411,9 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$request = Request::createFromGlobals(); $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 ...@@ -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, * Executes registered error handlers until a response is returned,
* in which case it returns it to the client. * in which case it returns it to the client.
......
...@@ -20,5 +20,6 @@ final class SilexEvents ...@@ -20,5 +20,6 @@ final class SilexEvents
{ {
const BEFORE = 'silex.before'; const BEFORE = 'silex.before';
const AFTER = 'silex.after'; const AFTER = 'silex.after';
const FINISH = 'silex.finish';
const ERROR = 'silex.error'; const ERROR = 'silex.error';
} }
...@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Request; ...@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernelInterface;
/** /**
...@@ -264,6 +265,33 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase ...@@ -264,6 +265,33 @@ class ApplicationTest extends \PHPUnit_Framework_TestCase
$this->assertSame(array('before_triggered', 'middleware_triggered', 'route_triggered'), $middlewareTarget); $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 * @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