Commit c100a49e authored by Fabien Potencier's avatar Fabien Potencier

merged branch igorw/late-listeners (PR #202)

Commits
-------

43ae6965 fix comment in LazyUrlMatcher
4d5ffca8 add test case for late listeners
51df9f5a Create default set of listeners late

Discussion
----------

Create default set of listeners late

Right now the following subscribers are created together with the
dispatcher:

* app
* exception_handler
* ResponseListener
* RouterListener

With the exception_handler in particular this is an issue because you
can no longer change it after the dispatcher has been created. And it
gets created when you call before(), after() or error(). It means that
you cannot unset it for tests.

The RouterListener has a similar issue because you can no longer change
the UrlMatcher it uses. And you cannot move its creation into a request
listener, because request listeners cannot add other request listeners
(the new ones won't get executed anymore at that point).

This commit ensures that those listeners will be created a lot later,
allowing their dependencies to be modified before runtime.
parents 72e1acc3 43ae6965
...@@ -80,11 +80,11 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -80,11 +80,11 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
$this['dispatcher'] = $this->share(function () use ($app) { $this['dispatcher'] = $this->share(function () use ($app) {
$dispatcher = new EventDispatcher(); $dispatcher = new EventDispatcher();
$dispatcher->addSubscriber($app); $dispatcher->addSubscriber($app);
if (isset($app['exception_handler'])) {
$dispatcher->addSubscriber($app['exception_handler']); $urlMatcher = new LazyUrlMatcher(function () use ($app) {
} return $app['url_matcher'];
$dispatcher->addSubscriber(new ResponseListener($app['charset'])); });
$dispatcher->addSubscriber(new RouterListener($app['url_matcher'])); $dispatcher->addSubscriber(new RouterListener($urlMatcher));
return $dispatcher; return $dispatcher;
}); });
...@@ -354,6 +354,19 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -354,6 +354,19 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
return $this['kernel']->handle($request, $type, $catch); return $this['kernel']->handle($request, $type, $catch);
} }
/**
* Handles onEarlyKernelRequest events.
*/
public function onEarlyKernelRequest(KernelEvent $event)
{
if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
if (isset($this['exception_handler'])) {
$this['dispatcher']->addSubscriber($this['exception_handler']);
}
$this['dispatcher']->addSubscriber(new ResponseListener($this['charset']));
}
}
/** /**
* Handles onKernelRequest events. * Handles onKernelRequest events.
*/ */
...@@ -434,7 +447,10 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe ...@@ -434,7 +447,10 @@ class Application extends \Pimple implements HttpKernelInterface, EventSubscribe
static public function getSubscribedEvents() static public function getSubscribedEvents()
{ {
return array( return array(
KernelEvents::REQUEST => 'onKernelRequest', KernelEvents::REQUEST => array(
array('onEarlyKernelRequest', 256),
array('onKernelRequest')
),
KernelEvents::CONTROLLER => 'onKernelController', KernelEvents::CONTROLLER => 'onKernelController',
KernelEvents::RESPONSE => 'onKernelResponse', KernelEvents::RESPONSE => 'onKernelResponse',
KernelEvents::EXCEPTION => 'onKernelException', KernelEvents::EXCEPTION => 'onKernelException',
......
<?php
/*
* This file is part of the Silex framework.
*
* (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 Silex;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
/**
* Implements a lazy UrlMatcher.
*
* @author Igor Wiedler <igor@wiedler.ch>
*/
class LazyUrlMatcher implements UrlMatcherInterface
{
private $factory;
private $urlMatcher;
public function __construct(\Closure $factory)
{
$this->factory = $factory;
}
public function getUrlMatcher()
{
$urlMatcher = call_user_func($this->factory);
if (!$urlMatcher instanceof UrlMatcherInterface) {
throw new \LogicException("Factory supplied to LazyUrlMatcher must return implementation of UrlMatcherInterface.");
}
return $urlMatcher;
}
/**
* {@inheritdoc}
*/
public function match($pathinfo)
{
return $this->getUrlMatcher()->match($pathinfo);
}
/**
* {@inheritdoc}
*/
public function setContext(RequestContext $context)
{
$this->getUrlMatcher()->setContext($context);
}
/**
* {@inheritdoc}
*/
public function getContext()
{
return $this->getUrlMatcher()->getContext();
}
}
...@@ -209,13 +209,36 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase ...@@ -209,13 +209,36 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase
try { try {
$request = Request::create('/foo'); $request = Request::create('/foo');
$this->checkRouteResponse($app, '/foo', 'foo exception handler', 'should accept a string response from the error handler'); $app->handle($request);
$this->fail('->handle() should not catch exceptions thrown from an error handler'); $this->fail('->handle() should not catch exceptions thrown from an error handler');
} catch (\RuntimeException $e) { } catch (\RuntimeException $e) {
$this->assertEquals('foo exception handler exception', $e->getMessage()); $this->assertEquals('foo exception handler exception', $e->getMessage());
} }
} }
public function testRemoveExceptionHandlerAfterDispatcherAccess()
{
$app = new Application();
$app->match('/foo', function () {
throw new \RuntimeException('foo exception');
});
$app->before(function () {
// just making sure the dispatcher gets created
});
unset($app['exception_handler']);
try {
$request = Request::create('/foo');
$app->handle($request);
$this->fail('default exception handler should have been removed');
} catch (\RuntimeException $e) {
$this->assertEquals('foo exception', $e->getMessage());
}
}
protected function checkRouteResponse($app, $path, $expectedContent, $method = 'get', $message = null) protected function checkRouteResponse($app, $path, $expectedContent, $method = 'get', $message = null)
{ {
$request = Request::create($path, $method); $request = Request::create($path, $method);
......
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