[TASK] Improve duplicate exception code check
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Console / CliRequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Backend\Console;
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 Symfony\Component\Console\Input\InputInterface;
18 use Symfony\Component\Console\Output\ConsoleOutput;
19 use TYPO3\CMS\Core\Console\RequestHandlerInterface;
20 use TYPO3\CMS\Core\Core\Bootstrap;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Core\Utility\StringUtility;
23
24 /**
25 * Command Line Interface Request Handler dealing with "cliKey"-based Commands from the cli_dispatch.phpsh script.
26 * Picks up requests only when coming from the CLI mode.
27 * Resolves the "cliKey" which is registered inside $TYPO3_CONF_VARS[SC_OPTIONS][GLOBAL][cliKeys]
28 * and includes the CLI-based script or exits if no valid "cliKey" is found.
29 * Also logs into the system as a backend user which needs to be added to the database called _CLI_mymodule
30 */
31 class CliRequestHandler implements RequestHandlerInterface
32 {
33 /**
34 * Instance of the current TYPO3 bootstrap
35 * @var Bootstrap
36 */
37 protected $bootstrap;
38
39 /**
40 * Constructor handing over the bootstrap
41 *
42 * @param Bootstrap $bootstrap
43 */
44 public function __construct(Bootstrap $bootstrap)
45 {
46 $this->bootstrap = $bootstrap;
47 }
48
49 /**
50 * Handles any commandline request
51 *
52 * @param InputInterface $input
53 * @return void
54 */
55 public function handleRequest(InputInterface $input)
56 {
57 $output = GeneralUtility::makeInstance(ConsoleOutput::class);
58 $exitCode = 0;
59
60 try {
61 $command = $this->validateCommandLineKeyFromInput($input);
62
63 // try and look up if the CLI command user exists, throws an exception if the CLI
64 // user cannot be found
65 list($commandLineScript, $commandLineName) = $this->getIncludeScriptByCommandLineKey($command);
66 $this->boot($commandLineName);
67
68 if (is_callable($commandLineScript)) {
69 call_user_func($commandLineScript);
70 } else {
71 // include the CLI script
72 include($commandLineScript);
73 }
74 } catch (\InvalidArgumentException $e) {
75 $output->writeln('<error>Oops, an error occurred: ' . $e->getMessage() . '</error>');
76 $output->writeln('');
77 $output->writeln('Valid keys are:');
78 $output->writeln('');
79 $cliKeys = array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys']);
80 asort($cliKeys);
81 foreach ($cliKeys as $key => $value) {
82 $output->writeln(' ' . $value);
83 }
84 $exitCode = $e->getCode();
85 } catch (\RuntimeException $e) {
86 $output->writeln('<error>Oops, an error occurred: ' . $e->getMessage() . '</error>');
87 $exitCode = $e->getCode();
88 } catch (\Throwable $e) {
89 $output->writeln('<error>Oops, an error occurred: ' . $e->getMessage() . '</error>');
90 $exitCode = $e->getCode();
91 }
92
93 exit($exitCode);
94 }
95
96 /**
97 * Execute TYPO3 bootstrap
98 *
99 * @throws \RuntimeException when the _CLI_ user cannot be authenticated properly
100 */
101 protected function boot($commandLineName)
102 {
103 $this->bootstrap
104 ->loadExtensionTables()
105 ->initializeBackendUser();
106
107 // Checks for a user called starting with _CLI_ e.g. "_CLI_lowlevel"
108 $this->loadCommandLineBackendUser($commandLineName);
109
110 $this->bootstrap
111 ->initializeBackendAuthentication()
112 ->initializeLanguageObject()
113 // Make sure output is not buffered, so command-line output and interaction can take place
114 ->endOutputBufferingAndCleanPreviousOutput();
115 }
116
117 /**
118 * Check CLI parameters.
119 * First argument is a key that points to the script configuration.
120 * If it is not set or not valid, the script exits with an error message.
121 *
122 * @param InputInterface $input an instance of the input given to the CLI call
123 * @return string the CLI key in use
124 * @throws \InvalidArgumentException
125 */
126 protected function validateCommandLineKeyFromInput(InputInterface $input)
127 {
128 $cliKey = $input->getFirstArgument();
129 if (empty($cliKey)) {
130 throw new \InvalidArgumentException('This script must have a command as first argument.', 1476107418);
131 } elseif (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys'][$cliKey])) {
132 throw new \InvalidArgumentException('This supplied command is not valid.', 1476107480);
133 }
134 return $cliKey;
135 }
136
137 /**
138 * Define cli-related parameters and return the include script as well as the command line name. Used for
139 * authentication against the backend user in the "loadCommandLineBackendUser()" action.
140 *
141 * @param string $cliKey the CLI key
142 * @return array the absolute path to the include script and the command line name
143 */
144 protected function getIncludeScriptByCommandLineKey($cliKey)
145 {
146 list($commandLineScript, $commandLineName) = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys'][$cliKey];
147 if (!is_callable($commandLineScript)) {
148 $commandLineScript = GeneralUtility::getFileAbsFileName($commandLineScript);
149 // Note: These constants are not in use anymore, and marked for deprecation and will be removed in TYPO3 CMS 8
150 define('TYPO3_cliKey', $cliKey);
151 define('TYPO3_cliInclude', $commandLineScript);
152 }
153 // This is a compatibility layer: Some cli scripts rely on this, like ext:phpunit cli
154 // This layer will be removed in TYPO3 CMS 8
155 $GLOBALS['temp_cliScriptPath'] = array_shift($_SERVER['argv']);
156 $GLOBALS['temp_cliKey'] = array_shift($_SERVER['argv']);
157 array_unshift($_SERVER['argv'], $GLOBALS['temp_cliScriptPath']);
158 return [$commandLineScript, $commandLineName];
159 }
160
161 /**
162 * If the backend script is in CLI mode, it will try to load a backend user named by the CLI module name (in lowercase)
163 *
164 * @param string $commandLineName the name of the module registered inside $TYPO3_CONF_VARS[SC_OPTIONS][GLOBAL][cliKeys] as second parameter
165 * @throws \RuntimeException if a non-admin Backend user could not be loaded
166 */
167 protected function loadCommandLineBackendUser($commandLineName)
168 {
169 if ($GLOBALS['BE_USER']->user['uid']) {
170 throw new \RuntimeException('Another user was already loaded which is impossible in CLI mode!', 1476107444);
171 }
172 if (!StringUtility::beginsWith($commandLineName, '_CLI_')) {
173 throw new \RuntimeException('Module name, "' . $commandLineName . '", was not prefixed with "_CLI_"', 1476107445);
174 }
175 $userName = strtolower($commandLineName);
176 $GLOBALS['BE_USER']->setBeUserByName($userName);
177 if (!$GLOBALS['BE_USER']->user['uid']) {
178 throw new \RuntimeException('No backend user named "' . $userName . '" was found!', 1476107195);
179 }
180 if ($GLOBALS['BE_USER']->isAdmin()) {
181 throw new \RuntimeException('CLI backend user "' . $userName . '" was ADMIN which is not allowed!', 1476107446);
182 }
183 }
184
185 /**
186 * This request handler can handle any CLI request.
187 *
188 * @param InputInterface $input
189 * @return bool Always TRUE
190 */
191 public function canHandleRequest(InputInterface $input)
192 {
193 return true;
194 }
195
196 /**
197 * Returns the priority - how eager the handler is to actually handle the request.
198 *
199 * @return int The priority of the request handler.
200 */
201 public function getPriority()
202 {
203 return 20;
204 }
205 }