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