diff --git a/src/Symfony/Components/HttpKernel/HttpKernel.php b/src/Symfony/Components/HttpKernel/HttpKernel.php index ebab5ddcb924..29df7627dc09 100644 --- a/src/Symfony/Components/HttpKernel/HttpKernel.php +++ b/src/Symfony/Components/HttpKernel/HttpKernel.php @@ -150,7 +150,7 @@ protected function handleRaw(Request $request, $type = self::MASTER_REQUEST) * Handles a request that need to be embedded. * * @param Request $request A Request instance - * @param Boolean $raw Whether to catch exceptions or not + * @param Boolean $raw Whether to catch exceptions or not * * @return string|false The Response content or false if there is a problem * @@ -182,7 +182,7 @@ protected function handleEmbedded(Request $request, $raw = false) * * @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 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 * diff --git a/src/Symfony/Framework/WebBundle/Controller/ControllerManager.php b/src/Symfony/Framework/WebBundle/Controller/ControllerManager.php new file mode 100644 index 000000000000..957953a65638 --- /dev/null +++ b/src/Symfony/Framework/WebBundle/Controller/ControllerManager.php @@ -0,0 +1,150 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +/** + * ControllerManager. + * + * @package Symfony + * @subpackage Framework_WebBundle + * @author Fabien Potencier + */ +class ControllerManager +{ + protected $container; + protected $logger; + + public function __construct(ContainerInterface $container, LoggerInterface $logger = null) + { + $this->container = $container; + $this->logger = $logger; + } + + /** + * Renders a Controller and returns the Response content. + * + * @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 + * + * @return string The Response content + */ + public function render($controller, array $options = array()) + { + $request = $this->container->getRequestService(); + + // controller or URI? + if (0 === strpos($controller, '/')) { + $subRequest = Request::create($controller, 'get', array(), $request->cookies->all(), array(), $request->server->all()); + } else { + $options['path']['_controller'] = $controller; + $options['path']['_format'] = $request->getRequestFormat(); + $subRequest = $request->duplicate($options['query'], null, $options['path']); + } + + try { + return $this->container->getKernelService()->handle($subRequest, HttpKernelInterface::EMBEDDED_REQUEST, true); + } catch (\Exception $e) { + if ($options['alt']) { + $alt = $options['alt']; + unset($options['alt']); + $options['path'] = isset($alt[1]) ? $alt[1] : array(); + $options['query'] = isset($alt[2]) ? $alt[2] : array(); + + return $this->render($alt[0], $options); + } + + if (!$options['ignore_errors']) { + throw $e; + } + } + } + + /** + * Creates the Controller instance associated with the controller string + * + * @param string $controller A controller name (a string like BlogBundle:Post:index) + * + * @return array An array composed of the Controller instance and the Controller method + * + * @throws \InvalidArgumentException|\LogicException If the controller can't be found + */ + public function findController($controller) + { + list($bundle, $controller, $action) = explode(':', $controller); + $class = null; + $logs = array(); + foreach (array_keys($this->container->getParameter('kernel.bundle_dirs')) as $namespace) { + $try = $namespace.'\\'.$bundle.'\\Controller\\'.$controller.'Controller'; + if (!class_exists($try)) { + if (null !== $this->logger) { + $logs[] = sprintf('Failed finding controller "%s:%s" from namespace "%s" (%s)', $bundle, $controller, $namespace, $try); + } + } else { + if (!in_array($namespace.'\\'.$bundle.'\\Bundle', array_map(function ($bundle) { return get_class($bundle); }, $this->container->getKernelService()->getBundles()))) { + throw new \LogicException(sprintf('To use the "%s" controller, you first need to enable the Bundle "%s" in your Kernel class.', $try, $namespace.'\\'.$bundle)); + } + + $class = $try; + + break; + } + } + + if (null === $class) { + if (null !== $this->logger) { + foreach ($logs as $log) { + $this->logger->info($log); + } + } + + throw new \InvalidArgumentException(sprintf('Unable to find controller "%s:%s".', $bundle, $controller)); + } + + $controller = new $class($this->container); + + $method = $action.'Action'; + if (!method_exists($controller, $method)) { + throw new \InvalidArgumentException(sprintf('Method "%s::%s" does not exist.', $class, $method)); + } + + if (null !== $this->logger) { + $this->logger->info(sprintf('Using controller "%s::%s"%s', $class, $method, isset($file) ? sprintf(' from file "%s"', $file) : '')); + } + + return array($controller, $method); + } + + /** + * @throws \RuntimeException When value for argument given is not provided + */ + public function getMethodArguments(array $path, $controller, $method) + { + $r = new \ReflectionObject($controller); + + $arguments = array(); + foreach ($r->getMethod($method)->getParameters() as $param) { + if (array_key_exists($param->getName(), $path)) { + $arguments[] = $path[$param->getName()]; + } elseif ($param->isDefaultValueAvailable()) { + $arguments[] = $param->getDefaultValue(); + } else { + throw new \RuntimeException(sprintf('Controller "%s::%s()" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $controller, $method, $param->getName())); + } + } + + return $arguments; + } +} diff --git a/src/Symfony/Framework/WebBundle/Helper/ActionsHelper.php b/src/Symfony/Framework/WebBundle/Helper/ActionsHelper.php index 799a271d95f5..95decdde6b59 100644 --- a/src/Symfony/Framework/WebBundle/Helper/ActionsHelper.php +++ b/src/Symfony/Framework/WebBundle/Helper/ActionsHelper.php @@ -80,39 +80,7 @@ public function render($controller, array $options = array()) $options['path'] = Escaper::unescape($options['path']); $options['query'] = Escaper::unescape($options['query']); - return $this->doRender($controller, $options); - } - - protected function doRender($controller, array $options = array()) - { - // controller or URI? - $request = $this->container->getRequestService(); - if (0 === strpos($controller, '/')) { - // URI - $subRequest = Request::create($controller, 'get', array(), $request->cookies->all(), array(), $request->server->all()); - } else { - // controller - $options['path']['_controller'] = $controller; - $options['path']['_format'] = $request->getRequestFormat(); - $subRequest = $request->duplicate($options['query'], null, $options['path']); - } - - try { - return $this->container->getKernelService()->handle($subRequest, HttpKernelInterface::EMBEDDED_REQUEST, true); - } catch (\Exception $e) { - if ($options['alt']) { - $alt = $options['alt']; - unset($options['alt']); - $options['path'] = isset($alt[1]) ? $alt[1] : array(); - $options['query'] = isset($alt[2]) ? $alt[2] : array(); - - return $this->doRender($alt[0], $options); - } - - if (!$options['ignore_errors']) { - throw $e; - } - } + return $this->container->getControllerManagerService()->render($controller, $options); } /** diff --git a/src/Symfony/Framework/WebBundle/Listener/ControllerLoader.php b/src/Symfony/Framework/WebBundle/Listener/ControllerLoader.php index 68ad5bf4e4c0..54a126207c82 100644 --- a/src/Symfony/Framework/WebBundle/Listener/ControllerLoader.php +++ b/src/Symfony/Framework/WebBundle/Listener/ControllerLoader.php @@ -2,8 +2,9 @@ namespace Symfony\Framework\WebBundle\Listener; +use Symfony\Framework\WebBundle\Controller\ControllerManager; use Symfony\Foundation\LoggerInterface; -use Symfony\Components\DependencyInjection\ContainerInterface; +use Symfony\Components\EventDispatcher\EventDispatcher; use Symfony\Components\EventDispatcher\Event; /* @@ -25,108 +26,48 @@ */ class ControllerLoader { - protected $container; + protected $manager; + protected $dispatcher; protected $logger; - public function __construct(ContainerInterface $container, LoggerInterface $logger = null) + public function __construct(EventDispatcher $dispatcher, ControllerManager $manager, LoggerInterface $logger = null) { - $this->container = $container; + $this->dispatcher = $dispatcher; + $this->manager = $manager; $this->logger = $logger; } public function register() { - $this->container->getEventDispatcherService()->connect('core.load_controller', array($this, 'resolve')); + $this->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 (!$controller = $request->path->get('_controller')) { if (null !== $this->logger) { - $this->logger->err('Unable to look for the controller as the _controller parameter is missing'); + $this->logger->err('Unable to look for the controller as the "_controller" parameter is missing'); } return false; } - $controller = $this->findController($controller); - $controller[0]->setRequest($request); + list($controller, $method) = $this->manager->findController($controller); + $controller->setRequest($request); - $r = new \ReflectionObject($controller[0]); - $arguments = $this->getMethodArguments($r->getMethod($controller[1]), $request->path->all(), sprintf('%s::%s()', get_class($controller[0]), $controller[1])); + $arguments = $this->manager->getMethodArguments($request->path->all(), $controller, $method); - $event->setReturnValue(array($controller, $arguments)); + $event->setReturnValue(array(array($controller, $method), $arguments)); return true; } - - /** - * @throws \InvalidArgumentException|\LogicException If controller can't be found - */ - public function findController($controller) - { - list($bundle, $controller, $action) = explode(':', $controller); - $class = null; - $logs = array(); - foreach (array_keys($this->container->getParameter('kernel.bundle_dirs')) as $namespace) { - $try = $namespace.'\\'.$bundle.'\\Controller\\'.$controller.'Controller'; - if (!class_exists($try)) { - if (null !== $this->logger) { - $logs[] = sprintf('Failed finding controller "%s:%s" from namespace "%s" (%s)', $bundle, $controller, $namespace, $try); - } - } else { - if (!in_array($namespace.'\\'.$bundle.'\\Bundle', array_map(function ($bundle) { return get_class($bundle); }, $this->container->getKernelService()->getBundles()))) { - throw new \LogicException(sprintf('To use the "%s" controller, you first need to enable the Bundle "%s" in your Kernel class.', $try, $namespace.'\\'.$bundle)); - } - - $class = $try; - - break; - } - } - - if (null === $class) { - if (null !== $this->logger) { - foreach ($logs as $log) { - $this->logger->info($log); - } - } - - throw new \InvalidArgumentException(sprintf('Unable to find controller "%s:%s".', $bundle, $controller)); - } - - $controller = new $class($this->container); - - $method = $action.'Action'; - if (!method_exists($controller, $method)) { - throw new \InvalidArgumentException(sprintf('Method "%s::%s" does not exist.', $class, $method)); - } - - if (null !== $this->logger) { - $this->logger->info(sprintf('Using controller "%s::%s"%s', $class, $method, isset($file) ? sprintf(' from file "%s"', $file) : '')); - } - - return array($controller, $method); - } - - /** - * @throws \RuntimeException When value for argument given is not provided - */ - public function getMethodArguments(\ReflectionFunctionAbstract $r, array $parameters, $controller) - { - $arguments = array(); - foreach ($r->getParameters() as $param) { - if (array_key_exists($param->getName(), $parameters)) { - $arguments[] = $parameters[$param->getName()]; - } elseif ($param->isDefaultValueAvailable()) { - $arguments[] = $param->getDefaultValue(); - } else { - throw new \RuntimeException(sprintf('Controller "%s" requires that you provide a value for the "$%s" argument (because there is no default value or because there is a non optional argument after this one).', $controller, $param->getName())); - } - } - - return $arguments; - } } diff --git a/src/Symfony/Framework/WebBundle/Resources/config/web.xml b/src/Symfony/Framework/WebBundle/Resources/config/web.xml index 07da371e9e60..7a08e2a201a9 100644 --- a/src/Symfony/Framework/WebBundle/Resources/config/web.xml +++ b/src/Symfony/Framework/WebBundle/Resources/config/web.xml @@ -6,6 +6,7 @@ Symfony\Framework\WebBundle\Listener\RequestParser + Symfony\Framework\WebBundle\Controller\ControllerManager Symfony\Framework\WebBundle\Listener\ControllerLoader Symfony\Components\Routing\Router Symfony\Framework\WebBundle\Listener\ResponseFilter @@ -17,9 +18,15 @@ + + + + + - + +