[!!!][FEATURE] Introduce single _cli_ user for all CLI calls
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Console / CommandRequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Core\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\Application;
18 use Symfony\Component\Console\Command\Command;
19 use Symfony\Component\Console\Input\InputInterface;
20 use Symfony\Component\Console\Output\ConsoleOutput;
21 use TYPO3\CMS\Core\Authentication\CommandLineUserAuthentication;
22 use TYPO3\CMS\Core\Core\Bootstrap;
23 use TYPO3\CMS\Core\Package\PackageManager;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25
26 /**
27 * Command Line Interface Request Handler dealing with registered commands.
28 */
29 class CommandRequestHandler implements RequestHandlerInterface
30 {
31 /**
32 * Instance of the current TYPO3 bootstrap
33 * @var Bootstrap
34 */
35 protected $bootstrap;
36
37 /**
38 * Instance of the symfony application
39 * @var Application
40 */
41 protected $application;
42
43 /**
44 * @var []
45 */
46 protected $availableCommands;
47
48 /**
49 * Constructor handing over the bootstrap
50 *
51 * @param Bootstrap $bootstrap
52 */
53 public function __construct(Bootstrap $bootstrap)
54 {
55 $this->bootstrap = $bootstrap;
56 $this->application = new Application('TYPO3 CMS', TYPO3_version);
57 }
58
59 /**
60 * Handles any commandline request
61 *
62 * @param InputInterface $input
63 * @return void
64 */
65 public function handleRequest(InputInterface $input)
66 {
67 $output = new ConsoleOutput();
68
69 $this->bootstrap->loadExtensionTables();
70
71 // Check if the command to run needs a backend user to be loaded
72 $command = $this->getCommandToRun($input);
73 foreach ($this->availableCommands as $data) {
74 if ($data['command'] !== $command) {
75 continue;
76 }
77 if (isset($data['user'])) {
78 $this->initializeBackendUser();
79 }
80 }
81
82 // Make sure output is not buffered, so command-line output and interaction can take place
83 $this->bootstrap->endOutputBufferingAndCleanPreviousOutput();
84
85 if (!$command) {
86 $cliKeys = array_keys($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['cliKeys']);
87
88 $output->writeln('Old entrypoint keys available:');
89 asort($cliKeys);
90 foreach ($cliKeys as $key => $value) {
91 $output->writeln(' ' . $value);
92 }
93 $output->writeln('');
94 $output->writeln('TYPO3 Console Commands:');
95 }
96
97 $exitCode = $this->application->run($input, $output);
98 exit($exitCode);
99 }
100
101 /**
102 * Logs in the user _cli_ to the system
103 *
104 * @throws \RuntimeException if a non-admin Backend user could not be loaded
105 */
106 protected function initializeBackendUser()
107 {
108 // create the BE_USER object
109 $this->bootstrap->initializeBackendUser(CommandLineUserAuthentication::class);
110 // log-in the _cli_ user, create the record if it does not exist
111 /** @var CommandLineUserAuthentication $backendUser */
112 $backendUser = $GLOBALS['BE_USER'];
113 $backendUser->authenticate();
114 $this->bootstrap
115 ->initializeLanguageObject();
116 }
117
118 /**
119 * This request handler can handle any CLI request
120 *
121 * @param InputInterface $input
122 * @return bool Always TRUE
123 */
124 public function canHandleRequest(InputInterface $input)
125 {
126 $this->populateAvailableCommands();
127 return true;
128 }
129
130 /**
131 * Returns the priority - how eager the handler is to actually handle the request.
132 *
133 * @return int The priority of the request handler.
134 */
135 public function getPriority()
136 {
137 return 50;
138 }
139
140 /**
141 *
142 * @param InputInterface $input
143 * @return bool|Command
144 */
145 protected function getCommandToRun(InputInterface $input)
146 {
147 $firstArgument = $input->getFirstArgument();
148 try {
149 return $this->application->find($firstArgument);
150 } catch (\InvalidArgumentException $e) {
151 return false;
152 }
153 }
154
155 /**
156 * put all available commands inside the application
157 */
158 protected function populateAvailableCommands()
159 {
160 $this->availableCommands = $this->getAvailableCommands();
161 foreach ($this->availableCommands as $name => $data) {
162 /** @var Command $cmd */
163 $cmd = GeneralUtility::makeInstance($data['class'], $name);
164 $this->application->add($cmd);
165 $this->availableCommands[$name]['command'] = $cmd;
166 }
167 }
168
169 /**
170 * Fetches all commands registered via Commands.php of all active packages
171 *
172 * @return array
173 */
174 protected function getAvailableCommands()
175 {
176 /** @var PackageManager $packageManager */
177 $packageManager = Bootstrap::getInstance()->getEarlyInstance(PackageManager::class);
178 $availableCommands = [];
179
180 foreach ($packageManager->getActivePackages() as $package) {
181 $commandsOfExtension = $package->getPackagePath() . 'Configuration/Commands.php';
182 if (@is_file($commandsOfExtension)) {
183 $commands = require_once $commandsOfExtension;
184 if (is_array($commands)) {
185 $availableCommands = array_merge($availableCommands, $commands);
186 }
187 }
188 }
189
190 return $availableCommands;
191 }
192 }