[BUGFIX] Output dashed name for missing CLI arguments
[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 use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
17 use TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition;
18
19 /**
20 * A controller which processes requests from the command line
21 *
22 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
23 */
24 class CommandController implements CommandControllerInterface {
25
26 const MAXIMUM_LINE_LENGTH = 79;
27
28 /**
29 * @var \TYPO3\CMS\Extbase\Mvc\Cli\Request
30 */
31 protected $request;
32
33 /**
34 * @var \TYPO3\CMS\Extbase\Mvc\Cli\Response
35 */
36 protected $response;
37
38 /**
39 * @var \TYPO3\CMS\Extbase\Mvc\Controller\Arguments
40 */
41 protected $arguments;
42
43 /**
44 * Name of the command method
45 *
46 * @var string
47 */
48 protected $commandMethodName = '';
49
50 /**
51 * Whether the command needs admin access to perform its job
52 *
53 * @var bool
54 * @api
55 */
56 protected $requestAdminPermissions = FALSE;
57
58 /**
59 * @var AbstractUserAuthentication
60 */
61 protected $userAuthentication;
62
63 /**
64 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
65 * @inject
66 */
67 protected $reflectionService;
68
69 /**
70 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
71 */
72 protected $objectManager;
73
74 /**
75 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
76 * @return void
77 */
78 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) {
79 $this->objectManager = $objectManager;
80 $this->arguments = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments::class);
81 $this->userAuthentication = isset($GLOBALS['BE_USER']) ? $GLOBALS['BE_USER'] : NULL;
82 }
83
84 /**
85 * Checks if the current request type is supported by the controller.
86 *
87 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
88 * @return bool TRUE if this request type is supported, otherwise FALSE
89 */
90 public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request) {
91 return $request instanceof \TYPO3\CMS\Extbase\Mvc\Cli\Request;
92 }
93
94 /**
95 * Processes a command line request.
96 *
97 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
98 * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this controller
99 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
100 * @return void
101 * @api
102 */
103 public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response) {
104 if (!$this->canProcessRequest($request)) {
105 throw new \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException(get_class($this) . ' does not support requests of type "' . get_class($request) . '".', 1300787096);
106 }
107 $this->request = $request;
108 $this->request->setDispatched(TRUE);
109 $this->response = $response;
110 $this->commandMethodName = $this->resolveCommandMethodName();
111 $this->initializeCommandMethodArguments();
112 $this->mapRequestArgumentsToControllerArguments();
113 $this->callCommandMethod();
114 }
115
116 /**
117 * Resolves and checks the current command method name
118 *
119 * Note: The resulting command method name might not have the correct case, which isn't a problem because PHP is
120 * case insensitive regarding method names.
121 *
122 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException
123 * @return string Method name of the current command
124 */
125 protected function resolveCommandMethodName() {
126 $commandMethodName = $this->request->getControllerCommandName() . 'Command';
127 if (!is_callable(array($this, $commandMethodName))) {
128 throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException('A command method "' . $commandMethodName . '()" does not exist in controller "' . get_class($this) . '".', 1300902143);
129 }
130 return $commandMethodName;
131 }
132
133 /**
134 * Initializes the arguments array of this controller by creating an empty argument object for each of the
135 * method arguments found in the designated command method.
136 *
137 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException
138 * @return void
139 */
140 protected function initializeCommandMethodArguments() {
141 $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->commandMethodName);
142 foreach ($methodParameters as $parameterName => $parameterInfo) {
143 $dataType = NULL;
144 if (isset($parameterInfo['type'])) {
145 $dataType = $parameterInfo['type'];
146 } elseif ($parameterInfo['array']) {
147 $dataType = 'array';
148 }
149 if ($dataType === NULL) {
150 throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException('The argument type for parameter $' . $parameterName . ' of method ' . get_class($this) . '->' . $this->commandMethodName . '() could not be detected.', 1306755296);
151 }
152 $defaultValue = isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : NULL;
153 $this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === FALSE, $defaultValue);
154 }
155 }
156
157 /**
158 * Maps arguments delivered by the request object to the local controller arguments.
159 *
160 * @return void
161 */
162 protected function mapRequestArgumentsToControllerArguments() {
163 /** @var Argument $argument */
164 foreach ($this->arguments as $argument) {
165 $argumentName = $argument->getName();
166 if ($this->request->hasArgument($argumentName)) {
167 $argument->setValue($this->request->getArgument($argumentName));
168 } elseif ($argument->isRequired()) {
169 $commandArgumentDefinition = $this->objectManager->get(CommandArgumentDefinition::class, $argumentName, TRUE, NULL);
170 $exception = new \TYPO3\CMS\Extbase\Mvc\Exception\CommandException('Required argument "' . $commandArgumentDefinition->getDashedName() . '" is not set.', 1306755520);
171 $this->forward('error', 'TYPO3\\CMS\\Extbase\\Command\\HelpCommandController', array('exception' => $exception));
172 }
173 }
174 }
175
176 /**
177 * Forwards the request to another command and / or CommandController.
178 *
179 * Request is directly transferred to the other command / controller
180 * without the need for a new request.
181 *
182 * @param string $commandName
183 * @param string $controllerObjectName
184 * @param array $arguments
185 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
186 * @return void
187 */
188 protected function forward($commandName, $controllerObjectName = NULL, array $arguments = array()) {
189 $this->request->setDispatched(FALSE);
190 $this->request->setControllerCommandName($commandName);
191 if ($controllerObjectName !== NULL) {
192 $this->request->setControllerObjectName($controllerObjectName);
193 }
194 $this->request->setArguments($arguments);
195 $this->arguments->removeAll();
196 throw new \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException();
197 }
198
199 /**
200 * Calls the specified command method and passes the arguments.
201 *
202 * If the command returns a string, it is appended to the content in the
203 * response object. If the command doesn't return anything and a valid
204 * view exists, the view is rendered automatically.
205 *
206 * @return void
207 */
208 protected function callCommandMethod() {
209 $preparedArguments = array();
210 /** @var Argument $argument */
211 foreach ($this->arguments as $argument) {
212 $preparedArguments[] = $argument->getValue();
213 }
214 $originalRole = $this->ensureAdminRoleIfRequested();
215 $commandResult = call_user_func_array(array($this, $this->commandMethodName), $preparedArguments);
216 $this->restoreUserRole($originalRole);
217 if (is_string($commandResult) && strlen($commandResult) > 0) {
218 $this->response->appendContent($commandResult);
219 } elseif (is_object($commandResult) && method_exists($commandResult, '__toString')) {
220 $this->response->appendContent((string)$commandResult);
221 }
222 }
223
224 /**
225 * Set admin permissions for currently authenticated user if requested
226 * and returns the original state or NULL
227 *
228 * @return NULL|int
229 */
230 protected function ensureAdminRoleIfRequested() {
231 if (!$this->requestAdminPermissions || !$this->userAuthentication || !isset($this->userAuthentication->user['admin'])) {
232 return NULL;
233 }
234 $originalRole = $this->userAuthentication->user['admin'];
235 $this->userAuthentication->user['admin'] = 1;
236 return $originalRole;
237 }
238
239 /**
240 * Restores the original user role
241 *
242 * @param NULL|int $originalRole
243 */
244 protected function restoreUserRole($originalRole) {
245 if ($originalRole !== NULL) {
246 $this->userAuthentication->user['admin'] = $originalRole;
247 }
248 }
249
250 /**
251 * Outputs specified text to the console window
252 * You can specify arguments that will be passed to the text via sprintf
253 *
254 * @see http://www.php.net/sprintf
255 * @param string $text Text to output
256 * @param array $arguments Optional arguments to use for sprintf
257 * @return void
258 */
259 protected function output($text, array $arguments = array()) {
260 if ($arguments !== array()) {
261 $text = vsprintf($text, $arguments);
262 }
263 $this->response->appendContent($text);
264 }
265
266 /**
267 * Outputs specified text to the console window and appends a line break
268 *
269 * @param string $text Text to output
270 * @param array $arguments Optional arguments to use for sprintf
271 * @return void
272 * @see output()
273 */
274 protected function outputLine($text = '', array $arguments = array()) {
275 $this->output($text . PHP_EOL, $arguments);
276 }
277
278 /**
279 * Exits the CLI through the dispatcher
280 * An exit status code can be specified @see http://www.php.net/exit
281 *
282 * @param int $exitCode Exit code to return on exit
283 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
284 * @return void
285 */
286 protected function quit($exitCode = 0) {
287 $this->response->setExitCode($exitCode);
288 throw new \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException();
289 }
290
291 /**
292 * Sends the response and exits the CLI without any further code execution
293 * Should be used for commands that flush code caches.
294 *
295 * @param int $exitCode Exit code to return on exit
296 * @return void
297 */
298 protected function sendAndExit($exitCode = 0) {
299 $this->response->send();
300 die($exitCode);
301 }
302 }