ControllerResolver.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. namespace Drupal\Core\Controller;
  3. use Drupal\Core\DependencyInjection\ClassResolverInterface;
  4. use Drupal\Core\Routing\RouteMatch;
  5. use Drupal\Core\Routing\RouteMatchInterface;
  6. use Psr\Http\Message\ServerRequestInterface;
  7. use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
  8. use Symfony\Component\HttpFoundation\Request;
  9. use Symfony\Component\HttpKernel\Controller\ControllerResolver as BaseControllerResolver;
  10. /**
  11. * ControllerResolver to enhance controllers beyond Symfony's basic handling.
  12. *
  13. * It adds two behaviors:
  14. *
  15. * - When creating a new object-based controller that implements
  16. * ContainerAwareInterface, inject the container into it. While not always
  17. * necessary, that allows a controller to vary the services it needs at
  18. * runtime.
  19. *
  20. * - By default, a controller name follows the class::method notation. This
  21. * class adds the possibility to use a service from the container as a
  22. * controller by using a service:method notation (Symfony uses the same
  23. * convention).
  24. */
  25. class ControllerResolver extends BaseControllerResolver implements ControllerResolverInterface {
  26. /**
  27. * The class resolver.
  28. *
  29. * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
  30. */
  31. protected $classResolver;
  32. /**
  33. * The PSR-7 converter.
  34. *
  35. * @var \Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface
  36. */
  37. protected $httpMessageFactory;
  38. /**
  39. * Constructs a new ControllerResolver.
  40. *
  41. * @param \Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface $http_message_factory
  42. * The PSR-7 converter.
  43. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
  44. * The class resolver.
  45. */
  46. public function __construct(HttpMessageFactoryInterface $http_message_factory, ClassResolverInterface $class_resolver) {
  47. $this->httpMessageFactory = $http_message_factory;
  48. $this->classResolver = $class_resolver;
  49. }
  50. /**
  51. * {@inheritdoc}
  52. */
  53. public function getControllerFromDefinition($controller, $path = '') {
  54. if (is_array($controller) || (is_object($controller) && method_exists($controller, '__invoke'))) {
  55. return $controller;
  56. }
  57. if (strpos($controller, ':') === FALSE) {
  58. if (function_exists($controller)) {
  59. return $controller;
  60. }
  61. elseif (method_exists($controller, '__invoke')) {
  62. return new $controller();
  63. }
  64. }
  65. $callable = $this->createController($controller);
  66. if (!is_callable($callable)) {
  67. throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable.', $path));
  68. }
  69. return $callable;
  70. }
  71. /**
  72. * {@inheritdoc}
  73. */
  74. public function getController(Request $request) {
  75. if (!$controller = $request->attributes->get('_controller')) {
  76. return FALSE;
  77. }
  78. return $this->getControllerFromDefinition($controller, $request->getPathInfo());
  79. }
  80. /**
  81. * Returns a callable for the given controller.
  82. *
  83. * @param string $controller
  84. * A Controller string.
  85. *
  86. * @return mixed
  87. * A PHP callable.
  88. *
  89. * @throws \LogicException
  90. * If the controller cannot be parsed.
  91. *
  92. * @throws \InvalidArgumentException
  93. * If the controller class does not exist.
  94. */
  95. protected function createController($controller) {
  96. // Controller in the service:method notation.
  97. $count = substr_count($controller, ':');
  98. if ($count == 1) {
  99. list($class_or_service, $method) = explode(':', $controller, 2);
  100. }
  101. // Controller in the class::method notation.
  102. elseif (strpos($controller, '::') !== FALSE) {
  103. list($class_or_service, $method) = explode('::', $controller, 2);
  104. }
  105. else {
  106. throw new \LogicException(sprintf('Unable to parse the controller name "%s".', $controller));
  107. }
  108. $controller = $this->classResolver->getInstanceFromDefinition($class_or_service);
  109. return [$controller, $method];
  110. }
  111. /**
  112. * {@inheritdoc}
  113. */
  114. protected function doGetArguments(Request $request, $controller, array $parameters) {
  115. $attributes = $request->attributes->all();
  116. $raw_parameters = $request->attributes->has('_raw_variables') ? $request->attributes->get('_raw_variables') : [];
  117. $arguments = [];
  118. foreach ($parameters as $param) {
  119. if (array_key_exists($param->name, $attributes)) {
  120. $arguments[] = $attributes[$param->name];
  121. }
  122. elseif (array_key_exists($param->name, $raw_parameters)) {
  123. $arguments[] = $attributes[$param->name];
  124. }
  125. elseif ($param->getClass() && $param->getClass()->isInstance($request)) {
  126. $arguments[] = $request;
  127. }
  128. elseif ($param->getClass() && $param->getClass()->name === ServerRequestInterface::class) {
  129. $arguments[] = $this->httpMessageFactory->createRequest($request);
  130. }
  131. elseif ($param->getClass() && ($param->getClass()->name == RouteMatchInterface::class || is_subclass_of($param->getClass()->name, RouteMatchInterface::class))) {
  132. $arguments[] = RouteMatch::createFromRequest($request);
  133. }
  134. elseif ($param->isDefaultValueAvailable()) {
  135. $arguments[] = $param->getDefaultValue();
  136. }
  137. else {
  138. if (is_array($controller)) {
  139. $repr = sprintf('%s::%s()', get_class($controller[0]), $controller[1]);
  140. }
  141. elseif (is_object($controller)) {
  142. $repr = get_class($controller);
  143. }
  144. else {
  145. $repr = $controller;
  146. }
  147. 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).', $repr, $param->name));
  148. }
  149. }
  150. return $arguments;
  151. }
  152. }