merged branch fabpot/request-stack (PR #8904)
This PR was merged into the master branch. Discussion ---------- Synchronized Service alternative, backwards compatible This is a rebased version #7707. | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #7707 | License | MIT | Doc PR | symfony/symfony-docs#2956 Todo/Questions - [x] do we deprecate the synchronize feature (and removed it in 3.0)? - [x] deal with BC for listeners - [x] rename RequestContext as we already have a class with the same name in the Routing component? - [x] move RequestStack and RequestContext to HttpFoundation? - [x] update documentation Prototype for introducing a ``RequestContext`` in HttpKernel. This PR keeps the synchronized services feature, however introduces a ``RequestContext`` object additionally, that allows to avoid using synchronized service when injecting ``request_context`` instead of ``request`` into a service. The FrameworkBundle is modified such that it does not depend on synchronized services anymore. Users however can still depend on ``request``, activating the synchronized services feature. Features: * Introduced ``REQUEST_FINSHED`` (name is up for grabs) event to handle global state and environment cleanup that should not be done in ``RESPONSE``. Called in both exception or success case correctly * Changed listeners that were synchronized before to using ``onKernelRequestFinished`` and ``RequestContext`` to reset to the parent request (RouterListener + LocaleListener). * Changed ``FragmentHandler`` to use the RequestContext. Added some more tests for this class. * ``RequestStack`` is injected into the ``HttpKernel`` to handle the request finished event and push/pop the stack with requests. Todos: * RequestContext name clashes with Routing components RequestContext. Keep or make unique? * Name for Kernel Request Finished Event could be improved. Commits -------1b2ef74
[Security] made sure that the exception listener is always removed from the event dispatcher at the end of the requestb1a062d
moved RequestStack to HttpFoundation and removed RequestContext93e60ea
[HttpKernel] modified listeners to be BC with Symfony <2.4018b719
[HttpKernel] tweaked the codef9b10ba
[HttpKernel] renamed the kernel finished eventa58a8a6
[HttpKernel] changed request_stack to a private servicec55f1ea
added a RequestStack class
This commit is contained in:
commit
599c86509e
|
@ -14,6 +14,8 @@ 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\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpKernel\Fragment\FragmentHandler;
|
||||
|
||||
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\\HttpFoundation\\RequestStack')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
$renderer = new FragmentHandler(array(), false, $context);
|
||||
|
||||
$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\\HttpFoundation\\RequestStack')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$context->expects($this->any())->method('getCurrentRequest')->will($this->returnValue(Request::create('/')));
|
||||
|
||||
$renderer = new FragmentHandler(array($strategy), false, $context);
|
||||
|
||||
return $renderer;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<service id="fragment.handler" class="%fragment.handler.class%">
|
||||
<argument type="collection" />
|
||||
<argument>%kernel.debug%</argument>
|
||||
<call method="setRequest"><argument type="service" id="request" on-invalid="null" strict="false" /></call>
|
||||
<argument type="service" id="request_stack" />
|
||||
</service>
|
||||
|
||||
<service id="fragment.renderer.inline" class="%fragment.renderer.inline.class%">
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
<argument type="service" id="router" />
|
||||
<argument type="service" id="router.request_context" on-invalid="ignore" />
|
||||
<argument type="service" id="logger" on-invalid="ignore" />
|
||||
<call method="setRequest"><argument type="service" id="request" on-invalid="null" strict="false" /></call>
|
||||
<argument type="service" id="request_stack" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<parameter key="cache_clearer.class">Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer</parameter>
|
||||
<parameter key="file_locator.class">Symfony\Component\HttpKernel\Config\FileLocator</parameter>
|
||||
<parameter key="uri_signer.class">Symfony\Component\HttpKernel\UriSigner</parameter>
|
||||
<parameter key="request_stack.class">Symfony\Component\HttpFoundation\RequestStack</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
|
@ -23,8 +24,11 @@
|
|||
<argument type="service" id="event_dispatcher" />
|
||||
<argument type="service" id="service_container" />
|
||||
<argument type="service" id="controller_resolver" />
|
||||
<argument type="service" id="request_stack" />
|
||||
</service>
|
||||
|
||||
<service id="request_stack" class="%request_stack.class%" />
|
||||
|
||||
<service id="cache_warmer" class="%cache_warmer.class%">
|
||||
<argument type="collection" />
|
||||
</service>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
<tag name="kernel.event_subscriber" />
|
||||
<argument>%kernel.default_locale%</argument>
|
||||
<argument type="service" id="router" on-invalid="ignore" />
|
||||
<call method="setRequest"><argument type="service" id="request" on-invalid="null" strict="false" /></call>
|
||||
<argument type="service" id="request_stack" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
@ -4,6 +4,7 @@ CHANGELOG
|
|||
2.4.0
|
||||
-----
|
||||
|
||||
* added RequestStack
|
||||
* added Request::getEncodings()
|
||||
* added accessors methods to session handlers
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation;
|
||||
|
||||
/**
|
||||
* Request stack that controls the lifecycle of requests.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class RequestStack
|
||||
{
|
||||
/**
|
||||
* @var Request[]
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the current request from the stack.
|
||||
*
|
||||
* 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()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
throw new \LogicException('Unable to pop a Request as the stack is already empty.');
|
||||
}
|
||||
|
||||
return array_pop($this->requests);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Request|null
|
||||
*/
|
||||
public function getCurrentRequest()
|
||||
{
|
||||
return end($this->requests) ?: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
if (!$this->requests) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->requests[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function getParentRequest()
|
||||
{
|
||||
$pos = count($this->requests) - 2;
|
||||
|
||||
if (!isset($this->requests[$pos])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->requests[$pos];
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
CHANGELOG
|
||||
=========
|
||||
|
||||
2.4.0
|
||||
-----
|
||||
|
||||
* added the KernelEvents::FINISH_REQUEST event
|
||||
|
||||
2.3.0
|
||||
-----
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
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\Controller\ControllerResolverInterface;
|
||||
|
@ -35,10 +36,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 = null)
|
||||
{
|
||||
parent::__construct($dispatcher, $controllerResolver);
|
||||
parent::__construct($dispatcher, $controllerResolver, $requestStack);
|
||||
|
||||
$this->container = $container;
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpKernel\Event;
|
||||
|
||||
/**
|
||||
* Triggered whenever a request is fully processed.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
class FinishRequestEvent extends KernelEvent
|
||||
{
|
||||
}
|
|
@ -12,7 +12,9 @@
|
|||
namespace Symfony\Component\HttpKernel\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\RequestContextAwareInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
@ -20,32 +22,48 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|||
/**
|
||||
* 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 RequestStack instance in the constructor.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LocaleListener implements EventSubscriberInterface
|
||||
{
|
||||
private $router;
|
||||
private $defaultLocale;
|
||||
private $requestStack;
|
||||
|
||||
public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null)
|
||||
/**
|
||||
* RequestStack will become required in 3.0.
|
||||
*/
|
||||
public function __construct($defaultLocale = 'en', RequestContextAwareInterface $router = null, RequestStack $requestStack = null)
|
||||
{
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
$this->requestStack = $requestStack;
|
||||
$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;
|
||||
}
|
||||
|
||||
if ($locale = $request->attributes->get('_locale')) {
|
||||
$request->setLocale($locale);
|
||||
}
|
||||
|
||||
if (null !== $this->router) {
|
||||
$this->router->getContext()->setParameter('_locale', $request->getLocale());
|
||||
}
|
||||
$this->setLocale($request);
|
||||
$this->setRouterContext($request);
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
|
@ -53,7 +71,33 @@ class LocaleListener implements EventSubscriberInterface
|
|||
$request = $event->getRequest();
|
||||
$request->setDefaultLocale($this->defaultLocale);
|
||||
|
||||
$this->setRequest($request);
|
||||
$this->setLocale($request);
|
||||
$this->setRouterContext($request);
|
||||
}
|
||||
|
||||
public function onKernelFinishRequest(FinishRequestEvent $event)
|
||||
{
|
||||
if (null === $this->requestStack) {
|
||||
throw new \LogicException('You must pass a RequestStack.');
|
||||
}
|
||||
|
||||
if (null !== $parentRequest = $this->requestStack->getParentRequest()) {
|
||||
$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 +105,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::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@ namespace Symfony\Component\HttpKernel\EventListener;
|
|||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\FinishRequestEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
|
||||
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||
|
@ -28,6 +30,11 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
/**
|
||||
* 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 RequestStack instance in the constructor.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RouterListener implements EventSubscriberInterface
|
||||
|
@ -36,17 +43,20 @@ class RouterListener implements EventSubscriberInterface
|
|||
private $context;
|
||||
private $logger;
|
||||
private $request;
|
||||
private $requestStack;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* 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)
|
||||
* @param LoggerInterface|null $logger The logger
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function __construct($matcher, RequestContext $context = null, LoggerInterface $logger = 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.');
|
||||
|
@ -58,18 +68,20 @@ class RouterListener implements EventSubscriberInterface
|
|||
|
||||
$this->matcher = $matcher;
|
||||
$this->context = $context ?: $matcher->getContext();
|
||||
$this->requestStack = $requestStack;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function setRequest(Request $request = null)
|
||||
{
|
||||
|
@ -79,6 +91,15 @@ class RouterListener implements EventSubscriberInterface
|
|||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function onKernelFinishRequest(FinishRequestEvent $event)
|
||||
{
|
||||
if (null === $this->requestStack) {
|
||||
throw new \LogicException('You must pass a RequestStack.');
|
||||
}
|
||||
|
||||
$this->setRequest($this->requestStack->getParentRequest());
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
@ -86,7 +107,10 @@ 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);
|
||||
// when we have a RequestStack, no need to do it
|
||||
if (null !== $this->requestStack) {
|
||||
$this->setRequest($request);
|
||||
}
|
||||
|
||||
if ($request->attributes->has('_controller')) {
|
||||
// routing is already done
|
||||
|
@ -139,6 +163,7 @@ class RouterListener implements EventSubscriberInterface
|
|||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => array(array('onKernelRequest', 32)),
|
||||
KernelEvents::FINISH_REQUEST => array(array('onKernelFinishRequest', 0)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Symfony\Component\HttpKernel\Fragment;
|
|||
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;
|
||||
|
||||
/**
|
||||
|
@ -22,6 +23,11 @@ use Symfony\Component\HttpKernel\Controller\ControllerReference;
|
|||
* 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 RequestStack instance in the constructor.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @see FragmentRendererInterface
|
||||
|
@ -31,15 +37,19 @@ class FragmentHandler
|
|||
private $debug;
|
||||
private $renderers;
|
||||
private $request;
|
||||
private $requestStack;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* 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)
|
||||
public function __construct(array $renderers = array(), $debug = false, RequestStack $requestStack = null)
|
||||
{
|
||||
$this->requestStack = $requestStack;
|
||||
$this->renderers = array();
|
||||
foreach ($renderers as $renderer) {
|
||||
$this->addRenderer($renderer);
|
||||
|
@ -60,7 +70,13 @@ class FragmentHandler
|
|||
/**
|
||||
* Sets the current Request.
|
||||
*
|
||||
* @param Request $request 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)
|
||||
{
|
||||
|
@ -93,11 +109,11 @@ 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.');
|
||||
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->request, $options));
|
||||
return $this->deliver($this->renderers[$renderer]->render($uri, $request, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -115,7 +131,7 @@ 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()));
|
||||
throw new \RuntimeException(sprintf('Error when rendering "%s" (Status code is %s).', $this->getRequest()->getUri(), $response->getStatusCode()));
|
||||
}
|
||||
|
||||
if (!$response instanceof StreamedResponse) {
|
||||
|
@ -124,4 +140,9 @@ class FragmentHandler
|
|||
|
||||
$response->sendContent();
|
||||
}
|
||||
|
||||
private function getRequest()
|
||||
{
|
||||
return $this->requestStack ? $this->requestStack->getCurrentRequest() : $this->request;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,13 @@ 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\FinishRequestEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
|
||||
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;
|
||||
|
||||
|
@ -35,19 +37,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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,6 +66,8 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
|||
return $this->handleRaw($request, $type);
|
||||
} catch (\Exception $e) {
|
||||
if (false === $catch) {
|
||||
$this->finishRequest($request, $type);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
@ -93,6 +100,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 +165,27 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
|||
|
||||
$this->dispatcher->dispatch(KernelEvents::RESPONSE, $event);
|
||||
|
||||
$this->finishRequest($request, $type);
|
||||
|
||||
return $event->getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes the finish request event, then pop the request from the stack.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
private function finishRequest(Request $request, $type)
|
||||
{
|
||||
$this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type));
|
||||
$this->requestStack->pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an exception by trying to convert it to a Response.
|
||||
*
|
||||
|
@ -179,6 +206,8 @@ class HttpKernel implements HttpKernelInterface, TerminableInterface
|
|||
$e = $event->getException();
|
||||
|
||||
if (!$event->hasResponse()) {
|
||||
$this->finishRequest($request, $type);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 FINISH_REQUEST = 'kernel.finish_request';
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Symfony\Component\HttpKernel\Tests\DependencyInjection;
|
|||
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel;
|
||||
use Symfony\Component\HttpFoundation\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\HttpFoundation\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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,16 +11,24 @@
|
|||
|
||||
namespace Symfony\Component\HttpKernel\Tests\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\EventListener\LocaleListener;
|
||||
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 $requestStack;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', array(), array(), '', false);
|
||||
}
|
||||
|
||||
public function testDefaultLocaleWithoutSession()
|
||||
{
|
||||
$listener = new LocaleListener('fr');
|
||||
$listener = new LocaleListener('fr', null, $this->requestStack);
|
||||
$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', null, $this->requestStack);
|
||||
$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', $router, $this->requestStack);
|
||||
$listener->onKernelRequest($this->getEvent($request));
|
||||
}
|
||||
|
||||
public function testRouterResetWithParentRequestOnKernelFinishRequest()
|
||||
{
|
||||
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->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->requestStack);
|
||||
$listener->onKernelFinishRequest($event);
|
||||
}
|
||||
|
||||
public function testRequestLocaleIsNotOverridden()
|
||||
{
|
||||
$request = Request::create('/');
|
||||
$request->setLocale('de');
|
||||
$listener = new LocaleListener('fr');
|
||||
$listener = new LocaleListener('fr', null, $this->requestStack);
|
||||
$event = $this->getEvent($request);
|
||||
|
||||
$listener->onKernelRequest($event);
|
||||
|
|
|
@ -11,14 +11,22 @@
|
|||
|
||||
namespace Symfony\Component\HttpKernel\Tests\EventListener;
|
||||
|
||||
use Symfony\Component\HttpKernel\EventListener\RouterListener;
|
||||
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 $requestStack;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->requestStack = $this->getMock('Symfony\Component\HttpFoundation\RequestStack', 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, null, null, $this->requestStack);
|
||||
$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(), null, null, $this->requestStack);
|
||||
}
|
||||
|
||||
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, new RequestContext(), null, $this->requestStack);
|
||||
$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, new RequestContext(), null, $this->requestStack);
|
||||
$listener->onKernelRequest($event);
|
||||
|
||||
// sub-request with another HTTP method
|
||||
|
|
|
@ -17,12 +17,27 @@ use Symfony\Component\HttpFoundation\Response;
|
|||
|
||||
class FragmentHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
private $requestStack;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->requestStack = $this->getMockBuilder('Symfony\\Component\\HttpFoundation\\RequestStack')
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
$this->requestStack
|
||||
->expects($this->any())
|
||||
->method('getCurrentRequest')
|
||||
->will($this->returnValue(Request::create('/')))
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testRenderWhenRendererDoesNotExist()
|
||||
{
|
||||
$handler = new FragmentHandler();
|
||||
$handler = new FragmentHandler(array(), null, $this->requestStack);
|
||||
$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(array(), null, $this->requestStack);
|
||||
$handler->addRenderer($renderer);
|
||||
$handler->setRequest(Request::create('/'));
|
||||
|
||||
return $handler;
|
||||
}
|
||||
|
|
|
@ -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\HttpFoundation\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) {
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http;
|
|||
|
||||
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 @@ class Firewall implements EventSubscriberInterface
|
|||
{
|
||||
$this->map = $map;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->exceptionListeners = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,6 +60,7 @@ class Firewall implements EventSubscriberInterface
|
|||
// 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 @@ class Firewall implements EventSubscriberInterface
|
|||
}
|
||||
}
|
||||
|
||||
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',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,16 @@ class ExceptionListener
|
|||
$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 @@ class ExceptionListener
|
|||
*/
|
||||
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();
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
Reference in New Issue