From c55f1ea8af80cf5c96b27d57b7bf97843ec73aef Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Thu, 18 Apr 2013 10:33:03 +0200 Subject: [PATCH] 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 | 59 ++++-- .../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 | 180 +++++++++--------- .../EventListener/LocaleListenerTest.php | 40 +++- .../EventListener/RouterListenerTest.php | 16 +- .../Tests/Fragment/FragmentHandlerTest.php | 20 +- .../HttpKernel/Tests/HttpKernelTest.php | 14 ++ 19 files changed, 487 insertions(+), 149 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 ce22481921..9739d96ef3 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -14,7 +14,9 @@ namespace Symfony\Bridge\Twig\Tests\Extension; 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); + } - $this->renderTemplate($kernel); + public function testRenderFragment() + { + $renderer = $this->getFragmentHandler($this->returnValue(new Response('html'))); + + $response = $this->renderTemplate($renderer); + + $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 @@ class HttpKernelExtensionTest extends TestCase $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 4773339906..e613ed11c1 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 9e21db4519..34eb332147 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 674e28f1c9..607634d1a7 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 177821a5af..d2984f5eb4 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 c9b8a211d4..214e3b255d 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/ContainerAwareHttpKernel.php @@ -14,6 +14,7 @@ namespace Symfony\Component\HttpKernel\DependencyInjection; 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 @@ use Symfony\Component\DependencyInjection\Scope; 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 0000000000..c9d3c39a72 --- /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 0b864c02f2..7b6f2ad208 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,34 +28,56 @@ 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) - { - if (null === $request) { - return; - } - - if ($locale = $request->attributes->get('_locale')) { - $request->setLocale($locale); - } - - 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); + $this->setLocale($request); + $this->setRouterContext($request); + } + + public function onKernelRequestFinished(RequestFinishedEvent $event) + { + $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 static function getSubscribedEvents() @@ -61,6 +85,7 @@ class LocaleListener implements EventSubscriberInterface 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 777fd11bf4..f2bad02968 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/RouterListener.php @@ -13,9 +13,11 @@ namespace Symfony\Component\HttpKernel\EventListener; 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 @@ class RouterListener implements EventSubscriberInterface $this->matcher = $matcher; $this->context = $context ?: $matcher->getContext(); + $this->kernelContext = $kernelContext; $this->logger = $logger; } @@ -71,7 +75,7 @@ class RouterListener implements EventSubscriberInterface * * @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 @@ class RouterListener implements EventSubscriberInterface $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 @@ class RouterListener implements EventSubscriberInterface // 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 @@ class RouterListener implements EventSubscriberInterface { 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 af9b9ba98b..099f35a306 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\Request; 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 @@ class FragmentHandler $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 @@ class FragmentHandler 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 @@ class FragmentHandler 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 837a16ff37..71c1b91fc8 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; 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 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface try { return $this->handleRaw($request, $type); } catch (\Exception $e) { + $this->finishRequest($request, $type); + if (false === $catch) { throw $e; } @@ -93,6 +99,8 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface */ 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 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface $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 fce48ac3a6..e1ad0fa004 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 0000000000..f6fdaf3bbe --- /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 0000000000..c8de54981d --- /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 28901dafdd..292a5e9f45 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/ContainerAwareHttpKernelTest.php @@ -13,6 +13,7 @@ namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; 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,61 +27,51 @@ class ContainerAwareHttpKernelTest extends \PHPUnit_Framework_TestCase { $request = new Request(); $expected = new Response(); - - $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')) - ; - - $dispatcher = new EventDispatcher(); - $resolver = $this->getMock('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface'); - $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver); - - $controller = function () use ($expected) { + $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())); + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $this + ->expectsEnterScopeOnce($container) + ->expectsLeaveScopeOnce($container) + ->expectsSetRequestWithAt($container, $request, 3) + ->expectsSetRequestWithAt($container, null, 4) + ; + + $dispatcher = new EventDispatcher(); + $resolver = $this->getResolverMockFor($controller, $request); + $stack = new RequestStack(); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver, $stack); $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; + }; + + $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'); + + $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); + $dispatcher = new EventDispatcher(); + $resolver = $this->getResolverMockFor($controller, $request); + $kernel = new ContainerAwareHttpKernel($dispatcher, $container, $resolver, $stack); + + $kernel->handle($request, $type); + } + /** * @dataProvider getProviderTypes */ @@ -88,51 +79,23 @@ class ContainerAwareHttpKernelTest extends \PHPUnit_Framework_TestCase { $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 @@ class ContainerAwareHttpKernelTest extends \PHPUnit_Framework_TestCase 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 36859baa16..2cdfec93de 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 @@ class LocaleListenerTest extends \PHPUnit_Framework_TestCase $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 @@ class LocaleListenerTest extends \PHPUnit_Framework_TestCase $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 66fe6bd55d..2bd1810dca 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 @@ use Symfony\Component\Routing\RequestContext; 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 @@ class RouterListenerTest extends \PHPUnit_Framework_TestCase ->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 @@ class RouterListenerTest extends \PHPUnit_Framework_TestCase */ public function testInvalidMatcher() { - new RouterListener(new \stdClass()); + new RouterListener(new \stdClass(), $this->kernelContext); } public function testRequestMatcher() @@ -87,7 +95,7 @@ class RouterListenerTest extends \PHPUnit_Framework_TestCase ->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 @@ class RouterListenerTest extends \PHPUnit_Framework_TestCase ->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 cec8ae9840..33e01d54b3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/FragmentHandlerTest.php @@ -17,12 +17,27 @@ use Symfony\Component\HttpFoundation\Response; 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 @@ class FragmentHandlerTest extends \PHPUnit_Framework_TestCase 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 1bfa0e5fa8..120e945cd6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -238,6 +238,20 @@ class HttpKernelTest extends \PHPUnit_Framework_TestCase $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) {