changed HttpKernel workflow to allow more flexibility

This commit is contained in:
Fabien Potencier 2010-07-22 19:57:45 +02:00
parent 3ec9005680
commit e35d345204
11 changed files with 73 additions and 144 deletions

View File

@ -3,7 +3,7 @@
namespace Symfony\Bundle\FrameworkBundle\Controller;
use Symfony\Components\HttpKernel\LoggerInterface;
use Symfony\Components\HttpKernel\Controller\ControllerManagerInterface;
use Symfony\Components\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Components\HttpKernel\HttpKernelInterface;
use Symfony\Components\HttpFoundation\Request;
use Symfony\Components\EventDispatcher\Event;
@ -19,13 +19,13 @@ use Symfony\Components\DependencyInjection\ContainerInterface;
*/
/**
* ControllerManager.
* ControllerResolver.
*
* @package Symfony
* @subpackage Bundle_FrameworkBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ControllerManager implements ControllerManagerInterface
class ControllerResolver implements ControllerResolverInterface
{
protected $container;
protected $logger;
@ -122,7 +122,7 @@ class ControllerManager implements ControllerManagerInterface
* @param \Symfony\Components\HttpFoundation\Request $request A Request instance
*
* @return mixed|Boolean A PHP callable representing the Controller,
* or false if this manager is not able to determine the controller
* or false if this resolver is not able to determine the controller
*
* @throws \InvalidArgumentException|\LogicException If the controller can't be found
*/
@ -190,10 +190,9 @@ class ControllerManager implements ControllerManagerInterface
*
* @throws \RuntimeException When value for argument given is not provided
*/
public function getMethodArguments(Request $request, $controller)
public function getArguments(Request $request, $controller)
{
$event = $this->container->get('event_dispatcher')->filter(new Event($this, 'controller_manager.filter_controller_arguments', array('controller' => $controller, 'request' => $request)), $request->attributes->all());
$attributes = $event->getReturnValue();
$attributes = $request->attributes->all();
list($controller, $method) = $controller;

View File

@ -83,7 +83,7 @@
<service id="templating.helper.actions" class="%templating.helper.actions.class%">
<annotation name="templating.helper" alias="actions" />
<argument type="service" id="controller_manager" />
<argument type="service" id="controller_resolver" />
</service>
<service id="templating.loader" alias="templating.loader.filesystem" />

View File

@ -6,8 +6,7 @@
<parameters>
<parameter key="request_listener.class">Symfony\Bundle\FrameworkBundle\RequestListener</parameter>
<parameter key="controller_manager.class">Symfony\Bundle\FrameworkBundle\Controller\ControllerManager</parameter>
<parameter key="controller_loader_listener.class">Symfony\Components\HttpKernel\Controller\ControllerLoaderListener</parameter>
<parameter key="controller_resolver.class">Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver</parameter>
<parameter key="response_listener.class">Symfony\Components\HttpKernel\ResponseListener</parameter>
<parameter key="exception_listener.class">Symfony\Bundle\FrameworkBundle\Controller\ExceptionListener</parameter>
<parameter key="exception_listener.controller">FrameworkBundle:Exception:exception</parameter>
@ -16,17 +15,11 @@
</parameters>
<services>
<service id="controller_manager" class="%controller_manager.class%">
<service id="controller_resolver" class="%controller_resolver.class%">
<argument type="service" id="service_container" />
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="controller_loader_listener" class="%controller_loader_listener.class%">
<annotation name="kernel.listener" />
<argument type="service" id="controller_manager" />
<argument type="service" id="logger" on-invalid="ignore" />
</service>
<service id="request_listener" class="%request_listener.class%">
<annotation name="kernel.listener" />
<argument type="service" id="service_container" />

View File

@ -4,7 +4,7 @@ namespace Symfony\Bundle\FrameworkBundle\Templating\Helper;
use Symfony\Components\Templating\Helper\Helper;
use Symfony\Components\OutputEscaper\Escaper;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerManager;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver;
/*
* This file is part of the Symfony framework.
@ -24,16 +24,16 @@ use Symfony\Bundle\FrameworkBundle\Controller\ControllerManager;
*/
class ActionsHelper extends Helper
{
protected $manager;
protected $resolver;
/**
* Constructor.
*
* @param Constructor $container A ContainerInterface instance
*/
public function __construct(ControllerManager $manager)
public function __construct(ControllerResolver $resolver)
{
$this->manager = $manager;
$this->resolver = $resolver;
}
/**
@ -55,7 +55,7 @@ class ActionsHelper extends Helper
* @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI
* @param array $options An array of options
*
* @see Symfony\Bundle\FrameworkBundle\Controller\ControllerManager::render()
* @see Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver::render()
*/
public function render($controller, array $options = array())
{
@ -69,7 +69,7 @@ class ActionsHelper extends Helper
$options['query'] = Escaper::unescape($options['query']);
}
return $this->manager->render($controller, $options);
return $this->resolver->render($controller, $options);
}
/**

View File

@ -1,65 +0,0 @@
<?php
namespace Symfony\Components\HttpKernel\Controller;
use Symfony\Components\EventDispatcher\EventDispatcher;
use Symfony\Components\EventDispatcher\Event;
/*
* This file is part of the Symfony framework.
*
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* ControllerLoaderListener listen to the core.load_controller and finds the controller
* to execute based on the request parameters.
*
* @package Symfony
* @subpackage Bundle_FrameworkBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ControllerLoaderListener
{
protected $manager;
public function __construct(ControllerManagerInterface $manager)
{
$this->manager = $manager;
}
/**
* Registers a core.load_controller listener.
*
* @param Symfony\Components\EventDispatcher\EventDispatcher $dispatcher An EventDispatcher instance
*/
public function register(EventDispatcher $dispatcher)
{
$dispatcher->connect('core.load_controller', array($this, 'resolve'));
}
/**
* Creates the Controller associated with the given Request.
*
* @param Event $event An Event instance
*
* @return Boolean true if the controller has been found, false otherwise
*/
public function resolve(Event $event)
{
$request = $event->getParameter('request');
if (false === $controller = $this->manager->getController($request)) {
return false;
}
$arguments = $this->manager->getMethodArguments($request, $controller);
$event->setReturnValue(array($controller, $arguments));
return true;
}
}

