vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php line 178

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\HttpKernel\Controller;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\HttpFoundation\Request;
  13. /**
  14.  * This implementation uses the '_controller' request attribute to determine
  15.  * the controller to execute and uses the request attributes to determine
  16.  * the controller method arguments.
  17.  *
  18.  * @author Fabien Potencier <fabien@symfony.com>
  19.  */
  20. class ControllerResolver implements ArgumentResolverInterfaceControllerResolverInterface
  21. {
  22.     private $logger;
  23.     /**
  24.      * If the ...$arg functionality is available.
  25.      *
  26.      * Requires at least PHP 5.6.0 or HHVM 3.9.1
  27.      *
  28.      * @var bool
  29.      */
  30.     private $supportsVariadic;
  31.     /**
  32.      * If scalar types exists.
  33.      *
  34.      * @var bool
  35.      */
  36.     private $supportsScalarTypes;
  37.     public function __construct(LoggerInterface $logger null)
  38.     {
  39.         $this->logger $logger;
  40.         $this->supportsVariadic method_exists('ReflectionParameter''isVariadic');
  41.         $this->supportsScalarTypes method_exists('ReflectionParameter''getType');
  42.     }
  43.     /**
  44.      * {@inheritdoc}
  45.      *
  46.      * This method looks for a '_controller' request attribute that represents
  47.      * the controller name (a string like ClassName::MethodName).
  48.      */
  49.     public function getController(Request $request)
  50.     {
  51.         if (!$controller $request->attributes->get('_controller')) {
  52.             if (null !== $this->logger) {
  53.                 $this->logger->warning('Unable to look for the controller as the "_controller" parameter is missing.');
  54.             }
  55.             return false;
  56.         }
  57.         if (\is_array($controller)) {
  58.             return $controller;
  59.         }
  60.         if (\is_object($controller)) {
  61.             if (method_exists($controller'__invoke')) {
  62.                 return $controller;
  63.             }
  64.             throw new \InvalidArgumentException(sprintf('Controller "%s" for URI "%s" is not callable.', \get_class($controller), $request->getPathInfo()));
  65.         }
  66.         if (\is_string($controller) && false === strpos($controller':')) {
  67.             if (method_exists($controller'__invoke')) {
  68.                 return $this->instantiateController($controller);
  69.             } elseif (\function_exists($controller)) {
  70.                 return $controller;
  71.             }
  72.         }
  73.         try {
  74.             $callable $this->createController($controller);
  75.         } catch (\InvalidArgumentException $e) {
  76.             throw new \InvalidArgumentException(sprintf('The controller for URI "%s" is not callable: '$request->getPathInfo()).$e->getMessage(), 0$e);
  77.         }
  78.         return $callable;
  79.     }
  80.     /**
  81.      * {@inheritdoc}
  82.      *
  83.      * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface and inject it in the HttpKernel instead.
  84.      */
  85.     public function getArguments(Request $request$controller)
  86.     {
  87.         @trigger_error(sprintf('The "%s()" method is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.'__METHOD__ArgumentResolverInterface::class), \E_USER_DEPRECATED);
  88.         if (\is_array($controller)) {
  89.             $r = new \ReflectionMethod($controller[0], $controller[1]);
  90.         } elseif (\is_object($controller) && !$controller instanceof \Closure) {
  91.             $r = new \ReflectionObject($controller);
  92.             $r $r->getMethod('__invoke');
  93.         } else {
  94.             $r = new \ReflectionFunction($controller);
  95.         }
  96.         return $this->doGetArguments($request$controller$r->getParameters());
  97.     }
  98.     /**
  99.      * @param callable               $controller
  100.      * @param \ReflectionParameter[] $parameters
  101.      *
  102.      * @return array The arguments to use when calling the action
  103.      *
  104.      * @deprecated This method is deprecated as of 3.1 and will be removed in 4.0. Implement the ArgumentResolverInterface and inject it in the HttpKernel instead.
  105.      */
  106.     protected function doGetArguments(Request $request$controller, array $parameters)
  107.     {
  108.         @trigger_error(sprintf('The "%s()" method is deprecated as of 3.1 and will be removed in 4.0. Implement the %s and inject it in the HttpKernel instead.'__METHOD__ArgumentResolverInterface::class), \E_USER_DEPRECATED);
  109.         $attributes $request->attributes->all();
  110.         $arguments = [];
  111.         foreach ($parameters as $param) {
  112.             if (\array_key_exists($param->name$attributes)) {
  113.                 if ($this->supportsVariadic && $param->isVariadic() && \is_array($attributes[$param->name])) {
  114.                     $arguments array_merge($argumentsarray_values($attributes[$param->name]));
  115.                 } else {
  116.                     $arguments[] = $attributes[$param->name];
  117.                 }
  118.             } elseif ($this->typeMatchesRequestClass($param$request)) {
  119.                 $arguments[] = $request;
  120.             } elseif ($param->isDefaultValueAvailable()) {
  121.                 $arguments[] = $param->getDefaultValue();
  122.             } elseif ($this->supportsScalarTypes && $param->hasType() && $param->allowsNull()) {
  123.                 $arguments[] = null;
  124.             } else {
  125.                 if (\is_array($controller)) {
  126.                     $repr sprintf('%s::%s()', \get_class($controller[0]), $controller[1]);
  127.                 } elseif (\is_object($controller)) {
  128.                     $repr = \get_class($controller);
  129.                 } else {
  130.                     $repr $controller;
  131.                 }
  132.                 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));
  133.             }
  134.         }
  135.         return $arguments;
  136.     }
  137.     /**
  138.      * Returns a callable for the given controller.
  139.      *
  140.      * @param string $controller A Controller string
  141.      *
  142.      * @return callable A PHP callable
  143.      *
  144.      * @throws \InvalidArgumentException When the controller cannot be created
  145.      */
  146.     protected function createController($controller)
  147.     {
  148.         if (false === strpos($controller'::')) {
  149.             throw new \InvalidArgumentException(sprintf('Unable to find controller "%s".'$controller));
  150.         }
  151.         list($class$method) = explode('::'$controller2);
  152.         if (!class_exists($class)) {
  153.             throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.'$class));
  154.         }
  155.         $controller = [$this->instantiateController($class), $method];
  156.         if (!\is_callable($controller)) {
  157.             throw new \InvalidArgumentException($this->getControllerError($controller));
  158.         }
  159.         return $controller;
  160.     }
  161.     /**
  162.      * Returns an instantiated controller.
  163.      *
  164.      * @param string $class A class name
  165.      *
  166.      * @return object
  167.      */
  168.     protected function instantiateController($class)
  169.     {
  170.         return new $class();
  171.     }
  172.     private function getControllerError($callable)
  173.     {
  174.         if (\is_string($callable)) {
  175.             if (false !== strpos($callable'::')) {
  176.                 $callable explode('::'$callable);
  177.             }
  178.             if (class_exists($callable) && !method_exists($callable'__invoke')) {
  179.                 return sprintf('Class "%s" does not have a method "__invoke".'$callable);
  180.             }
  181.             if (!\function_exists($callable)) {
  182.                 return sprintf('Function "%s" does not exist.'$callable);
  183.             }
  184.         }
  185.         if (!\is_array($callable)) {
  186.             return sprintf('Invalid type for controller given, expected string or array, got "%s".', \gettype($callable));
  187.         }
  188.         if (!== \count($callable)) {
  189.             return 'Invalid format for controller, expected [controller, method] or controller::method.';
  190.         }
  191.         list($controller$method) = $callable;
  192.         if (\is_string($controller) && !class_exists($controller)) {
  193.             return sprintf('Class "%s" does not exist.'$controller);
  194.         }
  195.         $className = \is_object($controller) ? \get_class($controller) : $controller;
  196.         if (method_exists($controller$method)) {
  197.             return sprintf('Method "%s" on class "%s" should be public and non-abstract.'$method$className);
  198.         }
  199.         $collection get_class_methods($controller);
  200.         $alternatives = [];
  201.         foreach ($collection as $item) {
  202.             $lev levenshtein($method$item);
  203.             if ($lev <= \strlen($method) / || false !== strpos($item$method)) {
  204.                 $alternatives[] = $item;
  205.             }
  206.         }
  207.         asort($alternatives);
  208.         $message sprintf('Expected method "%s" on class "%s"'$method$className);
  209.         if (\count($alternatives) > 0) {
  210.             $message .= sprintf(', did you mean "%s"?'implode('", "'$alternatives));
  211.         } else {
  212.             $message .= sprintf('. Available methods: "%s".'implode('", "'$collection));
  213.         }
  214.         return $message;
  215.     }
  216.     /**
  217.      * @return bool
  218.      */
  219.     private function typeMatchesRequestClass(\ReflectionParameter $paramRequest $request)
  220.     {
  221.         if (!method_exists($param'getType')) {
  222.             return $param->getClass() && $param->getClass()->isInstance($request);
  223.         }
  224.         if (!($type $param->getType()) || $type->isBuiltin()) {
  225.             return false;
  226.         }
  227.         $class = new \ReflectionClass($type instanceof \ReflectionNamedType $type->getName() : (string) $type);
  228.         return $class && $class->isInstance($request);
  229.     }
  230. }