From c55f1ea8af80cf5c96b27d57b7bf97843ec73aef Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 18 Apr 2013 10:33:03 +0200 Subject: [PATCH 1/7] added a RequestStack class --- .../Extension/HttpKernelExtensionTest.php | 39 +++- .../Resources/config/fragment_renderer.xml | 2 +- .../Resources/config/routing.xml | 2 +- .../Resources/config/services.xml | 10 + .../FrameworkBundle/Resources/config/web.xml | 2 +- .../ContainerAwareHttpKernel.php | 7 +- .../HttpKernel/Event/RequestFinishedEvent.php | 21 +++ .../EventListener/LocaleListener.php | 47 +++-- .../EventListener/RouterListener.php | 16 +- .../HttpKernel/Fragment/FragmentHandler.php | 25 +-- .../Component/HttpKernel/HttpKernel.php | 34 +++- .../Component/HttpKernel/KernelEvents.php | 10 + .../Component/HttpKernel/RequestContext.php | 55 ++++++ .../Component/HttpKernel/RequestStack.php | 84 +++++++++ .../ContainerAwareHttpKernelTest.php | 172 +++++++++--------- .../EventListener/LocaleListenerTest.php | 40 +++- .../EventListener/RouterListenerTest.php | 16 +- .../Tests/Fragment/FragmentHandlerTest.php | 20 +- .../HttpKernel/Tests/HttpKernelTest.php | 14 ++ 19 files changed, 477 insertions(+), 139 deletions(-) create mode 100644 src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php create mode 100644 src/Symfony/Component/HttpKernel/RequestContext.php create mode 100644 src/Symfony/Component/HttpKernel/RequestStack.php diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php index ce22481921f5..9739d96ef385 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -14,7 +14,9 @@ use Symfony\Bridge\Twig\Extension\HttpKernelExtension; use Symfony\Bridge\Twig\Tests\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; +use Symfony\Component\HttpKernel\RequestContext; class HttpKernelExtensionTest extends TestCase { @@ -23,13 +25,30 @@ class HttpKernelExtensionTest extends TestCase */ public function testFragmentWithError() { - $kernel = $this->getFragmentHandler($this->throwException(new \Exception('foo'))); + $renderer = $this->getFragmentHandler($this->throwException(new \Exception('foo'))); - $loader = new \Twig_Loader_Array(array('index' => '{{ fragment("foo") }}')); - $twig = new \Twig_Environment($loader, array('debug' => true, 'cache' => false)); - $twig->addExtension(new HttpKernelExtension($kernel)); + $this->renderTemplate($renderer); + } + + public function testRenderFragment() + { + $renderer = $this->getFragmentHandler($this->returnValue(new Response('html'))); + + $response = $this->renderTemplate($renderer); - $this->renderTemplate($kernel); + $this->assertEquals('html', $response); + } + + public function testUnknownFragmentRenderer() + { + $context = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\RequestContext') + ->disableOriginalConstructor() + ->getMock() + ; + $renderer = new FragmentHandler($context, array()); + + $this->setExpectedException('InvalidArgumentException', 'The "inline" renderer does not exist.'); + $renderer->render('/foo'); } protected function getFragmentHandler($return) @@ -38,8 +57,14 @@ protected function getFragmentHandler($return) $strategy->expects($this->once())->method('getName')->will($this->returnValue('inline')); $strategy->expects($this->once())->method('render')->will($return); - $renderer = new FragmentHandler(array($strategy)); - $renderer->setRequest(Request::create('/')); + $context = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\RequestContext') + ->disableOriginalConstructor() + ->getMock() + ; + + $context->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); + + $renderer = new FragmentHandler($context, array($strategy)); return $renderer; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml index 4773339906a4..e613ed11c1d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -15,9 +15,9 @@ + %kernel.debug% - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 9e21db451915..34eb33214749 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -92,9 +92,9 @@ + - diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index 674e28f1c98a..607634d1a742 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -12,6 +12,8 @@ Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer Symfony\Component\HttpKernel\Config\FileLocator Symfony\Component\HttpKernel\UriSigner + Symfony\Component\HttpKernel\RequestStack + Symfony\Component\HttpKernel\RequestContext @@ -23,6 +25,14 @@ + + + + + + + + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index 177821a5afb2..d2984f5eb460 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -37,8 +37,8 @@ %kernel.default_locale% + - diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php index c9b8a211d429..214e3b255dae 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernel; +use Symfony\Component\HttpKernel\RequestStack; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -28,6 +29,7 @@ class ContainerAwareHttpKernel extends HttpKernel { protected $container; + protected $requestStack; /** * Constructor. @@ -35,10 +37,11 @@ class ContainerAwareHttpKernel extends HttpKernel * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance * @param ContainerInterface $container A ContainerInterface instance * @param ControllerResolverInterface $controllerResolver A ControllerResolverInterface instance + * @param RequestStack $requestStack A stack for master/sub requests */ - public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver) + public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver, RequestStack $requestStack) { - parent::__construct($dispatcher, $controllerResolver); + parent::__construct($dispatcher, $controllerResolver, $requestStack); $this->container = $container; diff --git a/src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php b/src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php new file mode 100644 index 000000000000..c9d3c39a72cb --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php @@ -0,0 +1,21 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Event; + +/** + * Triggered whenever a request is fully processed. + * + * @author Benjamin Eberlei + */ +class RequestFinishedEvent extends KernelEvent +{ +} diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php index 0b864c02f2bc..7b6f2ad208cc 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -12,7 +12,9 @@ namespace Symfony\Component\HttpKernel\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestFinishedEvent; use Symfony\Component\HttpKernel\KernelEvents; +use Symfony\Component\HttpKernel\RequestContext; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RequestContextAwareInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -26,41 +28,64 @@ class LocaleListener implements EventSubscriberInterface { private $router; private $defaultLocale; + private $requestContext; - public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null) + public function __construct($defaultLocale = 'en', RequestContext $requestContext, RequestContextAwareInterface $router = null) { $this->defaultLocale = $defaultLocale; + $this->requestContext = $requestContext; $this->router = $router; } - public function setRequest(Request $request = null) + public function onKernelRequest(GetResponseEvent $event) + { + $request = $event->getRequest(); + $request->setDefaultLocale($this->defaultLocale); + + $this->setLocale($request); + $this->setRouterContext($request); + } + + public function onKernelRequestFinished(RequestFinishedEvent $event) { - if (null === $request) { + $this->resetRouterContext(); + } + + private function resetRouterContext() + { + if ($this->requestContext === null) { + return; + } + + $parentRequest = $this->requestContext->getParentRequest(); + + if ($parentRequest === null) { return; } + $this->setRouterContext($parentRequest); + } + + private function setLocale(Request $request) + { if ($locale = $request->attributes->get('_locale')) { $request->setLocale($locale); } + } + private function setRouterContext(Request $request) + { if (null !== $this->router) { $this->router->getContext()->setParameter('_locale', $request->getLocale()); } } - public function onKernelRequest(GetResponseEvent $event) - { - $request = $event->getRequest(); - $request->setDefaultLocale($this->defaultLocale); - - $this->setRequest($request); - } - public static function getSubscribedEvents() { return array( // must be registered after the Router to have access to the _locale KernelEvents::REQUEST => array(array('onKernelRequest', 16)), + KernelEvents::REQUEST_FINISHED => array(array('onKernelRequestFinished', 0)), ); } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index 777fd11bf40a..f2bad02968e6 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -13,9 +13,11 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestFinishedEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\RequestContext as KernelRequestContext; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; @@ -36,6 +38,7 @@ class RouterListener implements EventSubscriberInterface private $context; private $logger; private $request; + private $kernelContext; /** * Constructor. @@ -46,7 +49,7 @@ class RouterListener implements EventSubscriberInterface * * @throws \InvalidArgumentException */ - public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null) + public function __construct($matcher, KernelRequestContext $kernelContext, RequestContext $context = null, LoggerInterface $logger = null) { if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); @@ -58,6 +61,7 @@ public function __construct($matcher, RequestContext $context = null, LoggerInte $this->matcher = $matcher; $this->context = $context ?: $matcher->getContext(); + $this->kernelContext = $kernelContext; $this->logger = $logger; } @@ -71,7 +75,7 @@ public function __construct($matcher, RequestContext $context = null, LoggerInte * * @param Request|null $request A Request instance */ - public function setRequest(Request $request = null) + private function populateRoutingContext(Request $request = null) { if (null !== $request && $this->request !== $request) { $this->context->fromRequest($request); @@ -79,6 +83,11 @@ public function setRequest(Request $request = null) $this->request = $request; } + public function onKernelRequestFinished(RequestFinishedEvent $event) + { + $this->populateRoutingContext($this->kernelContext->getParentRequest()); + } + public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest(); @@ -86,7 +95,7 @@ public function onKernelRequest(GetResponseEvent $event) // initialize the context that is also used by the generator (assuming matcher and generator share the same context instance) // we call setRequest even if most of the time, it has already been done to keep compatibility // with frameworks which do not use the Symfony service container - $this->setRequest($request); + $this->populateRoutingContext($request); if ($request->attributes->has('_controller')) { // routing is already done @@ -139,6 +148,7 @@ public static function getSubscribedEvents() { return array( KernelEvents::REQUEST => array(array('onKernelRequest', 32)), + KernelEvents::REQUEST_FINISHED => array(array('onKernelRequestFinished', 0)), ); } } diff --git a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php index af9b9ba98b74..099f35a306bf 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpKernel\Controller\ControllerReference; +use Symfony\Component\HttpKernel\RequestContext; /** * Renders a URI that represents a resource fragment. @@ -30,7 +31,7 @@ class FragmentHandler { private $debug; private $renderers; - private $request; + private $context; /** * Constructor. @@ -38,8 +39,9 @@ class FragmentHandler * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances * @param Boolean $debug Whether the debug mode is enabled or not */ - public function __construct(array $renderers = array(), $debug = false) + public function __construct(RequestContext $context, array $renderers = array(), $debug = false) { + $this->context = $context; $this->renderers = array(); foreach ($renderers as $renderer) { $this->addRenderer($renderer); @@ -57,16 +59,6 @@ public function addRenderer(FragmentRendererInterface $renderer) $this->renderers[$renderer->getName()] = $renderer; } - /** - * Sets the current Request. - * - * @param Request $request The current Request - */ - public function setRequest(Request $request = null) - { - $this->request = $request; - } - /** * Renders a URI and returns the Response content. * @@ -93,11 +85,7 @@ public function render($uri, $renderer = 'inline', array $options = array()) throw new \InvalidArgumentException(sprintf('The "%s" renderer does not exist.', $renderer)); } - if (null === $this->request) { - throw new \LogicException('Rendering a fragment can only be done when handling a master Request.'); - } - - return $this->deliver($this->renderers[$renderer]->render($uri, $this->request, $options)); + return $this->deliver($this->renderers[$renderer]->render($uri, $this->context->getCurrentRequest(), $options)); } /** @@ -115,7 +103,8 @@ public function render($uri, $renderer = 'inline', array $options = array()) protected function deliver(Response $response) { if (!$response->isSuccessful()) { - throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->request->getUri(), $response->getStatusCode())); + $request = $this->context->getCurrentRequest(); + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $request->getUri(), $response->getStatusCode())); } if (!$response instanceof StreamedResponse) { diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 837a16ff370e..71c1b91fc8ac 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestFinishedEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -35,19 +36,22 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface { protected $dispatcher; protected $resolver; + protected $requestStack; /** * Constructor * - * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance - * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + * @param ControllerResolverInterface $resolver A ControllerResolverInterface instance + * @param RequestStack $requestStack A stack for master/sub requests * * @api */ - public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver) + public function __construct(EventDispatcherInterface $dispatcher, ControllerResolverInterface $resolver, RequestStack $requestStack = null) { $this->dispatcher = $dispatcher; $this->resolver = $resolver; + $this->requestStack = $requestStack ?: new RequestStack(); } /** @@ -60,6 +64,8 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ try { return $this->handleRaw($request, $type); } catch (\Exception $e) { + $this->finishRequest($request, $type); + if (false === $catch) { throw $e; } @@ -93,6 +99,8 @@ public function terminate(Request $request, Response $response) */ private function handleRaw(Request $request, $type = self::MASTER_REQUEST) { + $this->requestStack->push($request); + // request $event = new GetResponseEvent($this, $request, $type); $this->dispatcher->dispatch(KernelEvents::REQUEST, $event); @@ -156,9 +164,29 @@ private function filterResponse(Response $response, Request $request, $type) $this->dispatcher->dispatch(KernelEvents::RESPONSE, $event); + $this->finishRequest($request, $type); + return $event->getResponse(); } + /** + * Publish event finished event, then pop the request from the stack. + * + * Note: Order of the operations is important here, otherwise operations + * such as {@link RequestStack::getParentRequest()} can lead to weird + * results. + * + * @param Request $request + * @param int $type + * + * @return void + */ + private function finishRequest(Request $request, $type) + { + $this->dispatcher->dispatch(KernelEvents::REQUEST_FINISHED, new RequestFinishedEvent($this, $request, $type)); + $this->requestStack->pop(); + } + /** * Handles an exception by trying to convert it to a Response. * diff --git a/src/Symfony/Component/HttpKernel/KernelEvents.php b/src/Symfony/Component/HttpKernel/KernelEvents.php index fce48ac3a6ed..e1ad0fa004fe 100644 --- a/src/Symfony/Component/HttpKernel/KernelEvents.php +++ b/src/Symfony/Component/HttpKernel/KernelEvents.php @@ -102,4 +102,14 @@ final class KernelEvents * @var string */ const TERMINATE = 'kernel.terminate'; + + /** + * The REQUEST_FINISHED event occurs when a response was generated for a request. + * + * This event allows you to reset the global and environmental state of + * the application, when it was changed during the request. + * + * @var string + */ + const REQUEST_FINISHED = 'kernel.request_finished'; } diff --git a/src/Symfony/Component/HttpKernel/RequestContext.php b/src/Symfony/Component/HttpKernel/RequestContext.php new file mode 100644 index 000000000000..f6fdaf3bbe83 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/RequestContext.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +/** + * Registry for Requests. + * + * Facade for RequestStack that prevents modification of the stack, + * so that users don't accidentally push()/pop() from the stack and + * mess up the request cycle. + * + * @author Benjamin Eberlei + */ +class RequestContext +{ + private $stack; + + public function __construct(RequestStack $stack) + { + $this->stack = $stack; + } + + /** + * @return Request + */ + public function getCurrentRequest() + { + return $this->stack->getCurrentRequest(); + } + + /** + * @return Request + */ + public function getMasterRequest() + { + return $this->stack->getMasterRequest(); + } + + /** + * @return Request|null + */ + public function getParentRequest() + { + return $this->stack->getParentRequest(); + } +} diff --git a/src/Symfony/Component/HttpKernel/RequestStack.php b/src/Symfony/Component/HttpKernel/RequestStack.php new file mode 100644 index 000000000000..c8de54981d93 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/RequestStack.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\HttpFoundation\Request; + +/** + * Request stack that controls the lifecycle of requests. + * + * Notifies services of changes in the stack. + * + * @author Benjamin Eberlei + */ +class RequestStack +{ + /** + * @var Request[] + */ + private $requests = array(); + + public function push(Request $request) + { + $this->requests[] = $request; + } + + /** + * Pop the current request from the stack. + * + * This operation lets the current request go out of scope. + * + * @return Request + */ + public function pop() + { + return array_pop($this->requests); + } + + /** + * @return Request|null + */ + public function getCurrentRequest() + { + return end($this->requests) ?: null; + } + + /** + * @return Request|null + */ + public function getMasterRequest() + { + if (!$this->requests) { + return null; + } + + return $this->requests[0]; + } + + /** + * Return the parent request of the current. + * + * If current Request is the master request, method returns null. + * + * @return Request + */ + public function getParentRequest() + { + $pos = count($this->requests) - 2; + + if (!isset($this->requests[$pos])) { + return null; + } + + return $this->requests[$pos]; + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php index 28901dafdd64..292a5e9f45d1 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel; +use Symfony\Component\HttpKernel\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -26,59 +27,49 @@ public function testHandle($type) { $request = new Request(); $expected = new Response(); + $controller = function() use ($expected) { + return $expected; + }; $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container - ->expects($this->once()) - ->method('enterScope') - ->with($this->equalTo('request')) - ; - $container - ->expects($this->once()) - ->method('leaveScope') - ->with($this->equalTo('request')) - ; - $container - ->expects($this->at(0)) - ->method('hasScope') - ->with($this->equalTo('request')) - ->will($this->returnValue(false)); - $container - ->expects($this->at(1)) - ->method('addScope') - ->with($this->isInstanceOf('Symfony\Component\DependencyInjection\Scope')); - // enterScope() - $container - ->expects($this->at(3)) - ->method('set') - ->with($this->equalTo('request'), $this->equalTo($request), $this->equalTo('request')) - ; - $container - ->expects($this->at(4)) - ->method('set') - ->with($this->equalTo('request'), $this->equalTo(null), $this->equalTo('request')) + $this + ->expectsEnterScopeOnce($container) + ->expectsLeaveScopeOnce($container) + ->expectsSetRequestWithAt($container, $request, 3) + ->expectsSetRequestWithAt($container, null, 4) ; $dispatcher = new EventDispatcher(); - $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); - $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver); + $resolver = $this->getResolverMockFor($controller, $request); + $stack = new RequestStack(); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver, $stack); - $controller = function () use ($expected) { + $actual = $kernel->handle($request, $type); + + $this->assertSame($expected, $actual, '->handle() returns the response'); + } + + /** + * @dataProvider getProviderTypes + */ + public function testVerifyRequestStackPushPopDuringHandle($type) + { + $request = new Request(); + $expected = new Response(); + $controller = function() use ($expected) { return $expected; }; - $resolver->expects($this->once()) - ->method('getController') - ->with($request) - ->will($this->returnValue($controller)); - $resolver->expects($this->once()) - ->method('getArguments') - ->with($request, $controller) - ->will($this->returnValue(array())); + $stack = $this->getMock('Symfony\Component\HttpKernel\RequestStack', array('push', 'pop')); + $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); + $stack->expects($this->at(1))->method('pop'); - $actual = $kernel->handle($request, $type); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $dispatcher = new EventDispatcher(); + $resolver = $this->getResolverMockFor($controller, $request); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver, $stack); - $this->assertSame($expected, $actual, '->handle() returns the response'); + $kernel->handle($request, $type); } /** @@ -88,51 +79,23 @@ public function testHandleRestoresThePreviousRequestOnException($type) { $request = new Request(); $expected = new \Exception(); + $controller = function() use ($expected) { + throw $expected; + }; $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); - $container - ->expects($this->once()) - ->method('enterScope') - ->with($this->equalTo('request')) - ; - $container - ->expects($this->once()) - ->method('leaveScope') - ->with($this->equalTo('request')) - ; - $container - ->expects($this->at(0)) - ->method('hasScope') - ->with($this->equalTo('request')) - ->will($this->returnValue(true)); - // enterScope() - $container - ->expects($this->at(2)) - ->method('set') - ->with($this->equalTo('request'), $this->equalTo($request), $this->equalTo('request')) - ; - $container - ->expects($this->at(3)) - ->method('set') - ->with($this->equalTo('request'), $this->equalTo(null), $this->equalTo('request')) + $this + ->expectsEnterScopeOnce($container) + ->expectsLeaveScopeOnce($container) + ->expectsSetRequestWithAt($container, $request, 3) + ->expectsSetRequestWithAt($container, null, 4) ; $dispatcher = new EventDispatcher(); $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); - $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver); - - $controller = function () use ($expected) { - throw $expected; - }; - - $resolver->expects($this->once()) - ->method('getController') - ->with($request) - ->will($this->returnValue($controller)); - $resolver->expects($this->once()) - ->method('getArguments') - ->with($request, $controller) - ->will($this->returnValue(array())); + $resolver = $this->getResolverMockFor($controller, $request); + $stack = new RequestStack(); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver, $stack); try { $kernel->handle($request, $type); @@ -151,4 +114,51 @@ public function getProviderTypes() array(HttpKernelInterface::SUB_REQUEST), ); } + + private function getResolverMockFor($controller, $request) + { + $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); + $resolver->expects($this->once()) + ->method('getController') + ->with($request) + ->will($this->returnValue($controller)); + $resolver->expects($this->once()) + ->method('getArguments') + ->with($request, $controller) + ->will($this->returnValue(array())); + + return $resolver; + } + + private function expectsSetRequestWithAt($container, $with, $at) + { + $container + ->expects($this->at($at)) + ->method('set') + ->with($this->equalTo('request'), $this->equalTo($with), $this->equalTo('request')) + ; + return $this; + } + + private function expectsEnterScopeOnce($container) + { + $container + ->expects($this->once()) + ->method('enterScope') + ->with($this->equalTo('request')) + ; + + return $this; + } + + private function expectsLeaveScopeOnce($container) + { + $container + ->expects($this->once()) + ->method('leaveScope') + ->with($this->equalTo('request')) + ; + + return $this; + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php index 36859baa16b0..2cdfec93de45 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php @@ -12,15 +12,23 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; use Symfony\Component\HttpKernel\EventListener\LocaleListener; +use Symfony\Component\HttpKernel\RequestContext; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; class LocaleListenerTest extends \PHPUnit_Framework_TestCase { + private $context; + + protected function setUp() + { + $this->context = $this->getMock('Symfony\Component\HttpKernel\RequestContext', array(), array(), '', false); + } + public function testDefaultLocaleWithoutSession() { - $listener = new LocaleListener('fr'); + $listener = new LocaleListener('fr', $this->context); $event = $this->getEvent($request = Request::create('/')); $listener->onKernelRequest($event); @@ -34,7 +42,7 @@ public function testLocaleFromRequestAttribute() $request->cookies->set('foo', 'value'); $request->attributes->set('_locale', 'es'); - $listener = new LocaleListener('fr'); + $listener = new LocaleListener('fr', $this->context); $event = $this->getEvent($request); $listener->onKernelRequest($event); @@ -53,15 +61,39 @@ public function testLocaleSetForRoutingContext() $request = Request::create('/'); $request->attributes->set('_locale', 'es'); - $listener = new LocaleListener('fr', $router); + $listener = new LocaleListener('fr', $this->context, $router); $listener->onKernelRequest($this->getEvent($request)); } + public function testRouterResetWithParentRequestOnKernelRequestFinished() + { + if (!class_exists('Symfony\Component\Routing\Router')) { + $this->markTestSkipped('The "Routing" component is not available'); + } + + // the request context is updated + $context = $this->getMock('Symfony\Component\Routing\RequestContext'); + $context->expects($this->once())->method('setParameter')->with('_locale', 'es'); + + $router = $this->getMock('Symfony\Component\Routing\Router', array('getContext'), array(), '', false); + $router->expects($this->once())->method('getContext')->will($this->returnValue($context)); + + $parentRequest = Request::create('/'); + $parentRequest->setLocale('es'); + + $this->context->expects($this->once())->method('getParentRequest')->will($this->returnValue($parentRequest)); + + $event = $this->getMock('Symfony\Component\HttpKernel\Event\RequestFinishedEvent', array(), array(), '', false); + + $listener = new LocaleListener('fr', $this->context, $router); + $listener->onKernelRequestFinished($event); + } + public function testRequestLocaleIsNotOverridden() { $request = Request::create('/'); $request->setLocale('de'); - $listener = new LocaleListener('fr'); + $listener = new LocaleListener('fr', $this->context); $event = $this->getEvent($request); $listener->onKernelRequest($event); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php index 66fe6bd55d50..2bd1810dca46 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; use Symfony\Component\HttpKernel\EventListener\RouterListener; +use Symfony\Component\HttpKernel\RequestContext as KernelRequestContext; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -19,6 +20,13 @@ class RouterListenerTest extends \PHPUnit_Framework_TestCase { + private $kernelContext; + + public function setUp() + { + $this->kernelContext = $this->getMock('Symfony\Component\HttpKernel\RequestContext', array(), array(), '', false); + } + /** * @dataProvider getPortData */ @@ -34,7 +42,7 @@ public function testPort($defaultHttpPort, $defaultHttpsPort, $uri, $expectedHtt ->method('getContext') ->will($this->returnValue($context)); - $listener = new RouterListener($urlMatcher); + $listener = new RouterListener($urlMatcher, $this->kernelContext); $event = $this->createGetResponseEventForUri($uri); $listener->onKernelRequest($event); @@ -72,7 +80,7 @@ private function createGetResponseEventForUri($uri) */ public function testInvalidMatcher() { - new RouterListener(new \stdClass()); + new RouterListener(new \stdClass(), $this->kernelContext); } public function testRequestMatcher() @@ -87,7 +95,7 @@ public function testRequestMatcher() ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) ->will($this->returnValue(array())); - $listener = new RouterListener($requestMatcher, new RequestContext()); + $listener = new RouterListener($requestMatcher, $this->kernelContext, new RequestContext()); $listener->onKernelRequest($event); } @@ -108,7 +116,7 @@ public function testSubRequestWithDifferentMethod() ->method('getContext') ->will($this->returnValue($context)); - $listener = new RouterListener($requestMatcher, new RequestContext()); + $listener = new RouterListener($requestMatcher, $this->kernelContext, new RequestContext()); $listener->onKernelRequest($event); // sub-request with another HTTP method diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php index cec8ae98403a..33e01d54b397 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php @@ -17,12 +17,27 @@ class FragmentHandlerTest extends \PHPUnit_Framework_TestCase { + private $context; + + public function setUp() + { + $this->context = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\RequestContext') + ->disableOriginalConstructor() + ->getMock() + ; + $this->context + ->expects($this->any()) + ->method('getCurrentRequest') + ->will($this->returnValue(Request::create('/'))) + ; + } + /** * @expectedException \InvalidArgumentException */ public function testRenderWhenRendererDoesNotExist() { - $handler = new FragmentHandler(); + $handler = new FragmentHandler($this->context); $handler->render('/', 'foo'); } @@ -72,9 +87,8 @@ protected function getHandler($returnValue, $arguments = array()) call_user_func_array(array($e, 'with'), $arguments); } - $handler = new FragmentHandler(); + $handler = new FragmentHandler($this->context); $handler->addRenderer($renderer); - $handler->setRequest(Request::create('/')); return $handler; } diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php index 1bfa0e5fa822..120e945cd60b 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -238,6 +238,20 @@ public function testTerminate() $this->assertEquals($response, $capturedResponse); } + public function testVerifyRequestStackPushPopDuringHandle() + { + $request = new Request(); + + $stack = $this->getMock('Symfony\Component\HttpKernel\RequestStack', array('push', 'pop')); + $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); + $stack->expects($this->at(1))->method('pop'); + + $dispatcher = new EventDispatcher(); + $kernel = new HttpKernel($dispatcher, $this->getResolver(), $stack); + + $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST); + } + protected function getResolver($controller = null) { if (null === $controller) { From a58a8a69fd7e6922547a827037d182e29fb13211 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 31 Aug 2013 21:46:39 +0200 Subject: [PATCH 2/7] [HttpKernel] changed request_stack to a private service --- .../Bundle/FrameworkBundle/Resources/config/services.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index 607634d1a742..3bcbe16d628c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -28,7 +28,7 @@ - + From f9b10ba1d5715832fd12a930e282abdb3b2ea462 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 31 Aug 2013 22:02:11 +0200 Subject: [PATCH 3/7] [HttpKernel] renamed the kernel finished event --- .../{RequestFinishedEvent.php => FinishRequestEvent.php} | 2 +- .../Component/HttpKernel/EventListener/LocaleListener.php | 6 +++--- .../Component/HttpKernel/EventListener/RouterListener.php | 6 +++--- src/Symfony/Component/HttpKernel/HttpKernel.php | 4 ++-- src/Symfony/Component/HttpKernel/KernelEvents.php | 2 +- .../HttpKernel/Tests/EventListener/LocaleListenerTest.php | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) rename src/Symfony/Component/HttpKernel/Event/{RequestFinishedEvent.php => FinishRequestEvent.php} (89%) diff --git a/src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php b/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php similarity index 89% rename from src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php rename to src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php index c9d3c39a72cb..ee724843cd84 100644 --- a/src/Symfony/Component/HttpKernel/Event/RequestFinishedEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/FinishRequestEvent.php @@ -16,6 +16,6 @@ * * @author Benjamin Eberlei */ -class RequestFinishedEvent extends KernelEvent +class FinishRequestEvent extends KernelEvent { } diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php index 7b6f2ad208cc..2dd16a479c7b 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -12,7 +12,7 @@ namespace Symfony\Component\HttpKernel\EventListener; use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\HttpKernel\Event\RequestFinishedEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\RequestContext; use Symfony\Component\HttpFoundation\Request; @@ -46,7 +46,7 @@ public function onKernelRequest(GetResponseEvent $event) $this->setRouterContext($request); } - public function onKernelRequestFinished(RequestFinishedEvent $event) + public function onKernelFinishRequest(FinishRequestEvent $event) { $this->resetRouterContext(); } @@ -85,7 +85,7 @@ public static function getSubscribedEvents() return array( // must be registered after the Router to have access to the _locale KernelEvents::REQUEST => array(array('onKernelRequest', 16)), - KernelEvents::REQUEST_FINISHED => array(array('onKernelRequestFinished', 0)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), ); } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index f2bad02968e6..a09ec648d1b2 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -13,7 +13,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; -use Symfony\Component\HttpKernel\Event\RequestFinishedEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -83,7 +83,7 @@ private function populateRoutingContext(Request $request = null) $this->request = $request; } - public function onKernelRequestFinished(RequestFinishedEvent $event) + public function onKernelFinishRequest(FinishRequestEvent $event) { $this->populateRoutingContext($this->kernelContext->getParentRequest()); } @@ -148,7 +148,7 @@ public static function getSubscribedEvents() { return array( KernelEvents::REQUEST => array(array('onKernelRequest', 32)), - KernelEvents::REQUEST_FINISHED => array(array('onKernelRequestFinished', 0)), + KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)), ); } } diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 71c1b91fc8ac..511d942b654b 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -16,7 +16,7 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; -use Symfony\Component\HttpKernel\Event\RequestFinishedEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; @@ -183,7 +183,7 @@ private function filterResponse(Response $response, Request $request, $type) */ private function finishRequest(Request $request, $type) { - $this->dispatcher->dispatch(KernelEvents::REQUEST_FINISHED, new RequestFinishedEvent($this, $request, $type)); + $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type)); $this->requestStack->pop(); } diff --git a/src/Symfony/Component/HttpKernel/KernelEvents.php b/src/Symfony/Component/HttpKernel/KernelEvents.php index e1ad0fa004fe..5e6ebcb8d992 100644 --- a/src/Symfony/Component/HttpKernel/KernelEvents.php +++ b/src/Symfony/Component/HttpKernel/KernelEvents.php @@ -111,5 +111,5 @@ final class KernelEvents * * @var string */ - const REQUEST_FINISHED = 'kernel.request_finished'; + const FINISH_REQUEST = 'kernel.finish_request'; } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php index 2cdfec93de45..eb82ebeb1cb6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php @@ -65,7 +65,7 @@ public function testLocaleSetForRoutingContext() $listener->onKernelRequest($this->getEvent($request)); } - public function testRouterResetWithParentRequestOnKernelRequestFinished() + public function testRouterResetWithParentRequestOnKernelFinishRequest() { if (!class_exists('Symfony\Component\Routing\Router')) { $this->markTestSkipped('The "Routing" component is not available'); @@ -83,10 +83,10 @@ public function testRouterResetWithParentRequestOnKernelRequestFinished() $this->context->expects($this->once())->method('getParentRequest')->will($this->returnValue($parentRequest)); - $event = $this->getMock('Symfony\Component\HttpKernel\Event\RequestFinishedEvent', array(), array(), '', false); + $event = $this->getMock('Symfony\Component\HttpKernel\Event\FinishRequestEvent', array(), array(), '', false); $listener = new LocaleListener('fr', $this->context, $router); - $listener->onKernelRequestFinished($event); + $listener->onKernelFinishRequest($event); } public function testRequestLocaleIsNotOverridden() From 018b71936f077f59da748021be9b48f876d04e53 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 31 Aug 2013 22:10:38 +0200 Subject: [PATCH 4/7] [HttpKernel] tweaked the code --- .../ContainerAwareHttpKernel.php | 4 +- .../EventListener/LocaleListener.php | 38 +++++++++++-------- .../EventListener/RouterListener.php | 21 +++++----- .../HttpKernel/Fragment/FragmentHandler.php | 7 +++- .../Component/HttpKernel/HttpKernel.php | 18 ++++----- .../Component/HttpKernel/RequestContext.php | 4 +- .../Component/HttpKernel/RequestStack.php | 14 ++++--- 7 files changed, 60 insertions(+), 46 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php index 214e3b255dae..c7fea3be7b43 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php @@ -37,9 +37,9 @@ class ContainerAwareHttpKernel extends HttpKernel * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance * @param ContainerInterface $container A ContainerInterface instance * @param ControllerResolverInterface $controllerResolver A ControllerResolverInterface instance - * @param RequestStack $requestStack A stack for master/sub requests + * @param RequestStack $requestStack A stack for master/sub requests */ - public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver, RequestStack $requestStack) + public function __construct(EventDispatcherInterface $dispatcher, ContainerInterface $container, ControllerResolverInterface $controllerResolver, RequestStack $requestStack = null) { parent::__construct($dispatcher, $controllerResolver, $requestStack); diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php index 2dd16a479c7b..4bfbf9cb5e97 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -37,6 +37,27 @@ public function __construct($defaultLocale = 'en', RequestContext $requestContex $this->router = $router; } + /** + * Sets the current Request. + * + * This method was used to synchronize the Request, but as the HttpKernel + * is doing that automatically now, you should never be called it directly. + * It is kept public for BC with the 2.3 version. + * + * @param Request|null $request A Request instance + * + * @deprecated Deprecated since version 2.4, to be removed in 3.0. + */ + public function setRequest(Request $request = null) + { + if (null === $request) { + return; + } + + $this->setLocale($request); + $this->setRouterContext($request); + } + public function onKernelRequest(GetResponseEvent $event) { $request = $event->getRequest(); @@ -48,22 +69,9 @@ public function onKernelRequest(GetResponseEvent $event) public function onKernelFinishRequest(FinishRequestEvent $event) { - $this->resetRouterContext(); - } - - private function resetRouterContext() - { - if ($this->requestContext === null) { - return; - } - - $parentRequest = $this->requestContext->getParentRequest(); - - if ($parentRequest === null) { - return; + if (null !== $parentRequest = $this->requestContext->getParentRequest()) { + $this->setRouterContext($parentRequest); } - - $this->setRouterContext($parentRequest); } private function setLocale(Request $request) diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index a09ec648d1b2..8c94f23a430d 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -68,14 +68,15 @@ public function __construct($matcher, KernelRequestContext $kernelContext, Reque /** * Sets the current Request. * - * The application should call this method whenever the Request - * object changes (entering a Request scope for instance, but - * also when leaving a Request scope -- especially when they are - * nested). + * This method was used to synchronize the Request, but as the HttpKernel + * is doing that automatically now, you should never be called it directly. + * It is kept public for BC with the 2.3 version. * * @param Request|null $request A Request instance + * + * @deprecated Deprecated since version 2.4, to be moved to a private function in 3.0. */ - private function populateRoutingContext(Request $request = null) + public function setRequest(Request $request = null) { if (null !== $request && $this->request !== $request) { $this->context->fromRequest($request); @@ -83,10 +84,10 @@ private function populateRoutingContext(Request $request = null) $this->request = $request; } - public function onKernelFinishRequest(FinishRequestEvent $event) - { - $this->populateRoutingContext($this->kernelContext->getParentRequest()); - } + public function onKernelFinishRequest(FinishRequestEvent $event) + { + $this->setRequest($this->kernelContext->getParentRequest()); + } public function onKernelRequest(GetResponseEvent $event) { @@ -95,7 +96,7 @@ public function onKernelRequest(GetResponseEvent $event) // initialize the context that is also used by the generator (assuming matcher and generator share the same context instance) // we call setRequest even if most of the time, it has already been done to keep compatibility // with frameworks which do not use the Symfony service container - $this->populateRoutingContext($request); + $this->setRequest($request); if ($request->attributes->has('_controller')) { // routing is already done diff --git a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php index 099f35a306bf..d54daea9235d 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php @@ -85,6 +85,10 @@ public function render($uri, $renderer = 'inline', array $options = array()) throw new \InvalidArgumentException(sprintf('The "%s" renderer does not exist.', $renderer)); } + if (null === $this->context->getCurrentRequest()) { + throw new \LogicException('Rendering a fragment can only be done when handling a Request.'); + } + return $this->deliver($this->renderers[$renderer]->render($uri, $this->context->getCurrentRequest(), $options)); } @@ -103,8 +107,7 @@ public function render($uri, $renderer = 'inline', array $options = array()) protected function deliver(Response $response) { if (!$response->isSuccessful()) { - $request = $this->context->getCurrentRequest(); - throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $request->getUri(), $response->getStatusCode())); + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->context->getCurrentRequest()->getUri(), $response->getStatusCode())); } if (!$response instanceof StreamedResponse) { diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 511d942b654b..7ac5316adf8f 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -64,9 +64,9 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ try { return $this->handleRaw($request, $type); } catch (\Exception $e) { - $this->finishRequest($request, $type); - if (false === $catch) { + $this->finishRequest($request, $type); + throw $e; } @@ -170,16 +170,14 @@ private function filterResponse(Response $response, Request $request, $type) } /** - * Publish event finished event, then pop the request from the stack. + * Publishes the finish request event, then pop the request from the stack. * - * Note: Order of the operations is important here, otherwise operations - * such as {@link RequestStack::getParentRequest()} can lead to weird - * results. + * Note that the order of the operations is important here, otherwise + * operations such as {@link RequestStack::getParentRequest()} can lead to + * weird results. * * @param Request $request - * @param int $type - * - * @return void + * @param int $type */ private function finishRequest(Request $request, $type) { @@ -207,6 +205,8 @@ private function handleException(\Exception $e, $request, $type) $e = $event->getException(); if (!$event->hasResponse()) { + $this->finishRequest($request, $type); + throw $e; } diff --git a/src/Symfony/Component/HttpKernel/RequestContext.php b/src/Symfony/Component/HttpKernel/RequestContext.php index f6fdaf3bbe83..de8aacc3e581 100644 --- a/src/Symfony/Component/HttpKernel/RequestContext.php +++ b/src/Symfony/Component/HttpKernel/RequestContext.php @@ -30,7 +30,7 @@ public function __construct(RequestStack $stack) } /** - * @return Request + * @return Request|null */ public function getCurrentRequest() { @@ -38,7 +38,7 @@ public function getCurrentRequest() } /** - * @return Request + * @return Request|null */ public function getMasterRequest() { diff --git a/src/Symfony/Component/HttpKernel/RequestStack.php b/src/Symfony/Component/HttpKernel/RequestStack.php index c8de54981d93..40e37718b7d1 100644 --- a/src/Symfony/Component/HttpKernel/RequestStack.php +++ b/src/Symfony/Component/HttpKernel/RequestStack.php @@ -16,8 +16,6 @@ /** * Request stack that controls the lifecycle of requests. * - * Notifies services of changes in the stack. - * * @author Benjamin Eberlei */ class RequestStack @@ -33,7 +31,7 @@ public function push(Request $request) } /** - * Pop the current request from the stack. + * Pops the current request from the stack. * * This operation lets the current request go out of scope. * @@ -41,6 +39,10 @@ public function push(Request $request) */ public function pop() { + if (!$this->requests) { + throw new \LogicException('Unable to pop a Request as the stack is already empty.'); + } + return array_pop($this->requests); } @@ -65,11 +67,11 @@ public function getMasterRequest() } /** - * Return the parent request of the current. + * Returns the parent request of the current. * - * If current Request is the master request, method returns null. + * If current Request is the master request, it returns null. * - * @return Request + * @return Request|null */ public function getParentRequest() { From 93e60eaeabe74f11371cbf7597ab4d7026f260f3 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Sep 2013 20:20:07 +0200 Subject: [PATCH 5/7] [HttpKernel] modified listeners to be BC with Symfony <2.4 --- .../Extension/HttpKernelExtensionTest.php | 4 +- .../Resources/config/fragment_renderer.xml | 2 +- .../Resources/config/routing.xml | 2 +- .../FrameworkBundle/Resources/config/web.xml | 2 +- .../EventListener/LocaleListener.php | 14 ++++++- .../EventListener/RouterListener.php | 18 ++++++++- .../HttpKernel/Fragment/FragmentHandler.php | 37 +++++++++++++++++-- .../EventListener/LocaleListenerTest.php | 10 ++--- .../EventListener/RouterListenerTest.php | 8 ++-- .../Tests/Fragment/FragmentHandlerTest.php | 4 +- 10 files changed, 78 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php index 9739d96ef385..445bb036da4a 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -45,7 +45,7 @@ public function testUnknownFragmentRenderer() ->disableOriginalConstructor() ->getMock() ; - $renderer = new FragmentHandler($context, array()); + $renderer = new FragmentHandler(array(), false, $context); $this->setExpectedException('InvalidArgumentException', 'The "inline" renderer does not exist.'); $renderer->render('/foo'); @@ -64,7 +64,7 @@ protected function getFragmentHandler($return) $context->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/'))); - $renderer = new FragmentHandler($context, array($strategy)); + $renderer = new FragmentHandler(array($strategy), false, $context); return $renderer; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml index e613ed11c1d0..27fff103fc0f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -15,9 +15,9 @@ - %kernel.debug% + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 34eb33214749..5db144e814fe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -92,9 +92,9 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index d2984f5eb460..3f6e455aab66 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -37,8 +37,8 @@ %kernel.default_locale% - + diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php index 4bfbf9cb5e97..2170376b9804 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -22,6 +22,11 @@ /** * Initializes the locale based on the current request. * + * This listener works in 2 modes: + * + * * 2.3 compatibility mode where you must call setRequest whenever the Request changes. + * * 2.4+ mode where you must pass a RequestContext instance in the constructor. + * * @author Fabien Potencier */ class LocaleListener implements EventSubscriberInterface @@ -30,7 +35,10 @@ class LocaleListener implements EventSubscriberInterface private $defaultLocale; private $requestContext; - public function __construct($defaultLocale = 'en', RequestContext $requestContext, RequestContextAwareInterface $router = null) + /** + * RequestContext will become required in 3.0. + */ + public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null, RequestContext $requestContext = null) { $this->defaultLocale = $defaultLocale; $this->requestContext = $requestContext; @@ -69,6 +77,10 @@ public function onKernelRequest(GetResponseEvent $event) public function onKernelFinishRequest(FinishRequestEvent $event) { + if (null === $this->requestContext) { + throw new \LogicException('You must pass a RequestContext.'); + } + if (null !== $parentRequest = $this->requestContext->getParentRequest()) { $this->setRouterContext($parentRequest); } diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index 8c94f23a430d..5ed590d4570b 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -30,6 +30,11 @@ /** * Initializes the context from the request and sets request attributes based on a matching route. * + * This listener works in 2 modes: + * + * * 2.3 compatibility mode where you must call setRequest whenever the Request changes. + * * 2.4+ mode where you must pass a RequestContext instance in the constructor. + * * @author Fabien Potencier */ class RouterListener implements EventSubscriberInterface @@ -43,13 +48,15 @@ class RouterListener implements EventSubscriberInterface /** * Constructor. * + * RequestContext will become required in 3.0. + * * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) * @param LoggerInterface|null $logger The logger * * @throws \InvalidArgumentException */ - public function __construct($matcher, KernelRequestContext $kernelContext, RequestContext $context = null, LoggerInterface $logger = null) + public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null, KernelRequestContext $kernelContext = null) { if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); @@ -86,6 +93,10 @@ public function setRequest(Request $request = null) public function onKernelFinishRequest(FinishRequestEvent $event) { + if (null === $this->kernelContext) { + throw new \LogicException('You must pass a RequestContext.'); + } + $this->setRequest($this->kernelContext->getParentRequest()); } @@ -96,7 +107,10 @@ public function onKernelRequest(GetResponseEvent $event) // initialize the context that is also used by the generator (assuming matcher and generator share the same context instance) // we call setRequest even if most of the time, it has already been done to keep compatibility // with frameworks which do not use the Symfony service container - $this->setRequest($request); + // when we have a RequestContext, no need to do it + if (null !== $this->kernelContext) { + $this->setRequest($request); + } if ($request->attributes->has('_controller')) { // routing is already done diff --git a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php index d54daea9235d..d7cc5d182791 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php @@ -23,6 +23,11 @@ * This class handles the rendering of resource fragments that are included into * a main resource. The handling of the rendering is managed by specialized renderers. * + * This listener works in 2 modes: + * + * * 2.3 compatibility mode where you must call setRequest whenever the Request changes. + * * 2.4+ mode where you must pass a RequestContext instance in the constructor. + * * @author Fabien Potencier * * @see FragmentRendererInterface @@ -31,15 +36,18 @@ class FragmentHandler { private $debug; private $renderers; + private $request; private $context; /** * Constructor. * + * RequestContext will become required in 3.0. + * * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances * @param Boolean $debug Whether the debug mode is enabled or not */ - public function __construct(RequestContext $context, array $renderers = array(), $debug = false) + public function __construct(array $renderers = array(), $debug = false, RequestContext $context = null) { $this->context = $context; $this->renderers = array(); @@ -59,6 +67,22 @@ public function addRenderer(FragmentRendererInterface $renderer) $this->renderers[$renderer->getName()] = $renderer; } + /** + * Sets the current Request. + * + * This method was used to synchronize the Request, but as the HttpKernel + * is doing that automatically now, you should never be called it directly. + * It is kept public for BC with the 2.3 version. + * + * @param Request|null $request A Request instance + * + * @deprecated Deprecated since version 2.4, to be removed in 3.0. + */ + public function setRequest(Request $request = null) + { + $this->request = $request; + } + /** * Renders a URI and returns the Response content. * @@ -85,11 +109,11 @@ public function render($uri, $renderer = 'inline', array $options = array()) throw new \InvalidArgumentException(sprintf('The "%s" renderer does not exist.', $renderer)); } - if (null === $this->context->getCurrentRequest()) { + if (!$request = $this->getRequest()) { throw new \LogicException('Rendering a fragment can only be done when handling a Request.'); } - return $this->deliver($this->renderers[$renderer]->render($uri, $this->context->getCurrentRequest(), $options)); + return $this->deliver($this->renderers[$renderer]->render($uri, $request, $options)); } /** @@ -107,7 +131,7 @@ public function render($uri, $renderer = 'inline', array $options = array()) protected function deliver(Response $response) { if (!$response->isSuccessful()) { - throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->context->getCurrentRequest()->getUri(), $response->getStatusCode())); + throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->getRequest()->getUri(), $response->getStatusCode())); } if (!$response instanceof StreamedResponse) { @@ -116,4 +140,9 @@ protected function deliver(Response $response) $response->sendContent(); } + + private function getRequest() + { + return $this->context ? $this->context->getCurrentRequest() : $this->request; + } } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php index eb82ebeb1cb6..270a11d29518 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php @@ -28,7 +28,7 @@ protected function setUp() public function testDefaultLocaleWithoutSession() { - $listener = new LocaleListener('fr', $this->context); + $listener = new LocaleListener('fr', null, $this->context); $event = $this->getEvent($request = Request::create('/')); $listener->onKernelRequest($event); @@ -42,7 +42,7 @@ public function testLocaleFromRequestAttribute() $request->cookies->set('foo', 'value'); $request->attributes->set('_locale', 'es'); - $listener = new LocaleListener('fr', $this->context); + $listener = new LocaleListener('fr', null, $this->context); $event = $this->getEvent($request); $listener->onKernelRequest($event); @@ -61,7 +61,7 @@ public function testLocaleSetForRoutingContext() $request = Request::create('/'); $request->attributes->set('_locale', 'es'); - $listener = new LocaleListener('fr', $this->context, $router); + $listener = new LocaleListener('fr', $router, $this->context); $listener->onKernelRequest($this->getEvent($request)); } @@ -85,7 +85,7 @@ public function testRouterResetWithParentRequestOnKernelFinishRequest() $event = $this->getMock('Symfony\Component\HttpKernel\Event\FinishRequestEvent', array(), array(), '', false); - $listener = new LocaleListener('fr', $this->context, $router); + $listener = new LocaleListener('fr', $router, $this->context); $listener->onKernelFinishRequest($event); } @@ -93,7 +93,7 @@ public function testRequestLocaleIsNotOverridden() { $request = Request::create('/'); $request->setLocale('de'); - $listener = new LocaleListener('fr', $this->context); + $listener = new LocaleListener('fr', null, $this->context); $event = $this->getEvent($request); $listener->onKernelRequest($event); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php index 2bd1810dca46..07be703f87ff 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -42,7 +42,7 @@ public function testPort($defaultHttpPort, $defaultHttpsPort, $uri, $expectedHtt ->method('getContext') ->will($this->returnValue($context)); - $listener = new RouterListener($urlMatcher, $this->kernelContext); + $listener = new RouterListener($urlMatcher, null, null, $this->kernelContext); $event = $this->createGetResponseEventForUri($uri); $listener->onKernelRequest($event); @@ -80,7 +80,7 @@ private function createGetResponseEventForUri($uri) */ public function testInvalidMatcher() { - new RouterListener(new \stdClass(), $this->kernelContext); + new RouterListener(new \stdClass(), null, null, $this->kernelContext); } public function testRequestMatcher() @@ -95,7 +95,7 @@ public function testRequestMatcher() ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) ->will($this->returnValue(array())); - $listener = new RouterListener($requestMatcher, $this->kernelContext, new RequestContext()); + $listener = new RouterListener($requestMatcher, new RequestContext(), null, $this->kernelContext); $listener->onKernelRequest($event); } @@ -116,7 +116,7 @@ public function testSubRequestWithDifferentMethod() ->method('getContext') ->will($this->returnValue($context)); - $listener = new RouterListener($requestMatcher, $this->kernelContext, new RequestContext()); + $listener = new RouterListener($requestMatcher, new RequestContext(), null, $this->kernelContext); $listener->onKernelRequest($event); // sub-request with another HTTP method diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php index 33e01d54b397..1ea7b50c3374 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php @@ -37,7 +37,7 @@ public function setUp() */ public function testRenderWhenRendererDoesNotExist() { - $handler = new FragmentHandler($this->context); + $handler = new FragmentHandler(array(), null, $this->context); $handler->render('/', 'foo'); } @@ -87,7 +87,7 @@ protected function getHandler($returnValue, $arguments = array()) call_user_func_array(array($e, 'with'), $arguments); } - $handler = new FragmentHandler($this->context); + $handler = new FragmentHandler(array(), null, $this->context); $handler->addRenderer($renderer); return $handler; From b1a062d232866164ae5112c702ceab361213ce44 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sat, 7 Sep 2013 22:38:41 +0200 Subject: [PATCH 6/7] moved RequestStack to HttpFoundation and removed RequestContext --- .../Extension/HttpKernelExtensionTest.php | 6 +- .../Resources/config/fragment_renderer.xml | 2 +- .../Resources/config/routing.xml | 2 +- .../Resources/config/services.xml | 10 +--- .../FrameworkBundle/Resources/config/web.xml | 2 +- .../Component/HttpFoundation/CHANGELOG.md | 1 + .../RequestStack.php | 23 +++++++- src/Symfony/Component/HttpKernel/CHANGELOG.md | 5 ++ .../ContainerAwareHttpKernel.php | 3 +- .../EventListener/LocaleListener.php | 18 +++--- .../EventListener/RouterListener.php | 22 ++++---- .../HttpKernel/Fragment/FragmentHandler.php | 14 ++--- .../Component/HttpKernel/HttpKernel.php | 1 + .../Component/HttpKernel/RequestContext.php | 55 ------------------- .../ContainerAwareHttpKernelTest.php | 4 +- .../EventListener/LocaleListenerTest.php | 20 +++---- .../EventListener/RouterListenerTest.php | 16 +++--- .../Tests/Fragment/FragmentHandlerTest.php | 10 ++-- .../HttpKernel/Tests/HttpKernelTest.php | 2 +- .../Component/HttpKernel/composer.json | 2 +- 20 files changed, 90 insertions(+), 128 deletions(-) rename src/Symfony/Component/{HttpKernel => HttpFoundation}/RequestStack.php (68%) delete mode 100644 src/Symfony/Component/HttpKernel/RequestContext.php diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php index 445bb036da4a..8ce564265ec9 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -15,8 +15,8 @@ use Symfony\Bridge\Twig\Tests\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; -use Symfony\Component\HttpKernel\RequestContext; class HttpKernelExtensionTest extends TestCase { @@ -41,7 +41,7 @@ public function testRenderFragment() public function testUnknownFragmentRenderer() { - $context = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\RequestContext') + $context = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') ->disableOriginalConstructor() ->getMock() ; @@ -57,7 +57,7 @@ protected function getFragmentHandler($return) $strategy->expects($this->once())->method('getName')->will($this->returnValue('inline')); $strategy->expects($this->once())->method('render')->will($return); - $context = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\RequestContext') + $context = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') ->disableOriginalConstructor() ->getMock() ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml index 27fff103fc0f..a1beee30a1fb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/fragment_renderer.xml @@ -17,7 +17,7 @@ %kernel.debug% - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml index 5db144e814fe..6b2f7c968869 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing.xml @@ -94,7 +94,7 @@ - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml index 3bcbe16d628c..608bf42d5253 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.xml @@ -12,8 +12,7 @@ Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer Symfony\Component\HttpKernel\Config\FileLocator Symfony\Component\HttpKernel\UriSigner - Symfony\Component\HttpKernel\RequestStack - Symfony\Component\HttpKernel\RequestContext + Symfony\Component\HttpFoundation\RequestStack @@ -28,12 +27,7 @@ - - - - - - + diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml index 3f6e455aab66..6c1dd73e2e61 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.xml @@ -38,7 +38,7 @@ %kernel.default_locale% - + diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 954b66ac43c0..061d47e7eec9 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 2.4.0 ----- + * added RequestStack * added Request::getEncodings() * added accessors methods to session handlers diff --git a/src/Symfony/Component/HttpKernel/RequestStack.php b/src/Symfony/Component/HttpFoundation/RequestStack.php similarity index 68% rename from src/Symfony/Component/HttpKernel/RequestStack.php rename to src/Symfony/Component/HttpFoundation/RequestStack.php index 40e37718b7d1..71bfd106991f 100644 --- a/src/Symfony/Component/HttpKernel/RequestStack.php +++ b/src/Symfony/Component/HttpFoundation/RequestStack.php @@ -9,9 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel; - -use Symfony\Component\HttpFoundation\Request; +namespace Symfony\Component\HttpFoundation; /** * Request stack that controls the lifecycle of requests. @@ -25,6 +23,12 @@ class RequestStack */ private $requests = array(); + /** + * Pushes a Request on the stack. + * + * This method should generally not be called directly as the stack + * management should be taken care of by the application itself. + */ public function push(Request $request) { $this->requests[] = $request; @@ -35,6 +39,9 @@ public function push(Request $request) * * This operation lets the current request go out of scope. * + * This method should generally not be called directly as the stack + * management should be taken care of by the application itself. + * * @return Request */ public function pop() @@ -55,6 +62,12 @@ public function getCurrentRequest() } /** + * Gets the master Request. + * + * Be warned that making your code aware of the master request + * might make it un-compatible with other features of your framework + * like ESI support. + * * @return Request|null */ public function getMasterRequest() @@ -69,6 +82,10 @@ public function getMasterRequest() /** * Returns the parent request of the current. * + * Be warned that making your code aware of the parent request + * might make it un-compatible with other features of your framework + * like ESI support. + * * If current Request is the master request, it returns null. * * @return Request|null diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index c06dd3fa57e0..b36e9358ff13 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +2.4.0 +----- + + * added the KernelEvents::FINISH_REQUEST event + 2.3.0 ----- diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php index c7fea3be7b43..69e7937d18c1 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php @@ -12,9 +12,9 @@ namespace Symfony\Component\HttpKernel\DependencyInjection; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\HttpKernel; -use Symfony\Component\HttpKernel\RequestStack; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -29,7 +29,6 @@ class ContainerAwareHttpKernel extends HttpKernel { protected $container; - protected $requestStack; /** * Constructor. diff --git a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php index 2170376b9804..7cab6aa39b56 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/LocaleListener.php @@ -14,7 +14,7 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\RequestContext; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RequestContextAwareInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -25,7 +25,7 @@ * This listener works in 2 modes: * * * 2.3 compatibility mode where you must call setRequest whenever the Request changes. - * * 2.4+ mode where you must pass a RequestContext instance in the constructor. + * * 2.4+ mode where you must pass a RequestStack instance in the constructor. * * @author Fabien Potencier */ @@ -33,15 +33,15 @@ class LocaleListener implements EventSubscriberInterface { private $router; private $defaultLocale; - private $requestContext; + private $requestStack; /** - * RequestContext will become required in 3.0. + * RequestStack will become required in 3.0. */ - public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null, RequestContext $requestContext = null) + public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null, RequestStack $requestStack = null) { $this->defaultLocale = $defaultLocale; - $this->requestContext = $requestContext; + $this->requestStack = $requestStack; $this->router = $router; } @@ -77,11 +77,11 @@ public function onKernelRequest(GetResponseEvent $event) public function onKernelFinishRequest(FinishRequestEvent $event) { - if (null === $this->requestContext) { - throw new \LogicException('You must pass a RequestContext.'); + if (null === $this->requestStack) { + throw new \LogicException('You must pass a RequestStack.'); } - if (null !== $parentRequest = $this->requestContext->getParentRequest()) { + if (null !== $parentRequest = $this->requestStack->getParentRequest()) { $this->setRouterContext($parentRequest); } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php index 5ed590d4570b..d122388b4b08 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -17,7 +17,7 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\RequestContext as KernelRequestContext; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; @@ -33,7 +33,7 @@ * This listener works in 2 modes: * * * 2.3 compatibility mode where you must call setRequest whenever the Request changes. - * * 2.4+ mode where you must pass a RequestContext instance in the constructor. + * * 2.4+ mode where you must pass a RequestStack instance in the constructor. * * @author Fabien Potencier */ @@ -43,12 +43,12 @@ class RouterListener implements EventSubscriberInterface private $context; private $logger; private $request; - private $kernelContext; + private $requestStack; /** * Constructor. * - * RequestContext will become required in 3.0. + * RequestStack will become required in 3.0. * * @param UrlMatcherInterface|RequestMatcherInterface $matcher The Url or Request matcher * @param RequestContext|null $context The RequestContext (can be null when $matcher implements RequestContextAwareInterface) @@ -56,7 +56,7 @@ class RouterListener implements EventSubscriberInterface * * @throws \InvalidArgumentException */ - public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null, KernelRequestContext $kernelContext = null) + public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = null, RequestStack $requestStack = null) { if (!$matcher instanceof UrlMatcherInterface && !$matcher instanceof RequestMatcherInterface) { throw new \InvalidArgumentException('Matcher must either implement UrlMatcherInterface or RequestMatcherInterface.'); @@ -68,7 +68,7 @@ public function __construct($matcher, RequestContext $context = null, LoggerInte $this->matcher = $matcher; $this->context = $context ?: $matcher->getContext(); - $this->kernelContext = $kernelContext; + $this->requestStack = $requestStack; $this->logger = $logger; } @@ -93,11 +93,11 @@ public function setRequest(Request $request = null) public function onKernelFinishRequest(FinishRequestEvent $event) { - if (null === $this->kernelContext) { - throw new \LogicException('You must pass a RequestContext.'); + if (null === $this->requestStack) { + throw new \LogicException('You must pass a RequestStack.'); } - $this->setRequest($this->kernelContext->getParentRequest()); + $this->setRequest($this->requestStack->getParentRequest()); } public function onKernelRequest(GetResponseEvent $event) @@ -107,8 +107,8 @@ public function onKernelRequest(GetResponseEvent $event) // initialize the context that is also used by the generator (assuming matcher and generator share the same context instance) // we call setRequest even if most of the time, it has already been done to keep compatibility // with frameworks which do not use the Symfony service container - // when we have a RequestContext, no need to do it - if (null !== $this->kernelContext) { + // when we have a RequestStack, no need to do it + if (null !== $this->requestStack) { $this->setRequest($request); } diff --git a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php index d7cc5d182791..05b6a086e970 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php +++ b/src/Symfony/Component/HttpKernel/Fragment/FragmentHandler.php @@ -14,8 +14,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\StreamedResponse; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Controller\ControllerReference; -use Symfony\Component\HttpKernel\RequestContext; /** * Renders a URI that represents a resource fragment. @@ -26,7 +26,7 @@ * This listener works in 2 modes: * * * 2.3 compatibility mode where you must call setRequest whenever the Request changes. - * * 2.4+ mode where you must pass a RequestContext instance in the constructor. + * * 2.4+ mode where you must pass a RequestStack instance in the constructor. * * @author Fabien Potencier * @@ -37,19 +37,19 @@ class FragmentHandler private $debug; private $renderers; private $request; - private $context; + private $requestStack; /** * Constructor. * - * RequestContext will become required in 3.0. + * RequestStack will become required in 3.0. * * @param FragmentRendererInterface[] $renderers An array of FragmentRendererInterface instances * @param Boolean $debug Whether the debug mode is enabled or not */ - public function __construct(array $renderers = array(), $debug = false, RequestContext $context = null) + public function __construct(array $renderers = array(), $debug = false, RequestStack $requestStack = null) { - $this->context = $context; + $this->requestStack = $requestStack; $this->renderers = array(); foreach ($renderers as $renderer) { $this->addRenderer($renderer); @@ -143,6 +143,6 @@ protected function deliver(Response $response) private function getRequest() { - return $this->context ? $this->context->getCurrentRequest() : $this->request; + return $this->requestStack ? $this->requestStack->getCurrentRequest() : $this->request; } } diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 7ac5316adf8f..0be8e1b4dbb7 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -22,6 +22,7 @@ use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Event\PostResponseEvent; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\EventDispatcher\EventDispatcherInterface; diff --git a/src/Symfony/Component/HttpKernel/RequestContext.php b/src/Symfony/Component/HttpKernel/RequestContext.php deleted file mode 100644 index de8aacc3e581..000000000000 --- a/src/Symfony/Component/HttpKernel/RequestContext.php +++ /dev/null @@ -1,55 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\HttpKernel; - -/** - * Registry for Requests. - * - * Facade for RequestStack that prevents modification of the stack, - * so that users don't accidentally push()/pop() from the stack and - * mess up the request cycle. - * - * @author Benjamin Eberlei - */ -class RequestContext -{ - private $stack; - - public function __construct(RequestStack $stack) - { - $this->stack = $stack; - } - - /** - * @return Request|null - */ - public function getCurrentRequest() - { - return $this->stack->getCurrentRequest(); - } - - /** - * @return Request|null - */ - public function getMasterRequest() - { - return $this->stack->getMasterRequest(); - } - - /** - * @return Request|null - */ - public function getParentRequest() - { - return $this->stack->getParentRequest(); - } -} diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php index 292a5e9f45d1..2c8a6a28ec14 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php @@ -13,7 +13,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel; -use Symfony\Component\HttpKernel\RequestStack; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -60,7 +60,7 @@ public function testVerifyRequestStackPushPopDuringHandle($type) return $expected; }; - $stack = $this->getMock('Symfony\Component\HttpKernel\RequestStack', array('push', 'pop')); + $stack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array('push', 'pop')); $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); $stack->expects($this->at(1))->method('pop'); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php index 270a11d29518..d128753ffab0 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/LocaleListenerTest.php @@ -11,24 +11,24 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; -use Symfony\Component\HttpKernel\EventListener\LocaleListener; -use Symfony\Component\HttpKernel\RequestContext; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\LocaleListener; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; class LocaleListenerTest extends \PHPUnit_Framework_TestCase { - private $context; + private $requestStack; protected function setUp() { - $this->context = $this->getMock('Symfony\Component\HttpKernel\RequestContext', array(), array(), '', false); + $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array(), array(), '', false); } public function testDefaultLocaleWithoutSession() { - $listener = new LocaleListener('fr', null, $this->context); + $listener = new LocaleListener('fr', null, $this->requestStack); $event = $this->getEvent($request = Request::create('/')); $listener->onKernelRequest($event); @@ -42,7 +42,7 @@ public function testLocaleFromRequestAttribute() $request->cookies->set('foo', 'value'); $request->attributes->set('_locale', 'es'); - $listener = new LocaleListener('fr', null, $this->context); + $listener = new LocaleListener('fr', null, $this->requestStack); $event = $this->getEvent($request); $listener->onKernelRequest($event); @@ -61,7 +61,7 @@ public function testLocaleSetForRoutingContext() $request = Request::create('/'); $request->attributes->set('_locale', 'es'); - $listener = new LocaleListener('fr', $router, $this->context); + $listener = new LocaleListener('fr', $router, $this->requestStack); $listener->onKernelRequest($this->getEvent($request)); } @@ -81,11 +81,11 @@ public function testRouterResetWithParentRequestOnKernelFinishRequest() $parentRequest = Request::create('/'); $parentRequest->setLocale('es'); - $this->context->expects($this->once())->method('getParentRequest')->will($this->returnValue($parentRequest)); + $this->requestStack->expects($this->once())->method('getParentRequest')->will($this->returnValue($parentRequest)); $event = $this->getMock('Symfony\Component\HttpKernel\Event\FinishRequestEvent', array(), array(), '', false); - $listener = new LocaleListener('fr', $router, $this->context); + $listener = new LocaleListener('fr', $router, $this->requestStack); $listener->onKernelFinishRequest($event); } @@ -93,7 +93,7 @@ public function testRequestLocaleIsNotOverridden() { $request = Request::create('/'); $request->setLocale('de'); - $listener = new LocaleListener('fr', null, $this->context); + $listener = new LocaleListener('fr', null, $this->requestStack); $event = $this->getEvent($request); $listener->onKernelRequest($event); diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php index 07be703f87ff..ac742b35e08c 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/RouterListenerTest.php @@ -11,20 +11,20 @@ namespace Symfony\Component\HttpKernel\Tests\EventListener; -use Symfony\Component\HttpKernel\EventListener\RouterListener; -use Symfony\Component\HttpKernel\RequestContext as KernelRequestContext; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\EventListener\RouterListener; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Routing\RequestContext; class RouterListenerTest extends \PHPUnit_Framework_TestCase { - private $kernelContext; + private $requestStack; public function setUp() { - $this->kernelContext = $this->getMock('Symfony\Component\HttpKernel\RequestContext', array(), array(), '', false); + $this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array(), array(), '', false); } /** @@ -42,7 +42,7 @@ public function testPort($defaultHttpPort, $defaultHttpsPort, $uri, $expectedHtt ->method('getContext') ->will($this->returnValue($context)); - $listener = new RouterListener($urlMatcher, null, null, $this->kernelContext); + $listener = new RouterListener($urlMatcher, null, null, $this->requestStack); $event = $this->createGetResponseEventForUri($uri); $listener->onKernelRequest($event); @@ -80,7 +80,7 @@ private function createGetResponseEventForUri($uri) */ public function testInvalidMatcher() { - new RouterListener(new \stdClass(), null, null, $this->kernelContext); + new RouterListener(new \stdClass(), null, null, $this->requestStack); } public function testRequestMatcher() @@ -95,7 +95,7 @@ public function testRequestMatcher() ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request')) ->will($this->returnValue(array())); - $listener = new RouterListener($requestMatcher, new RequestContext(), null, $this->kernelContext); + $listener = new RouterListener($requestMatcher, new RequestContext(), null, $this->requestStack); $listener->onKernelRequest($event); } @@ -116,7 +116,7 @@ public function testSubRequestWithDifferentMethod() ->method('getContext') ->will($this->returnValue($context)); - $listener = new RouterListener($requestMatcher, new RequestContext(), null, $this->kernelContext); + $listener = new RouterListener($requestMatcher, new RequestContext(), null, $this->requestStack); $listener->onKernelRequest($event); // sub-request with another HTTP method diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php index 1ea7b50c3374..dbf6b20b424e 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php @@ -17,15 +17,15 @@ class FragmentHandlerTest extends \PHPUnit_Framework_TestCase { - private $context; + private $requestStack; public function setUp() { - $this->context = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\RequestContext') + $this->requestStack = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack') ->disableOriginalConstructor() ->getMock() ; - $this->context + $this->requestStack ->expects($this->any()) ->method('getCurrentRequest') ->will($this->returnValue(Request::create('/'))) @@ -37,7 +37,7 @@ public function setUp() */ public function testRenderWhenRendererDoesNotExist() { - $handler = new FragmentHandler(array(), null, $this->context); + $handler = new FragmentHandler(array(), null, $this->requestStack); $handler->render('/', 'foo'); } @@ -87,7 +87,7 @@ protected function getHandler($returnValue, $arguments = array()) call_user_func_array(array($e, 'with'), $arguments); } - $handler = new FragmentHandler(array(), null, $this->context); + $handler = new FragmentHandler(array(), null, $this->requestStack); $handler->addRenderer($renderer); return $handler; diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php index 120e945cd60b..2f69a9457547 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -242,7 +242,7 @@ public function testVerifyRequestStackPushPopDuringHandle() { $request = new Request(); - $stack = $this->getMock('Symfony\Component\HttpKernel\RequestStack', array('push', 'pop')); + $stack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array('push', 'pop')); $stack->expects($this->at(0))->method('push')->with($this->equalTo($request)); $stack->expects($this->at(1))->method('pop'); diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 0e8aac54c6af..a09b0013271c 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -18,7 +18,7 @@ "require": { "php": ">=5.3.3", "symfony/event-dispatcher": "~2.1", - "symfony/http-foundation": "~2.2", + "symfony/http-foundation": "~2.4", "symfony/debug": "~2.3", "psr/log": "~1.0" }, From 1b2ef74a9a0b2f0b866b0faf2543140ed5eac5af Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Sun, 8 Sep 2013 15:07:37 +0200 Subject: [PATCH 7/7] [Security] made sure that the exception listener is always removed from the event dispatcher at the end of the request --- .../Component/Security/Http/Firewall.php | 19 ++++++++++++++++++- .../Http/Firewall/ExceptionListener.php | 14 ++++++++++---- src/Symfony/Component/Security/composer.json | 4 ++-- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Security/Http/Firewall.php b/src/Symfony/Component/Security/Http/Firewall.php index 36df81a80e62..5a1e9d564749 100644 --- a/src/Symfony/Component/Security/Http/Firewall.php +++ b/src/Symfony/Component/Security/Http/Firewall.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -30,6 +31,7 @@ class Firewall implements EventSubscriberInterface { private $map; private $dispatcher; + private $exceptionListeners; /** * Constructor. @@ -41,6 +43,7 @@ public function __construct(FirewallMapInterface $map, EventDispatcherInterface { $this->map = $map; $this->dispatcher = $dispatcher; + $this->exceptionListeners = new \SplObjectStorage(); } /** @@ -57,6 +60,7 @@ public function onKernelRequest(GetResponseEvent $event) // register listeners for this firewall list($listeners, $exception) = $this->map->getListeners($event->getRequest()); if (null !== $exception) { + $this->exceptionListeners[$event->getRequest()] = $exception; $exception->register($this->dispatcher); } @@ -70,8 +74,21 @@ public function onKernelRequest(GetResponseEvent $event) } } + public function onKernelFinishRequest(FinishRequestEvent $event) + { + $request = $event->getRequest(); + + if (isset($this->exceptionListeners[$request])) { + $this->exceptionListeners[$request]->unregister($this->dispatcher); + unset($this->exceptionListeners[$request]); + } + } + public static function getSubscribedEvents() { - return array(KernelEvents::REQUEST => array('onKernelRequest', 8)); + return array( + KernelEvents::REQUEST => array('onKernelRequest', 8), + KernelEvents::FINISH_REQUEST => 'onKernelFinishRequest', + ); } } diff --git a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php index abbb4606a6e8..0cca0c46cc39 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ExceptionListener.php @@ -69,6 +69,16 @@ public function register(EventDispatcherInterface $dispatcher) $dispatcher->addListener(KernelEvents::EXCEPTION, array($this, 'onKernelException')); } + /** + * Unregisters the dispatcher. + * + * @param EventDispatcherInterface $dispatcher An EventDispatcherInterface instance + */ + public function unregister(EventDispatcherInterface $dispatcher) + { + $dispatcher->removeListener(KernelEvents::EXCEPTION, array($this, 'onKernelException')); + } + /** * Handles security related exceptions. * @@ -76,10 +86,6 @@ public function register(EventDispatcherInterface $dispatcher) */ public function onKernelException(GetResponseForExceptionEvent $event) { - // we need to remove ourselves as the exception listener can be - // different depending on the Request - $event->getDispatcher()->removeListener(KernelEvents::EXCEPTION, array($this, 'onKernelException')); - $exception = $event->getException(); $request = $event->getRequest(); diff --git a/src/Symfony/Component/Security/composer.json b/src/Symfony/Component/Security/composer.json index b6bbae451502..fe1299c170e1 100644 --- a/src/Symfony/Component/Security/composer.json +++ b/src/Symfony/Component/Security/composer.json @@ -18,8 +18,8 @@ "require": { "php": ">=5.3.3", "symfony/event-dispatcher": "~2.1", - "symfony/http-foundation": "~2.1", - "symfony/http-kernel": "~2.1" + "symfony/http-foundation": "~2.4", + "symfony/http-kernel": "~2.4" }, "require-dev": { "symfony/form": "~2.0",