diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index dc42e4a139755..92d93eb384190 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG * added `Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface` * added `Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface` as argument to `HttpKernel` * added `Symfony\Component\HttpKernel\Controller\ArgumentResolver` + * added the `kernel.controller_arguments` event, triggered after controller arguments have been resolved 3.0.0 ----- @@ -22,8 +23,8 @@ CHANGELOG * removed `Symfony\Component\HttpKernel\EventListener\RouterListener::setRequest()` * removed `Symfony\Component\HttpKernel\EventListener\ProfilerListener::onKernelRequest()` * removed `Symfony\Component\HttpKernel\Fragment\FragmentHandler::setRequest()` - * removed `Symfony\Component\HttpKernel\HttpCache\Esi::hasSurrogateEsiCapability()` - * removed `Symfony\Component\HttpKernel\HttpCache\Esi::addSurrogateEsiCapability()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::hasSurrogateEsiCapability()` + * removed `Symfony\Component\HttpKernel\HttpCache\Esi::addSurrogateEsiCapability()` * removed `Symfony\Component\HttpKernel\HttpCache\Esi::needsEsiParsing()` * removed `Symfony\Component\HttpKernel\HttpCache\HttpCache::getEsi()` * removed `Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel` diff --git a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php index 9644b049b30bb..fbc49dffc8daa 100644 --- a/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/HttpKernel/Debug/TraceableEventDispatcher.php @@ -61,7 +61,7 @@ protected function preDispatch($eventName, Event $event) protected function postDispatch($eventName, Event $event) { switch ($eventName) { - case KernelEvents::CONTROLLER: + case KernelEvents::CONTROLLER_ARGUMENTS: $this->stopwatch->start('controller', 'section'); break; case KernelEvents::RESPONSE: diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.php new file mode 100644 index 0000000000000..1dc784ed52aca --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Event/FilterControllerArgumentsEvent.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\Event; + +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * Allows filtering of controller arguments. + * + * You can call getController() to retrieve the controller and getArguments + * to retrieve the current arguments. With setArguments() you can replace + * arguments that are used to call the controller. + * + * Arguments set in the event must be compatible with the signature of the + * controller. + * + * @author Christophe Coevoet + */ +class FilterControllerArgumentsEvent extends FilterControllerEvent +{ + private $arguments; + + public function __construct(HttpKernelInterface $kernel, callable $controller, array $arguments, Request $request, $requestType) + { + parent::__construct($kernel, $controller, $request, $requestType); + + $this->arguments = $arguments; + } + + /** + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * @param array $arguments + */ + public function setArguments(array $arguments) + { + $this->arguments = $arguments; + } +} diff --git a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php index e0af13106c2bf..e0d46aa4ce3e8 100644 --- a/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php +++ b/src/Symfony/Component/HttpKernel/Event/FilterControllerEvent.php @@ -53,8 +53,6 @@ public function getController() * Sets a new controller. * * @param callable $controller - * - * @throws \LogicException */ public function setController(callable $controller) { diff --git a/src/Symfony/Component/HttpKernel/HttpKernel.php b/src/Symfony/Component/HttpKernel/HttpKernel.php index 0294bb584aeba..49c044163c6f0 100644 --- a/src/Symfony/Component/HttpKernel/HttpKernel.php +++ b/src/Symfony/Component/HttpKernel/HttpKernel.php @@ -14,6 +14,7 @@ use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\Event\FilterControllerEvent; @@ -138,6 +139,11 @@ private function handleRaw(Request $request, $type = self::MASTER_REQUEST) // controller arguments $arguments = $this->argumentResolver->getArguments($request, $controller); + $event = new FilterControllerArgumentsEvent($this, $controller, $arguments, $request, $type); + $this->dispatcher->dispatch(KernelEvents::CONTROLLER_ARGUMENTS, $event); + $controller = $event->getController(); + $arguments = $event->getArguments(); + // call controller $response = call_user_func_array($controller, $arguments); diff --git a/src/Symfony/Component/HttpKernel/KernelEvents.php b/src/Symfony/Component/HttpKernel/KernelEvents.php index 8768979da6bd4..4f44bc6503d9f 100644 --- a/src/Symfony/Component/HttpKernel/KernelEvents.php +++ b/src/Symfony/Component/HttpKernel/KernelEvents.php @@ -76,6 +76,19 @@ final class KernelEvents */ const CONTROLLER = 'kernel.controller'; + /** + * The CONTROLLER_ARGUMENTS event occurs once controller arguments have been resolved. + * + * This event allows you to change the arguments that will be passed to + * the controller. The event listener method receives a + * Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent instance. + * + * @Event + * + * @var string + */ + const CONTROLLER_ARGUMENTS = 'kernel.controller_arguments'; + /** * The RESPONSE event occurs once a response was created for * replying to a request. diff --git a/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php index 5894ffbb8d744..d90e9dc11f1fa 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Debug/TraceableEventDispatcherTest.php @@ -34,6 +34,7 @@ public function testStopwatchSections() '__section__', 'kernel.request', 'kernel.controller', + 'kernel.controller_arguments', 'controller', 'kernel.response', 'kernel.terminate', diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php index 60c2a177793ae..437c1676bdfb2 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpKernelTest.php @@ -15,6 +15,7 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface; use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface; +use Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent; use Symfony\Component\HttpKernel\HttpKernel; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; @@ -233,6 +234,42 @@ public function testHandleWithAResponseListener() $this->assertEquals('foo', $kernel->handle(new Request())->getContent()); } + public function testHandleAllowChangingControllerArguments() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::CONTROLLER_ARGUMENTS, function (FilterControllerArgumentsEvent $event) { + $event->setArguments(array('foo')); + }); + + $kernel = $this->getHttpKernel($dispatcher, function ($content) { return new Response($content); }); + + $this->assertResponseEquals(new Response('foo'), $kernel->handle(new Request())); + } + + public function testHandleAllowChangingControllerAndArguments() + { + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(KernelEvents::CONTROLLER_ARGUMENTS, function (FilterControllerArgumentsEvent $event) { + $oldController = $event->getController(); + $oldArguments = $event->getArguments(); + + $newController = function ($id) use ($oldController, $oldArguments) { + $response = call_user_func_array($oldController, $oldArguments); + + $response->headers->set('X-Id', $id); + + return $response; + }; + + $event->setController($newController); + $event->setArguments(array('bar')); + }); + + $kernel = $this->getHttpKernel($dispatcher, function ($content) { return new Response($content); }, null, array('foo')); + + $this->assertResponseEquals(new Response('foo', 200, array('X-Id' => 'bar')), $kernel->handle(new Request())); + } + public function testTerminate() { $dispatcher = new EventDispatcher(); @@ -265,7 +302,7 @@ public function testVerifyRequestStackPushPopDuringHandle() $kernel->handle($request, HttpKernelInterface::MASTER_REQUEST); } - private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $controller = null, RequestStack $requestStack = null) + private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $controller = null, RequestStack $requestStack = null, array $arguments = array()) { if (null === $controller) { $controller = function () { return new Response('Hello'); }; @@ -281,7 +318,7 @@ private function getHttpKernel(EventDispatcherInterface $eventDispatcher, $contr $argumentResolver ->expects($this->any()) ->method('getArguments') - ->will($this->returnValue(array())); + ->will($this->returnValue($arguments)); return new HttpKernel($eventDispatcher, $controllerResolver, $requestStack, $argumentResolver); }