View File

@ -14,7 +14,7 @@ use Symfony\Components\HttpFoundation\Request;
*/
/**
* A ControllerManagerInterface implementation knows how to determine the
* A ControllerResolverInterface implementation knows how to determine the
* controller to execute based on a Request object.
*
* It can also determine the arguments to pass to the Controller.
@ -25,21 +25,21 @@ use Symfony\Components\HttpFoundation\Request;
* @subpackage Bundle_FrameworkBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
interface ControllerManagerInterface
interface ControllerResolverInterface
{
/**
* Returns the Controller instance associated with a Request.
*
* As several managers can exist for a single application, a manager must
* As several resolvers can exist for a single application, a resolver must
* return false when it is not able to determine the controller.
*
* The manager must only throw an exception when it should be able to load
* The resolver must only throw an exception when it should be able to load
* controller but cannot because of some errors made by the developer.
*
* @param \Symfony\Components\HttpFoundation\Request $request A Request instance
*
* @return mixed|Boolean A PHP callable representing the Controller,
* or false if this manager is not able to determine the controller
* or false if this resolver is not able to determine the controller
*
* @throws \InvalidArgumentException|\LogicException If the controller can't be found
*/
@ -53,5 +53,5 @@ interface ControllerManagerInterface
*
* @throws \RuntimeException When value for argument given is not provided
*/
public function getMethodArguments(Request $request, $controller);
public function getArguments(Request $request, $controller);
}

View File

