[TASK] Deprecate Extbase CommandControllers and @cli annotation
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Mvc / Cli / Command.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Mvc\Cli;
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\Extbase\Reflection\ClassSchema;
18
19 /**
20 * Represents a Command
21 *
22 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
23 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use symfony/console commands instead.
24 */
25 class Command
26 {
27 /**
28 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
29 */
30 protected $objectManager;
31
32 /**
33 * @var string
34 */
35 protected $controllerClassName;
36
37 /**
38 * @var string
39 */
40 protected $controllerCommandName;
41
42 /**
43 * @var string
44 */
45 protected $commandIdentifier;
46
47 /**
48 * Name of the extension to which this command belongs
49 *
50 * @var string
51 */
52 protected $extensionName;
53
54 /**
55 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
56 */
57 protected $reflectionService;
58
59 /**
60 * @var ClassSchema
61 */
62 protected $classSchema;
63
64 /**
65 * @var string
66 */
67 protected $controllerCommandMethod;
68
69 /**
70 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
71 */
72 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
73 {
74 $this->objectManager = $objectManager;
75 }
76
77 /**
78 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
79 */
80 public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
81 {
82 $this->reflectionService = $reflectionService;
83 }
84
85 /**
86 * Constructor
87 *
88 * @param string $controllerClassName Class name of the controller providing the command
89 * @param string $controllerCommandName Command name, i.e. the method name of the command, without the "Command" suffix
90 * @throws \InvalidArgumentException
91 */
92 public function __construct($controllerClassName, $controllerCommandName)
93 {
94 $this->controllerClassName = $controllerClassName;
95 $this->controllerCommandName = $controllerCommandName;
96 $this->controllerCommandMethod = $this->controllerCommandName . 'Command';
97 $classNameParts = explode('\\', $controllerClassName);
98 if (isset($classNameParts[0]) && $classNameParts[0] === 'TYPO3' && isset($classNameParts[1]) && $classNameParts[1] === 'CMS') {
99 $classNameParts[0] .= '\\' . $classNameParts[1];
100 unset($classNameParts[1]);
101 $classNameParts = array_values($classNameParts);
102 }
103 $numberOfClassNameParts = count($classNameParts);
104 if ($numberOfClassNameParts < 3) {
105 throw new \InvalidArgumentException(
106 'Controller class names must at least consist of three parts: vendor, extension name and path.',
107 1438782187
108 );
109 }
110 if (strpos($classNameParts[$numberOfClassNameParts - 1], 'CommandController') === false) {
111 throw new \InvalidArgumentException(
112 'Invalid controller class name "' . $controllerClassName . '". Class name must end with "CommandController".',
113 1305100019
114 );
115 }
116
117 $this->extensionName = $classNameParts[1];
118 $extensionKey = \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($this->extensionName);
119 $this->commandIdentifier = strtolower($extensionKey . ':' . substr($classNameParts[$numberOfClassNameParts - 1], 0, -17) . ':' . $controllerCommandName);
120 }
121
122 public function initializeObject()
123 {
124 $this->classSchema = $this->reflectionService->getClassSchema($this->controllerClassName);
125 }
126
127 /**
128 * @return string
129 */
130 public function getControllerClassName()
131 {
132 return $this->controllerClassName;
133 }
134
135 /**
136 * @return string
137 */
138 public function getControllerCommandName()
139 {
140 return $this->controllerCommandName;
141 }
142
143 /**
144 * Returns the command identifier for this command
145 *
146 * @return string The command identifier for this command, following the pattern extensionname:controllername:commandname
147 */
148 public function getCommandIdentifier()
149 {
150 return $this->commandIdentifier;
151 }
152
153 /**
154 * Returns the name of the extension to which this command belongs
155 *
156 * @return string
157 */
158 public function getExtensionName()
159 {
160 return $this->extensionName;
161 }
162
163 /**
164 * Returns a short description of this command
165 *
166 * @return string A short description
167 */
168 public function getShortDescription()
169 {
170 $lines = explode(LF, $this->classSchema->getMethod($this->controllerCommandMethod)['description']);
171 return !empty($lines) ? trim($lines[0]) : '<no description available>';
172 }
173
174 /**
175 * Returns a longer description of this command
176 * This is the complete method description except for the first line which can be retrieved via getShortDescription()
177 * If The command description only consists of one line, an empty string is returned
178 *
179 * @return string A longer description of this command
180 */
181 public function getDescription()
182 {
183 $lines = explode(LF, $this->classSchema->getMethod($this->controllerCommandMethod)['description']);
184 array_shift($lines);
185 $descriptionLines = [];
186 foreach ($lines as $line) {
187 $trimmedLine = trim($line);
188 if ($descriptionLines !== [] || $trimmedLine !== '') {
189 $descriptionLines[] = $trimmedLine;
190 }
191 }
192 return implode(LF, $descriptionLines);
193 }
194
195 /**
196 * Returns TRUE if this command expects required and/or optional arguments, otherwise FALSE
197 *
198 * @return bool
199 */
200 public function hasArguments()
201 {
202 return !empty($this->classSchema->getMethod($this->controllerCommandMethod)['params']);
203 }
204
205 /**
206 * Returns an array of \TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition that contains
207 * information about required/optional arguments of this command.
208 * If the command does not expect any arguments, an empty array is returned
209 *
210 * @return array<\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition>
211 */
212 public function getArgumentDefinitions()
213 {
214 if (!$this->hasArguments()) {
215 return [];
216 }
217 $commandArgumentDefinitions = [];
218 $commandParameters = $this->classSchema->getMethod($this->controllerCommandMethod)['params'];
219 $commandParameterTags = $this->classSchema->getMethod($this->controllerCommandMethod)['tags']['param'];
220 $i = 0;
221 foreach ($commandParameters as $commandParameterName => $commandParameterDefinition) {
222 $explodedAnnotation = preg_split('/\s+/', $commandParameterTags[$i], 3);
223 $description = !empty($explodedAnnotation[2]) ? $explodedAnnotation[2] : '';
224 $required = $commandParameterDefinition['optional'] !== true;
225 $commandArgumentDefinitions[] = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class, $commandParameterName, $required, $description);
226 $i++;
227 }
228 return $commandArgumentDefinitions;
229 }
230
231 /**
232 * Tells if this command is internal and thus should not be exposed through help texts, user documentation etc.
233 * Internall commands are still accessible through the regular command line interface, but should not be used
234 * by users.
235 *
236 * @return bool
237 */
238 public function isInternal()
239 {
240 return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['internal']);
241 }
242
243 /**
244 * Tells if this command is meant to be used on CLI only.
245 *
246 * @return bool
247 */
248 public function isCliOnly()
249 {
250 return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['cli']);
251 }
252
253 /**
254 * Tells if this command flushes all caches and thus needs special attention in the interactive shell.
255 *
256 * Note that neither this method nor the @flushesCaches annotation is currently part of the official API.
257 *
258 * @return bool
259 */
260 public function isFlushingCaches()
261 {
262 return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['flushesCaches']);
263 }
264
265 /**
266 * Returns an array of command identifiers which were specified in the "@see"
267 * annotation of a command method.
268 *
269 * @return array
270 */
271 public function getRelatedCommandIdentifiers()
272 {
273 if (!isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['see'])) {
274 return [];
275 }
276 $relatedCommandIdentifiers = [];
277 foreach ($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['see'] as $tagValue) {
278 if (preg_match('/^[\\w\\d\\.]+:[\\w\\d]+:[\\w\\d]+$/', $tagValue) === 1) {
279 $relatedCommandIdentifiers[] = $tagValue;
280 }
281 }
282 return $relatedCommandIdentifiers;
283 }
284 }