This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
symfony/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php

243 lines
9.4 KiB
PHP
Raw Normal View History

<?php
namespace Symfony\Bundle\FrameworkBundle\Controller;
2010-06-16 13:19:46 +01:00
use Symfony\Components\HttpKernel\LoggerInterface;
use Symfony\Components\HttpKernel\Controller\ControllerResolverInterface;
use Symfony\Components\HttpKernel\HttpKernelInterface;
use Symfony\Components\HttpFoundation\Request;
use Symfony\Components\EventDispatcher\Event;
use Symfony\Components\DependencyInjection\ContainerInterface;
/*
* 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.
*/
/**
* ControllerResolver.
*
* @package Symfony
2010-07-09 09:27:29 +01:00
* @subpackage Bundle_FrameworkBundle
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
*/
class ControllerResolver implements ControllerResolverInterface
{
protected $container;
protected $logger;
2010-06-23 20:42:41 +01:00
protected $esiSupport;
public function __construct(ContainerInterface $container, LoggerInterface $logger = null)
{
$this->container = $container;
$this->logger = $logger;
2010-06-27 17:28:29 +01:00
$this->esiSupport = $container->has('esi') && $container->getEsiService()->hasSurrogateEsiCapability($container->getRequestService());
}
/**
* Renders a Controller and returns the Response content.
*
2010-06-23 20:42:41 +01:00
* Note that this method generates an esi:include tag only when both the standalone
* option is set to true and the request has ESI capability (@see Symfony\Components\HttpKernel\Cache\ESI).
*
* Available options:
*
* * path: An array of path parameters (only when the first argument is a controller)
* * query: An array of query parameters (only when the first argument is a controller)
* * ignore_errors: true to return an empty string in case of an error
* * alt: an alternative controller to execute in case of an error (can be a controller, a URI, or an array with the controller, the path arguments, and the query arguments)
2010-06-23 20:42:41 +01:00
* * standalone: whether to generate an esi:include tag or not when ESI is supported
* * comment: a comment to add when returning an esi:include tag
*
* @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())
{
$options = array_merge(array(
'path' => array(),
'query' => array(),
'ignore_errors' => true,
'alt' => array(),
2010-06-23 20:42:41 +01:00
'standalone' => false,
'comment' => '',
), $options);
if (!is_array($options['alt'])) {
$options['alt'] = array($options['alt']);
}
2010-06-23 20:42:41 +01:00
if ($this->esiSupport && $options['standalone']) {
$uri = $this->generateInternalUri($controller, $options['path'], $options['query']);
$alt = '';
if ($options['alt']) {
$alt = $this->generateInternalUri($options['alt'][0], isset($options['alt'][1]) ? $options['alt'][1] : array(), isset($options['alt'][2]) ? $options['alt'][2] : array());
}
return $this->container->getEsiService()->renderTag($uri, $alt, $options['ignore_errors'], $options['comment']);
}
$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;
}
}
}
/**
* Returns the Controller instance associated with a Request.
*
* This method looks for a '_controller' request parameter that represents
* the controller name (a string like BlogBundle:Post:index).
*
* @param Request $request A Request instance
*
* @return mixed|Boolean A PHP callable representing the Controller,
* or false if this resolver is not able to determine the controller
*
* @throws \InvalidArgumentException|\LogicException If the controller can't be found
*/
public function getController(Request $request)
{
if (!$controller = $request->attributes->get('_controller')) {
if (null !== $this->logger) {
$this->logger->err('Unable to look for the controller as the "_controller" parameter is missing');
}
return false;
}
list($bundle, $controller, $action) = explode(':', $controller);
$bundle = strtr($bundle, array('/' => '\\'));
$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.'\\'.strtr($bundle, array('\\' => '')), 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);
}
/**
* Returns the arguments to pass to the controller.
*
* @param Request $request A Request instance
* @param mixed $controller A PHP callable
*
* @throws \RuntimeException When value for argument given is not provided
*/
public function getArguments(Request $request, $controller)
{
$attributes = $request->attributes->all();
list($controller, $method) = $controller;
$r = new \ReflectionObject($controller);
$arguments = array();
foreach ($r->getMethod($method)->getParameters() as $param) {
if (array_key_exists($param->getName(), $attributes)) {
$arguments[] = $attributes[$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).', get_class($controller), $method, $param->getName()));
}
}
return $arguments;
}
2010-06-23 20:42:41 +01:00
/**
* Generates an internal URI for a given controller.
*
* This method uses the "_internal" route, which should be available.
*
* @param string $controller A controller name to execute (a string like BlogBundle:Post:index), or a relative URI
* @param array $path An array of path parameters
* @param array $query An array of query parameters
*
* @return string An internal URI
*/
public function generateInternalUri($controller, array $path = array(), array $query = array())
{
if (0 === strpos($controller, '/')) {
return $controller;
}
$uri = $this->container->getRouterService()->generate('_internal', array(
'controller' => $controller,
'path' => $path ? http_build_query($path) : 'none',
'_format' => $this->container->getRequestService()->getRequestFormat(),
), true);
if ($query) {
$uri = $uri.'?'.http_build_query($query);
}
return $uri;
}
}