1<?php 2namespace 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 17use TYPO3\CMS\Extbase\Reflection\ClassSchema; 18 19/** 20 * Represents a Command 21 * 22 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use symfony/console commands instead. 23 */ 24class 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 $classNameParts = explode('\\', $controllerClassName); 97 if (isset($classNameParts[0]) && $classNameParts[0] === 'TYPO3' && isset($classNameParts[1]) && $classNameParts[1] === 'CMS') { 98 $classNameParts[0] .= '\\' . $classNameParts[1]; 99 unset($classNameParts[1]); 100 $classNameParts = array_values($classNameParts); 101 } 102 $numberOfClassNameParts = count($classNameParts); 103 if ($numberOfClassNameParts < 3) { 104 throw new \InvalidArgumentException( 105 'Controller class names must at least consist of three parts: vendor, extension name and path.', 106 1438782187 107 ); 108 } 109 if (strpos($classNameParts[$numberOfClassNameParts - 1], 'CommandController') === false) { 110 throw new \InvalidArgumentException( 111 'Invalid controller class name "' . $controllerClassName . '". Class name must end with "CommandController".', 112 1305100019 113 ); 114 } 115 116 $this->extensionName = $classNameParts[1]; 117 $extensionKey = \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($this->extensionName); 118 $this->commandIdentifier = strtolower($extensionKey . ':' . substr($classNameParts[$numberOfClassNameParts - 1], 0, -17) . ':' . $controllerCommandName); 119 } 120 121 public function initializeObject() 122 { 123 $this->classSchema = $this->reflectionService->getClassSchema($this->controllerClassName); 124 } 125 126 /** 127 * @return string 128 */ 129 public function getControllerClassName() 130 { 131 return $this->controllerClassName; 132 } 133 134 /** 135 * @return string 136 */ 137 public function getControllerCommandName() 138 { 139 return $this->controllerCommandName; 140 } 141 142 /** 143 * Returns the command identifier for this command 144 * 145 * @return string The command identifier for this command, following the pattern extensionname:controllername:commandname 146 */ 147 public function getCommandIdentifier() 148 { 149 return $this->commandIdentifier; 150 } 151 152 /** 153 * Returns the name of the extension to which this command belongs 154 * 155 * @return string 156 */ 157 public function getExtensionName() 158 { 159 return $this->extensionName; 160 } 161 162 /** 163 * Returns a short description of this command 164 * 165 * @return string A short description 166 */ 167 public function getShortDescription() 168 { 169 $lines = explode(LF, $this->classSchema->getMethod($this->controllerCommandMethod)['description']); 170 return !empty($lines) ? trim($lines[0]) : '<no description available>'; 171 } 172 173 /** 174 * Returns a longer description of this command 175 * This is the complete method description except for the first line which can be retrieved via getShortDescription() 176 * If The command description only consists of one line, an empty string is returned 177 * 178 * @return string A longer description of this command 179 */ 180 public function getDescription() 181 { 182 $lines = explode(LF, $this->classSchema->getMethod($this->controllerCommandMethod)['description']); 183 array_shift($lines); 184 $descriptionLines = []; 185 foreach ($lines as $line) { 186 $trimmedLine = trim($line); 187 if ($descriptionLines !== [] || $trimmedLine !== '') { 188 $descriptionLines[] = $trimmedLine; 189 } 190 } 191 return implode(LF, $descriptionLines); 192 } 193 194 /** 195 * Returns TRUE if this command expects required and/or optional arguments, otherwise FALSE 196 * 197 * @return bool 198 */ 199 public function hasArguments() 200 { 201 return !empty($this->classSchema->getMethod($this->controllerCommandMethod)['params']); 202 } 203 204 /** 205 * Returns an array of \TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition that contains 206 * information about required/optional arguments of this command. 207 * If the command does not expect any arguments, an empty array is returned 208 * 209 * @return array<\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition> 210 */ 211 public function getArgumentDefinitions() 212 { 213 if (!$this->hasArguments()) { 214 return []; 215 } 216 $commandArgumentDefinitions = []; 217 $commandParameters = $this->classSchema->getMethod($this->controllerCommandMethod)['params']; 218 $commandParameterTags = $this->classSchema->getMethod($this->controllerCommandMethod)['tags']['param']; 219 $i = 0; 220 foreach ($commandParameters as $commandParameterName => $commandParameterDefinition) { 221 $explodedAnnotation = preg_split('/\s+/', $commandParameterTags[$i], 3); 222 $description = !empty($explodedAnnotation[2]) ? $explodedAnnotation[2] : ''; 223 $required = $commandParameterDefinition['optional'] !== true; 224 $commandArgumentDefinitions[] = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class, $commandParameterName, $required, $description); 225 $i++; 226 } 227 return $commandArgumentDefinitions; 228 } 229 230 /** 231 * Tells if this command is internal and thus should not be exposed through help texts, user documentation etc. 232 * Internall commands are still accessible through the regular command line interface, but should not be used 233 * by users. 234 * 235 * @return bool 236 */ 237 public function isInternal() 238 { 239 return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['internal']); 240 } 241 242 /** 243 * Tells if this command is meant to be used on CLI only. 244 * 245 * @return bool 246 */ 247 public function isCliOnly() 248 { 249 return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['cli']); 250 } 251 252 /** 253 * Tells if this command flushes all caches and thus needs special attention in the interactive shell. 254 * 255 * Note that neither this method nor the @flushesCaches annotation is currently part of the official API. 256 * 257 * @return bool 258 * 259 * @deprecated will be removed in TYPO3 v10.0. 260 */ 261 public function isFlushingCaches() 262 { 263 trigger_error( 264 'Method isFlushingCaches() will be removed in TYPO3 v10.0. Do not call from other extension.', 265 E_USER_DEPRECATED 266 ); 267 return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['flushesCaches']); 268 } 269 270 /** 271 * Returns an array of command identifiers which were specified in the "@see" 272 * annotation of a command method. 273 * 274 * @return array 275 */ 276 public function getRelatedCommandIdentifiers() 277 { 278 if (!isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['see'])) { 279 return []; 280 } 281 $relatedCommandIdentifiers = []; 282 foreach ($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['see'] as $tagValue) { 283 if (preg_match('/^[\\w\\d\\.]+:[\\w\\d]+:[\\w\\d]+$/', $tagValue) === 1) { 284 $relatedCommandIdentifiers[] = $tagValue; 285 } 286 } 287 return $relatedCommandIdentifiers; 288 } 289} 290