29e3342c3f1edb789ab8cf808ae23d5b7b9f14d2
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Http / Dispatcher.php
1 <?php
2 namespace TYPO3\CMS\Core\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Core\Configuration\Features;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Dispatcher which resolves a target, which was given to the request to call a controller and method (but also a callable)
24 * where the request contains a "target" as attribute.
25 *
26 * Used in eID Frontend Requests, see EidHandler
27 */
28 class Dispatcher implements DispatcherInterface
29 {
30 /**
31 * Main method that fetches the target from the request and calls the target directly
32 *
33 * @param ServerRequestInterface $request the current server request
34 * @param ResponseInterface $response the prepared response @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
35 * @return ResponseInterface the filled response by the callable/controller/action
36 * @throws \InvalidArgumentException if the defined target is invalid
37 */
38 public function dispatch(ServerRequestInterface $request, ResponseInterface $response = null): ResponseInterface
39 {
40 $targetIdentifier = $request->getAttribute('target');
41 $target = $this->getCallableFromTarget($targetIdentifier);
42 $arguments = [$request];
43
44 // @deprecated Test if target accepts one (ok) or two (deprecated) arguments
45 $scanForResponse = !GeneralUtility::makeInstance(Features::class)
46 ->isFeatureEnabled('simplifiedControllerActionDispatching');
47 if ($scanForResponse) {
48 if (is_array($targetIdentifier)) {
49 $controllerActionName = implode('::', $targetIdentifier);
50 $targetReflection = new \ReflectionMethod($controllerActionName);
51 } elseif (is_string($targetIdentifier) && strpos($targetIdentifier, '::') !== false) {
52 $controllerActionName = $targetIdentifier;
53 $targetReflection = new \ReflectionMethod($controllerActionName);
54 } elseif (is_callable($targetIdentifier)) {
55 $controllerActionName = 'closure function';
56 $targetReflection = new \ReflectionFunction($targetIdentifier);
57 } else {
58 $controllerActionName = $targetIdentifier . '::__invoke';
59 $targetReflection = new \ReflectionMethod($controllerActionName);
60 }
61 if ($targetReflection->getNumberOfParameters() >= 2) {
62 trigger_error(
63 'Handing over second argument $response to controller action ' . $controllerActionName . '() is deprecated and will be removed in TYPO3 v10.0.',
64 E_USER_DEPRECATED
65 );
66 $arguments[] = $response;
67 }
68 }
69
70 return call_user_func_array($target, $arguments);
71 }
72
73 /**
74 * Creates a callable out of the given parameter, which can be a string, a callable / closure or an array
75 * which can be handed to call_user_func_array()
76 *
77 * @param array|string|callable $target the target which is being resolved.
78 * @return callable
79 * @throws \InvalidArgumentException
80 */
81 protected function getCallableFromTarget($target)
82 {
83 if (is_array($target)) {
84 return $target;
85 }
86
87 if (is_object($target) && $target instanceof \Closure) {
88 return $target;
89 }
90
91 // Only a class name is given
92 if (is_string($target) && strpos($target, ':') === false) {
93 $targetObject = GeneralUtility::makeInstance($target);
94 if (!method_exists($targetObject, '__invoke')) {
95 throw new \InvalidArgumentException('Object "' . $target . '" doesn\'t implement an __invoke() method and cannot be used as target.', 1442431631);
96 }
97 return $targetObject;
98 }
99
100 // Check if the target is a concatenated string of "className::actionMethod"
101 if (is_string($target) && strpos($target, '::') !== false) {
102 list($className, $methodName) = explode('::', $target, 2);
103 $targetObject = GeneralUtility::makeInstance($className);
104 return [$targetObject, $methodName];
105 }
106
107 // Closures needs to be checked at last as a string with object::method is recognized as callable
108 if (is_callable($target)) {
109 return $target;
110 }
111
112 throw new \InvalidArgumentException('Invalid target for "' . $target . '", as it is not callable.', 1425381442);
113 }
114 }