@ -4,6 +4,7 @@ namespace Symfony\Components\HttpKernel;
use Symfony\Components\EventDispatcher\Event;
use Symfony\Components\EventDispatcher\EventDispatcher;
use Symfony\Components\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Components\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Components\HttpFoundation\Request;
use Symfony\Components\HttpFoundation\Response;
@ -27,16 +28,19 @@ use Symfony\Components\HttpFoundation\Response;
class HttpKernel implements HttpKernelInterface
{
protected $dispatcher;
protected $resolver;
protected $request;
/**
* Constructor
*
* @param EventDispatcher $dispatcher An event dispatcher instance
* @param \Symfony\Components\EventDispatcher\EventDispatcher $dispatcher An event dispatcher instance
* @param \Symfony\Components\HttpKernel\Controller\ControllerResolverInterface $resolver A ControllerResolverInterface instance
*/
public function __construct(EventDispatcher $dispatcher)
public function __construct(EventDispatcher $dispatcher, ControllerResolverInterface $resolver)
{
$this->dispatcher = $dispatcher;
$this->resolver = $resolver;
}
/**
@ -55,11 +59,11 @@ class HttpKernel implements HttpKernelInterface
* All exceptions are caught, and a core.exception event is notified
* for user management.
*
* @param Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
* @param Boolean $raw Whether to catch exceptions or not
* @param \Symfony\Components\HttpFoundation\Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
* @param Boolean $raw Whether to catch exceptions or not
*
* @return Response A Response instance
* @return \Symfony\Components\HttpFoundation\Response A Response instance
*
* @throws \Exception When an Exception occurs during processing
* and couldn't be caught by event processing or $raw is true
@ -100,13 +104,13 @@ class HttpKernel implements HttpKernelInterface
*
* Exceptions are not caught.
*
* @param Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
* @param \Symfony\Components\HttpFoundation\Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
*
* @return Response A Response instance
* @return \Symfony\Components\HttpFoundation\Response A Response instance
*
* @throws \LogicException If one of the listener does not behave as expected
* @throws NotFoundHttpException When controller cannot be found
* @throws \LogicException If one of the listener does not behave as expected
* @throws \Symfony\Components\HttpKernel\Exception\NotFoundHttpException When controller cannot be found
*/
protected function handleRaw(Request $request, $type = self::MASTER_REQUEST)
{
@ -117,30 +121,23 @@ class HttpKernel implements HttpKernelInterface
}
// load controller
$event = $this->dispatcher->notifyUntil(new Event($this, 'core.load_controller', array('request_type' => $type, 'request' => $request)));
if (!$event->isProcessed()) {
if (false === $controller = $this->resolver->getController($request)) {
throw new NotFoundHttpException('Unable to find the controller.');
}
list($controller, $arguments) = $event->getReturnValue();
$event = $this->dispatcher->filter(new Event($this, 'core.controller', array('request' => $request)), $controller);
$controller = $event->getReturnValue();
// controller must be a callable
if (!is_callable($controller)) {
throw new \LogicException(sprintf('The controller must be a callable (%s).', var_export($controller, true)));
}
// controller
$event = $this->dispatcher->notifyUntil(new Event($this, 'core.controller', array('request_type' => $type, 'request' => $request, 'controller' => &$controller, 'arguments' => &$arguments)));
if ($event->isProcessed()) {
try {
return $this->filterResponse($event->getReturnValue(), $request, 'A "core.controller" listener returned a non response object.', $type);
} catch (\Exception $e) {
$retval = $event->getReturnValue();
}
} else {
// call controller
$retval = call_user_func_array($controller, $arguments);
}
// controller arguments
$arguments = $this->resolver->getArguments($request, $controller);
// call controller
$retval = call_user_func_array($controller, $arguments);
// view
$event = $this->dispatcher->filter(new Event($this, 'core.view', array('request_type' => $type, 'request' => $request)), $retval);
@ -151,7 +148,7 @@ class HttpKernel implements HttpKernelInterface
/**
* Handles a request that need to be embedded.
*
* @param Request $request A Request instance
* @param \Symfony\Components\HttpFoundation\Request $request A Request instance
* @param Boolean $raw Whether to catch exceptions or not
*
* @return string|false The Response content or false if there is a problem
@ -182,11 +179,11 @@ class HttpKernel implements HttpKernelInterface
/**
* Filters a response object.
*
* @param Response $response A Response instance
* @param string $message A error message in case the response is not a Response object
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
* @param \Symfony\Components\HttpFoundation\Response $response A Response instance
* @param string $message A error message in case the response is not a Response object
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
*
* @return Response The filtered Response instance
* @return \Symfony\Components\HttpFoundation\Response The filtered Response instance
*
* @throws \RuntimeException if the passed object is not a Response instance
*/

View File

@ -29,18 +29,18 @@ interface HttpKernelInterface
/**
* Handles a request to convert it to a response.
*
* @param Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
* @param Boolean $raw Whether to catch exceptions or not
* @param \Symfony\Components\HttpFoundation\Request $request A Request instance
* @param integer $type The type of the request (one of HttpKernelInterface::MASTER_REQUEST, HttpKernelInterface::FORWARDED_REQUEST, or HttpKernelInterface::EMBEDDED_REQUEST)
* @param Boolean $raw Whether to catch exceptions or not
*
* @return Response $response A Response instance
* @return \Symfony\Components\HttpFoundation\Response $response A Response instance
*/
public function handle(Request $request = null, $type = self::MASTER_REQUEST, $raw = false);
/**
* Gets the Request instance associated with the master request.
*
* @return Request A Request instance
* @return \Symfony\Components\HttpFoundation\Request A Request instance
*/
public function getRequest();
}

View File

@ -29,6 +29,7 @@
<service id="http_kernel" class="%http_kernel.class%">
<argument type="service" id="event_dispatcher" />
<argument type="service" id="controller_resolver" />
</service>
<service id="request" class="%request.class%">

View File

@ -15,9 +15,9 @@ use Symfony\Components\HttpKernel\HttpKernel;
use Symfony\Components\HttpFoundation\Request;
use Symfony\Components\HttpFoundation\Response;
use Symfony\Components\EventDispatcher\EventDispatcher;
use Symfony\Components\EventDispatcher\Event;
use Symfony\Components\HttpKernel\Controller\ControllerResolverInterface;
class TestHttpKernel extends HttpKernel
class TestHttpKernel extends HttpKernel implements ControllerResolverInterface
{
protected $body;
protected $status;
@ -33,15 +33,17 @@ class TestHttpKernel extends HttpKernel
$this->customizer = $customizer;
$this->called = false;
$this->dispatcher = new EventDispatcher();
$this->dispatcher->connect('core.load_controller', array($this, 'loadController'));
parent::__construct(new EventDispatcher(), $this);
}
public function loadController(Event $event)
public function getController(Request $request)
{
$event->setReturnValue(array(array($this, 'callController'), array($event['request'])));
return array($this, 'callController');
}
return true;
public function getArguments(Request $request, $controller)
{
return array($request);
}
public function callController(Request $request)

View File

@ -15,21 +15,23 @@ use Symfony\Components\HttpKernel\HttpKernel;
use Symfony\Components\HttpFoundation\Request;
use Symfony\Components\HttpFoundation\Response;
use Symfony\Components\EventDispatcher\EventDispatcher;
use Symfony\Components\EventDispatcher\Event;
use Symfony\Components\HttpKernel\Controller\ControllerResolverInterface;
class TestHttpKernel extends HttpKernel
class TestHttpKernel extends HttpKernel implements ControllerResolverInterface
{
public function __construct()
{
$this->dispatcher = new EventDispatcher();
$this->dispatcher->connect('core.load_controller', array($this, 'loadController'));
parent::__construct(new EventDispatcher(), $this);
}
public function loadController(Event $event)
public function getController(Request $request)
{
$event->setReturnValue(array(array($this, 'callController'), array($event['request'])));
return array($this, 'callController');
}
return true;
public function getArguments(Request $request, $controller)
{
return array($request);
}
public function callController(Request $request)