aa3a51e61fee7cc96a8c90883343e5051276493a
[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 */
24 class Command
25 {
26 /**
27 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
28 */
29 protected $objectManager;
30
31 /**
32 * @var string
33 */
34 protected $controllerClassName;
35
36 /**
37 * @var string
38 */
39 protected $controllerCommandName;
40
41 /**
42 * @var string
43 */
44 protected $commandIdentifier;
45
46 /**
47 * Name of the extension to which this command belongs
48 *
49 * @var string
50 */
51 protected $extensionName;
52
53 /**
54 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
55 */
56 protected $reflectionService;
57
58 /**
59 * @var ClassSchema
60 */
61 protected $classSchema;
62
63 /**
64 * @var string
65 */
66 protected $controllerCommandMethod;
67
68 /**
69 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
70 */
71 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
72 {
73 $this->objectManager = $objectManager;
74 }
75
76 /**
77 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
78 */
79 public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
80 {
81 $this->reflectionService = $reflectionService;
82 }
83
84 /**
85 * Constructor
86 *
87 * @param string $controllerClassName Class name of the controller providing the command
88 * @param string $controllerCommandName Command name, i.e. the method name of the command, without the "Command" suffix
89 * @throws \InvalidArgumentException
90 */
91 public function __construct($controllerClassName, $controllerCommandName)
92 {
93 $this->controllerClassName = $controllerClassName;
94 $this->controllerCommandName = $controllerCommandName;
95 $this->controllerCommandMethod = $this->controllerCommandName . 'Command';
96 $delimiter = strpos($controllerClassName, '\\') !== false ? '\\' : '_';
97 $classNameParts = explode($delimiter, $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 }