[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Mvc / Controller / CommandController.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Mvc\Controller;
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 TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition;
20 use TYPO3\CMS\Extbase\Mvc\Cli\ConsoleOutput;
21 use TYPO3\CMS\Extbase\Mvc\Cli\Request;
22 use TYPO3\CMS\Extbase\Mvc\Cli\Response;
23 use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException;
24 use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException;
25 use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
26 use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
27 use TYPO3\CMS\Extbase\Mvc\RequestInterface;
28 use TYPO3\CMS\Extbase\Mvc\ResponseInterface;
29 use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
30 use TYPO3\CMS\Extbase\Reflection\ReflectionService;
31
32 /**
33 * A controller which processes requests from the command line
34 *
35 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
36 */
37 class CommandController implements CommandControllerInterface
38 {
39 /**
40 * @var Request
41 */
42 protected $request;
43
44 /**
45 * @var Response
46 */
47 protected $response;
48
49 /**
50 * @var Arguments
51 */
52 protected $arguments;
53
54 /**
55 * Name of the command method
56 *
57 * @var string
58 */
59 protected $commandMethodName = '';
60
61 /**
62 * Whether the command needs admin access to perform its job
63 *
64 * @var bool
65 * @api
66 */
67 protected $requestAdminPermissions = false;
68
69 /**
70 * @var ReflectionService
71 */
72 protected $reflectionService;
73
74 /**
75 * @var ObjectManagerInterface
76 */
77 protected $objectManager;
78
79 /**
80 * @var ConsoleOutput
81 */
82 protected $output;
83
84 /**
85 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
86 */
87 public function injectObjectManager(ObjectManagerInterface $objectManager)
88 {
89 $this->objectManager = $objectManager;
90 }
91
92 /**
93 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
94 */
95 public function injectReflectionService(ReflectionService $reflectionService)
96 {
97 $this->reflectionService = $reflectionService;
98 }
99
100 /**
101 * Checks if the current request type is supported by the controller.
102 *
103 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
104 * @return bool TRUE if this request type is supported, otherwise FALSE
105 */
106 public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)
107 {
108 return $request instanceof Request;
109 }
110
111 /**
112 * Processes a command line request.
113 *
114 * @param RequestInterface $request The request object
115 * @param ResponseInterface $response The response, modified by this handler
116 * @throws UnsupportedRequestTypeException if the controller doesn't support the current request type
117 * @api
118 */
119 public function processRequest(RequestInterface $request, ResponseInterface $response)
120 {
121 if (!$this->canProcessRequest($request)) {
122 throw new UnsupportedRequestTypeException(sprintf('%s only supports command line requests – requests of type "%s" given.', get_class($this), get_class($request)), 1300787096);
123 }
124
125 $this->request = $request;
126 $this->request->setDispatched(true);
127 $this->response = $response;
128
129 $this->commandMethodName = $this->resolveCommandMethodName();
130 $this->output = $this->objectManager->get(ConsoleOutput::class);
131 $this->arguments = $this->objectManager->get(Arguments::class);
132 $this->initializeCommandMethodArguments();
133 $this->mapRequestArgumentsToControllerArguments();
134 $this->initializeBackendAuthentication();
135 $this->callCommandMethod();
136 }
137
138 /**
139 * Resolves and checks the current command method name
140 *
141 * Note: The resulting command method name might not have the correct case, which isn't a problem because PHP is
142 * case insensitive regarding method names.
143 *
144 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException
145 * @return string Method name of the current command
146 * @throws NoSuchCommandException
147 */
148 protected function resolveCommandMethodName()
149 {
150 $commandMethodName = $this->request->getControllerCommandName() . 'Command';
151 if (!is_callable([$this, $commandMethodName])) {
152 throw new NoSuchCommandException(sprintf('A command method "%s()" does not exist in controller "%s".', $commandMethodName, get_class($this)), 1300902143);
153 }
154 return $commandMethodName;
155 }
156
157 /**
158 * Initializes the arguments array of this controller by creating an empty argument object for each of the
159 * method arguments found in the designated command method.
160 *
161 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException
162 * @throws InvalidArgumentTypeException
163 */
164 protected function initializeCommandMethodArguments()
165 {
166 $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->commandMethodName);
167
168 foreach ($methodParameters as $parameterName => $parameterInfo) {
169 $dataType = null;
170 if (isset($parameterInfo['type'])) {
171 $dataType = $parameterInfo['type'];
172 } elseif ($parameterInfo['array']) {
173 $dataType = 'array';
174 }
175 if ($dataType === null) {
176 throw new InvalidArgumentTypeException(sprintf('The argument type for parameter $%s of method %s->%s() could not be detected.', $parameterName, get_class($this), $this->commandMethodName), 1306755296);
177 }
178 $defaultValue = (isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : null);
179 $this->arguments->addNewArgument($parameterName, $dataType, ($parameterInfo['optional'] === false), $defaultValue);
180 }
181 }
182
183 /**
184 * Maps arguments delivered by the request object to the local controller arguments.
185 */
186 protected function mapRequestArgumentsToControllerArguments()
187 {
188 /** @var Argument $argument */
189 foreach ($this->arguments as $argument) {
190 $argumentName = $argument->getName();
191 if ($this->request->hasArgument($argumentName)) {
192 $argument->setValue($this->request->getArgument($argumentName));
193 continue;
194 }
195 if (!$argument->isRequired()) {
196 continue;
197 }
198 $argumentValue = null;
199 $commandArgumentDefinition = $this->objectManager->get(CommandArgumentDefinition::class, $argumentName, true, null);
200 while ($argumentValue === null) {
201 $argumentValue = $this->output->ask(sprintf('<comment>Please specify the required argument "%s":</comment> ', $commandArgumentDefinition->getDashedName()));
202 }
203 $argument->setValue($argumentValue);
204 }
205 }
206
207 /**
208 * Initializes and ensures authenticated backend access
209 */
210 protected function initializeBackendAuthentication()
211 {
212 $backendUserAuthentication = $this->getBackendUserAuthentication();
213 if ($backendUserAuthentication !== null) {
214 $backendUserAuthentication->backendCheckLogin();
215 }
216 }
217
218 /**
219 * Forwards the request to another command and / or CommandController.
220 *
221 * Request is directly transferred to the other command / controller
222 * without the need for a new request.
223 *
224 * @param string $commandName
225 * @param string $controllerObjectName
226 * @param array $arguments
227 * @throws StopActionException
228 */
229 protected function forward($commandName, $controllerObjectName = null, array $arguments = [])
230 {
231 $this->request->setDispatched(false);
232 $this->request->setControllerCommandName($commandName);
233 if ($controllerObjectName !== null) {
234 $this->request->setControllerObjectName($controllerObjectName);
235 }
236 $this->request->setArguments($arguments);
237
238 $this->arguments->removeAll();
239 throw new StopActionException('forward', 1476107661);
240 }
241
242 /**
243 * Calls the specified command method and passes the arguments.
244 *
245 * If the command returns a string, it is appended to the content in the
246 * response object. If the command doesn't return anything and a valid
247 * view exists, the view is rendered automatically.
248 */
249 protected function callCommandMethod()
250 {
251 $preparedArguments = [];
252 /** @var Argument $argument */
253 foreach ($this->arguments as $argument) {
254 $preparedArguments[] = $argument->getValue();
255 }
256 $commandResult = call_user_func_array([$this, $this->commandMethodName], $preparedArguments);
257 if (is_string($commandResult) && $commandResult !== '') {
258 $this->response->appendContent($commandResult);
259 } elseif (is_object($commandResult) && method_exists($commandResult, '__toString')) {
260 $this->response->appendContent((string)$commandResult);
261 }
262 }
263
264 /**
265 * Set admin permissions for currently authenticated user if requested
266 * and returns the original state or NULL
267 *
268 * @return null|int
269 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, because admin role is always used in CLI mode
270 */
271 protected function ensureAdminRoleIfRequested()
272 {
273 GeneralUtility::logDeprecatedFunction();
274 $userAuthentication = $this->getBackendUserAuthentication();
275
276 if (!$this->requestAdminPermissions || $userAuthentication === null || !isset($userAuthentication->user['admin'])) {
277 return null;
278 }
279
280 $originalRole = $userAuthentication->user['admin'];
281 $userAuthentication->user['admin'] = 1;
282 return $originalRole;
283 }
284
285 /**
286 * Restores the original user role
287 *
288 * @param null|int $originalRole
289 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9, because admin role is always used in CLI mode
290 */
291 protected function restoreUserRole($originalRole)
292 {
293 GeneralUtility::logDeprecatedFunction();
294 $userAuthentication = $this->getBackendUserAuthentication();
295
296 if ($originalRole !== null && $userAuthentication !== null) {
297 $userAuthentication->user['admin'] = $originalRole;
298 }
299 }
300
301 /**
302 * Outputs specified text to the console window
303 * You can specify arguments that will be passed to the text via sprintf
304 *
305 * @see http://www.php.net/sprintf
306 * @param string $text Text to output
307 * @param array $arguments Optional arguments to use for sprintf
308 */
309 protected function output($text, array $arguments = [])
310 {
311 $this->output->output($text, $arguments);
312 }
313
314 /**
315 * Outputs specified text to the console window and appends a line break
316 *
317 * @param string $text Text to output
318 * @param array $arguments Optional arguments to use for sprintf
319 * @see output()
320 */
321 protected function outputLine($text = '', array $arguments = [])
322 {
323 $this->output->outputLine($text, $arguments);
324 }
325
326 /**
327 * Formats the given text to fit into MAXIMUM_LINE_LENGTH and outputs it to the
328 * console window
329 *
330 * @param string $text Text to output
331 * @param array $arguments Optional arguments to use for sprintf
332 * @param int $leftPadding The number of spaces to use for indentation
333 * @see outputLine()
334 */
335 protected function outputFormatted($text = '', array $arguments = [], $leftPadding = 0)
336 {
337 $this->output->outputFormatted($text, $arguments, $leftPadding);
338 }
339
340 /**
341 * Exits the CLI through the dispatcher
342 * An exit status code can be specified @see http://www.php.net/exit
343 *
344 * @param int $exitCode Exit code to return on exit
345 * @throws StopActionException
346 */
347 protected function quit($exitCode = 0)
348 {
349 $this->response->setExitCode($exitCode);
350 throw new StopActionException('quit', 1476107681);
351 }
352
353 /**
354 * Sends the response and exits the CLI without any further code execution
355 * Should be used for commands that flush code caches.
356 *
357 * @param int $exitCode Exit code to return on exit
358 */
359 protected function sendAndExit($exitCode = 0)
360 {
361 $this->response->send();
362 exit($exitCode);
363 }
364
365 /**
366 * Returns the global BackendUserAuthentication object.
367 *
368 * @return BackendUserAuthentication|null
369 */
370 protected function getBackendUserAuthentication()
371 {
372 return isset($GLOBALS['BE_USER']) ? $GLOBALS['BE_USER'] : null;
373 }
374 }