From be4ba978e73f3073d389fb9987b0fc97eafc173f Mon Sep 17 00:00:00 2001 From: Wouter Wolters Date: Mon, 16 Jun 2014 14:32:12 +0200 Subject: [PATCH] [!!!][FEATURE] Integrate Symfony/Console into CommandController This is a backport from the new introduced feature in Flow https://review.typo3.org/#/c/30653/ with upstream patches. This extends the base ``CommandController`` by some convenience helpers from the ``symfony/console`` package: easy output coloring through "Warning!" TableHelper to render values to a grid ProgressHelper to render and advance and progress bars DialogHelper with numerous types of questions like: select, ask, confirm, askHidden, etc Additionally this change improves the ``mapRequestArgumentsToControllerArguments()`` method to ask for missing required arguments instead of quitting with an exception. You can make use of the new features by calling the introduced ConsoleOutput object with its respective methods: outputTable() select() ask() askConfirmation() askHiddenResponse() askAndValidate() askHiddenResponseAndValidate() progressStart() progressSet() progressAdvance() progressFinish() This change does not alter the public API so it is not breaking in the strict sense. But it introduces a new behavior: Previously all outputs where collected in the ``Cli\Response`` and only rendered to the console at the end of a CLI request. Now all methods producing output (inluding ``output()`` and ``outputLine()``) render the result directly to the console. If you use ``$this->response`` directly or let the command method return a string, the rendering is still deferred until the end of the CLI request. Resolves: #59606 Releases: master Change-Id: I33e051f698f5cc1e204f609734280bbed69610c9 Reviewed-on: http://review.typo3.org/30743 Tested-by: Wouter Wolters Reviewed-by: Helmut Hummel Tested-by: Helmut Hummel Reviewed-by: Anja Leichsenring Tested-by: Anja Leichsenring --- composer.json | 1 + ...ateSymfonyConsoleIntoCommandController.rst | 93 ++++++ .../Classes/Command/HelpCommandController.php | 6 +- .../extbase/Classes/Mvc/Cli/ConsoleOutput.php | 307 ++++++++++++++++++ .../Mvc/Controller/CommandController.php | 101 ++++-- .../Mvc/Controller/CommandControllerTest.php | 25 +- .../Command/LanguageCommandController.php | 4 + 7 files changed, 489 insertions(+), 48 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-59606-IntegrateSymfonyConsoleIntoCommandController.rst create mode 100644 typo3/sysext/extbase/Classes/Mvc/Cli/ConsoleOutput.php diff --git a/composer.json b/composer.json index dd5a1258dc89..ed3ead12684b 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,7 @@ "pear/http_request2": "2.2.1", "phpwhois/idna-convert": "0.8.2", "swiftmailer/swiftmailer": "5.2.1", + "symfony/console": "2.5.*", "helhum/class-alias-loader": "~1.1", "typo3/cms-composer-installers": "1.1.*@dev" }, diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-59606-IntegrateSymfonyConsoleIntoCommandController.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-59606-IntegrateSymfonyConsoleIntoCommandController.rst new file mode 100644 index 000000000000..03e1e70e93ad --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-59606-IntegrateSymfonyConsoleIntoCommandController.rst @@ -0,0 +1,93 @@ +================================================================== +Feature: #59606 - Integrate Symfony/Console into CommandController +================================================================== + +Description +=========== + +The CommandController now makes use of Symfony/Console internally and +provides various methods directly from the CommandController's ``output`` member: + +* TableHelper + + * outputTable($rows, $headers = NULL) + +* DialogHelper + + * select($question, $choices, $default = NULL, $multiSelect = false, $attempts = FALSE) + * ask($question, $default = NULL, array $autocomplete = array()) + * askConfirmation($question, $default = TRUE) + * askHiddenResponse($question, $fallback = TRUE) + * askAndValidate($question, $validator, $attempts = FALSE, $default = NULL, array $autocomplete = NULL) + * askHiddenResponseAndValidate($question, $validator, $attempts = FALSE, $fallback = TRUE) + +* ProgressHelper + + * progressStart($max = NULL) + * progressSet($current) + * progressAdvance($step = 1) + * progressFinish() + +Here's an example showing of some of those functions: + +.. code-block:: php + + namespace Acme\Demo\Command; + + use TYPO3\CMS\Extbase\Mvc\Controller\CommandController; + + /** + * My command + */ + class MyCommandController extends CommandController { + + /** + * @return string + */ + public function myCommand() { + // render a table + $this->output->outputTable(array( + array('Bob', 34, 'm'), + array('Sally', 21, 'f'), + array('Blake', 56, 'm') + ), + array('Name', 'Age', 'Gender')); + + // select + $colors = array('red', 'blue', 'yellow'); + $selectedColorIndex = $this->output->select('Please select one color', $colors, 'red'); + $this->outputLine('You choose the color %s.', array($colors[$selectedColorIndex])); + + // ask + $name = $this->output->ask('What is your name?' . PHP_EOL, 'Bob', array('Bob', 'Sally', 'Blake')); + $this->outputLine('Hello %s.', array($name)); + + // prompt + $likesDogs = $this->output->askConfirmation('Do you like dogs?'); + if ($likesDogs) { + $this->outputLine('You do like dogs!'); + } + + // progress + $this->output->progressStart(600); + for ($i = 0; $i < 300; $i ++) { + $this->output->progressAdvance(); + usleep(5000); + } + $this->output->progressFinish(); + + } + } + + +Impact +====== + +This change does not alter the public API so it is not breaking +in the strict sense. But it introduces a new behavior: +Previously all outputs where collected in the ``Cli\Response`` +and only rendered to the console at the end of a CLI request. +Now all methods producing output (inluding ``output()`` and +``outputLine()``) render the result directly to the console.If you use ``$this->response`` directly or let the command method +return a string, the rendering is still deferred until the end of +the CLI request. \ No newline at end of file diff --git a/typo3/sysext/extbase/Classes/Command/HelpCommandController.php b/typo3/sysext/extbase/Classes/Command/HelpCommandController.php index 49478051dd85..66eaf1778403 100644 --- a/typo3/sysext/extbase/Classes/Command/HelpCommandController.php +++ b/typo3/sysext/extbase/Classes/Command/HelpCommandController.php @@ -84,10 +84,10 @@ protected function displayHelpIndex() { foreach ($this->commandsByExtensionsAndControllers as $extensionKey => $commandControllers) { $this->outputLine(''); $this->outputLine('EXTENSION "%s":', array(strtoupper($extensionKey))); - $this->outputLine(str_repeat('-', self::MAXIMUM_LINE_LENGTH)); + $this->outputLine(str_repeat('-', $this->output->getMaximumLineLength())); foreach ($commandControllers as $commands) { foreach ($commands as $command) { - $description = wordwrap($command->getShortDescription(), self::MAXIMUM_LINE_LENGTH - 43, PHP_EOL . str_repeat(' ', 43), TRUE); + $description = wordwrap($command->getShortDescription(), $this->output->getMaximumLineLength() - 43, PHP_EOL . str_repeat(' ', 43), TRUE); $shortCommandIdentifier = $this->commandManager->getShortestIdentifierForCommand($command); $this->outputLine('%-2s%-40s %s', array(' ', $shortCommandIdentifier, $description)); } @@ -129,7 +129,7 @@ protected function displayHelpForCommand(\TYPO3\CMS\Extbase\Mvc\Cli\Command $com if ($command->hasArguments()) { foreach ($commandArgumentDefinitions as $commandArgumentDefinition) { $argumentDescription = $commandArgumentDefinition->getDescription(); - $argumentDescription = wordwrap($argumentDescription, self::MAXIMUM_LINE_LENGTH - 23, PHP_EOL . str_repeat(' ', 23), TRUE); + $argumentDescription = wordwrap($argumentDescription, $this->output->getMaximumLineLength() - 23, PHP_EOL . str_repeat(' ', 23), TRUE); if ($commandArgumentDefinition->isRequired()) { $argumentDescriptions[] = vsprintf(' %-20s %s', array($commandArgumentDefinition->getDashedName(), $argumentDescription)); } else { diff --git a/typo3/sysext/extbase/Classes/Mvc/Cli/ConsoleOutput.php b/typo3/sysext/extbase/Classes/Mvc/Cli/ConsoleOutput.php new file mode 100644 index 000000000000..d02f123588d7 --- /dev/null +++ b/typo3/sysext/extbase/Classes/Mvc/Cli/ConsoleOutput.php @@ -0,0 +1,307 @@ +output = new SymfonyConsoleOutput(); + $this->output->getFormatter()->setStyle('b', new OutputFormatterStyle(NULL, NULL, array('bold'))); + $this->output->getFormatter()->setStyle('i', new OutputFormatterStyle('black', 'white')); + $this->output->getFormatter()->setStyle('u', new OutputFormatterStyle(NULL, NULL, array('underscore'))); + $this->output->getFormatter()->setStyle('em', new OutputFormatterStyle(NULL, NULL, array('reverse'))); + $this->output->getFormatter()->setStyle('strike', new OutputFormatterStyle(NULL, NULL, array('conceal'))); + } + + /** + * Returns the desired maximum line length for console output. + * + * @return int + */ + public function getMaximumLineLength() { + return 79; + } + + /** + * Outputs specified text to the console window + * You can specify arguments that will be passed to the text via sprintf + * @see http://www.php.net/sprintf + * + * @param string $text Text to output + * @param array $arguments Optional arguments to use for sprintf + * @return void + */ + public function output($text, array $arguments = array()) { + if ($arguments !== array()) { + $text = vsprintf($text, $arguments); + } + $this->output->write($text); + } + + /** + * Outputs specified text to the console window and appends a line break + * + * @param string $text Text to output + * @param array $arguments Optional arguments to use for sprintf + * @return void + * @see output() + * @see outputLines() + */ + public function outputLine($text = '', array $arguments = array()) { + $this->output($text . PHP_EOL, $arguments); + } + + /** + * Formats the given text to fit into the maximum line length and outputs it to the + * console window + * + * @param string $text Text to output + * @param array $arguments Optional arguments to use for sprintf + * @param int $leftPadding The number of spaces to use for indentation + * @return void + * @see outputLine() + */ + public function outputFormatted($text = '', array $arguments = array(), $leftPadding = 0) { + $lines = explode(PHP_EOL, $text); + foreach ($lines as $line) { + $formattedText = str_repeat(' ', $leftPadding) . wordwrap($line, $this->getMaximumLineLength() - $leftPadding, PHP_EOL . str_repeat(' ', $leftPadding), TRUE); + $this->outputLine($formattedText, $arguments); + } + } + + /** + * Renders a table like output of the given $rows + * + * @param array $rows + * @param array $headers + */ + public function outputTable($rows, $headers = NULL) { + $tableHelper = $this->getTableHelper(); + if ($headers !== NULL) { + $tableHelper->setHeaders($headers); + } + $tableHelper->setRows($rows); + $tableHelper->render($this->output); + } + + /** + * Asks the user to select a value + * + * @param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question + * @param array $choices List of choices to pick from + * @param bool $default The default answer if the user enters nothing + * @param bool $multiSelect If TRUE the result will be an array with the selected options. Multiple options can be given separated by commas + * @param bool|int $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @return int|string|array The selected value or values (the key of the choices array) + * @throws \InvalidArgumentException + */ + public function select($question, $choices, $default = NULL, $multiSelect = FALSE, $attempts = FALSE) { + return $this->getDialogHelper()->select($this->output, $question, $choices, $default, $attempts, 'Value "%s" is invalid', $multiSelect); + } + + /** + * Asks a question to the user + * + * @param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete. This only works if "stty" is installed + * @return string The user answer + * @throws \RuntimeException If there is no data to read in the input stream + */ + public function ask($question, $default = NULL, array $autocomplete = NULL) { + return $this->getDialogHelper()->ask($this->output, $question, $default, $autocomplete); + } + + /** + * Asks a confirmation to the user. + * + * The question will be asked until the user answers by nothing, yes, or no. + * + * @param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question + * @param bool $default The default answer if the user enters nothing + * @return bool true if the user has confirmed, false otherwise + */ + public function askConfirmation($question, $default = TRUE) { + return $this->getDialogHelper()->askConfirmation($this->output, $question, $default); + } + + /** + * Asks a question to the user, the response is hidden + * + * @param string|array $question The question. If an array each array item is turned into one line of a multi-line question + * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * @return string The answer + * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponse($question, $fallback = TRUE) { + return $this->getDialogHelper()->askHiddenResponse($this->output, $question, $fallback); + } + + /** + * Asks for a value and validates the response + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question + * @param callable $validator A PHP callback that gets a value and is expected to return the (transformed) value or throw an exception if it wasn't valid + * @param int|bool $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param string $default The default answer if none is given by the user + * @param array $autocomplete List of values to autocomplete. This only works if "stty" is installed + * @return mixed + * @throws \Exception When any of the validators return an error + */ + public function askAndValidate($question, $validator, $attempts = FALSE, $default = NULL, array $autocomplete = NULL) { + return $this->getDialogHelper()->askAndValidate($this->output, $question, $validator, $attempts, $default, $autocomplete); + } + + /** + * Asks for a value, hide and validates the response + * + * The validator receives the data to validate. It must return the + * validated data when the data is valid and throw an exception + * otherwise. + * + * @param string|array $question The question to ask. If an array each array item is turned into one line of a multi-line question + * @param callable $validator A PHP callback that gets a value and is expected to return the (transformed) value or throw an exception if it wasn't valid + * @param int|bool $attempts Max number of times to ask before giving up (false by default, which means infinite) + * @param bool $fallback In case the response can not be hidden, whether to fallback on non-hidden question or not + * @return string The response + * @throws \Exception When any of the validators return an error + * @throws \RuntimeException In case the fallback is deactivated and the response can not be hidden + */ + public function askHiddenResponseAndValidate($question, $validator, $attempts = FALSE, $fallback = TRUE) { + return $this->getDialogHelper()->askHiddenResponseAndValidate($this->output, $question, $validator, $attempts, $fallback); + } + + /** + * Starts the progress output + * + * @param int $max Maximum steps. If NULL an indeterminate progress bar is rendered + * @return void + */ + public function progressStart($max = NULL) { + $this->getProgressHelper()->start($this->output, $max); + } + + /** + * Advances the progress output X steps + * + * @param int $step Number of steps to advance + * @param bool $redraw Whether to redraw or not + * @return void + * @throws \LogicException + */ + public function progressAdvance($step = 1, $redraw = false) { + $this->getProgressHelper()->advance($step, $redraw); + } + + /** + * Sets the current progress + * + * @param int $current The current progress + * @param bool $redraw Whether to redraw or not + * @return void + * @throws \LogicException + */ + public function progressSet($current, $redraw = false) { + $this->getProgressHelper()->setCurrent($current, $redraw); + } + + /** + * Finishes the progress output + * + * @return void + */ + public function progressFinish() { + $this->getProgressHelper()->finish(); + } + + /** + * Returns or initializes the symfony/console DialogHelper + * + * @return DialogHelper + */ + protected function getDialogHelper() { + if ($this->dialogHelper === NULL) { + $this->dialogHelper = new DialogHelper(); + $helperSet = new HelperSet(array(new FormatterHelper())); + $this->dialogHelper->setHelperSet($helperSet); + } + return $this->dialogHelper; + } + + /** + * Returns or initializes the symfony/console ProgressHelper + * + * @return ProgressHelper + */ + protected function getProgressHelper() { + if ($this->progressHelper === NULL) { + $this->progressHelper = new ProgressHelper(); + } + return $this->progressHelper; + } + + /** + * Returns or initializes the symfony/console TableHelper + * + * @return TableHelper + */ + protected function getTableHelper() { + if ($this->tableHelper === NULL) { + $this->tableHelper = new TableHelper(); + } + return $this->tableHelper; + } + +} \ No newline at end of file diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/CommandController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/CommandController.php index 1c5824871c6c..0fb18d9d4a92 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Controller/CommandController.php +++ b/typo3/sysext/extbase/Classes/Mvc/Controller/CommandController.php @@ -14,6 +14,21 @@ * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Extbase\Mvc\Cli\ConsoleOutput; +use TYPO3\CMS\Extbase\Mvc\Cli\Request; +use TYPO3\CMS\Extbase\Mvc\Cli\Response; +use Symfony\Component\Console\Output\ConsoleOutput as SymfonyConsoleOutput; +use Symfony\Component\Console\Helper\DialogHelper; +use Symfony\Component\Console\Helper\FormatterHelper; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\ProgressHelper; +use Symfony\Component\Console\Helper\TableHelper; +use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException; +use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException; +use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; +use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException; +use TYPO3\CMS\Extbase\Mvc\RequestInterface; +use TYPO3\CMS\Extbase\Mvc\ResponseInterface; use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication; use TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition; @@ -24,20 +39,18 @@ */ class CommandController implements CommandControllerInterface { - const MAXIMUM_LINE_LENGTH = 79; - /** - * @var \TYPO3\CMS\Extbase\Mvc\Cli\Request + * @var Request */ protected $request; /** - * @var \TYPO3\CMS\Extbase\Mvc\Cli\Response + * @var Response */ protected $response; /** - * @var \TYPO3\CMS\Extbase\Mvc\Controller\Arguments + * @var Arguments */ protected $arguments; @@ -72,6 +85,11 @@ class CommandController implements CommandControllerInterface { */ protected $objectManager; + /** + * @var \TYPO3\CMS\Extbase\Mvc\Cli\ConsoleOutput + */ + protected $output; + /** * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager * @return void @@ -80,6 +98,7 @@ public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInter $this->objectManager = $objectManager; $this->arguments = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments::class); $this->userAuthentication = isset($GLOBALS['BE_USER']) ? $GLOBALS['BE_USER'] : NULL; + $this->output = $this->objectManager->get(ConsoleOutput::class); } /** @@ -89,25 +108,27 @@ public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInter * @return bool TRUE if this request type is supported, otherwise FALSE */ public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request) { - return $request instanceof \TYPO3\CMS\Extbase\Mvc\Cli\Request; + return $request instanceof Request; } /** * Processes a command line request. * - * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object - * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this controller - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException + * @param RequestInterface $request The request object + * @param ResponseInterface $response The response, modified by this handler * @return void + * @throws UnsupportedRequestTypeException if the controller doesn't support the current request type * @api */ - public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response) { + public function processRequest(RequestInterface $request, ResponseInterface $response) { if (!$this->canProcessRequest($request)) { - throw new \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException(get_class($this) . ' does not support requests of type "' . get_class($request) . '".', 1300787096); + throw new UnsupportedRequestTypeException(sprintf('%s only supports command line requests – requests of type "%s" given.', get_class($this), get_class($request)), 1300787096); } + $this->request = $request; $this->request->setDispatched(TRUE); $this->response = $response; + $this->commandMethodName = $this->resolveCommandMethodName(); $this->initializeCommandMethodArguments(); $this->mapRequestArgumentsToControllerArguments(); @@ -122,11 +143,12 @@ public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, * * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException * @return string Method name of the current command + * @throws NoSuchCommandException */ protected function resolveCommandMethodName() { $commandMethodName = $this->request->getControllerCommandName() . 'Command'; if (!is_callable(array($this, $commandMethodName))) { - throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException('A command method "' . $commandMethodName . '()" does not exist in controller "' . get_class($this) . '".', 1300902143); + throw new NoSuchCommandException(sprintf('A command method "%s()" does not exist in controller "%s".', $commandMethodName, get_class($this)), 1300902143); } return $commandMethodName; } @@ -137,9 +159,12 @@ protected function resolveCommandMethodName() { * * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException * @return void + * @throws InvalidArgumentTypeException */ protected function initializeCommandMethodArguments() { + $this->arguments->removeAll(); $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->commandMethodName); + foreach ($methodParameters as $parameterName => $parameterInfo) { $dataType = NULL; if (isset($parameterInfo['type'])) { @@ -148,10 +173,10 @@ protected function initializeCommandMethodArguments() { $dataType = 'array'; } if ($dataType === NULL) { - throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException('The argument type for parameter $' . $parameterName . ' of method ' . get_class($this) . '->' . $this->commandMethodName . '() could not be detected.', 1306755296); + throw new InvalidArgumentTypeException(sprintf('The argument type for parameter $%s of method %s->%s() could not be detected.', $parameterName, get_class($this), $this->commandMethodName), 1306755296); } - $defaultValue = isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : NULL; - $this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === FALSE, $defaultValue); + $defaultValue = (isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : NULL); + $this->arguments->addNewArgument($parameterName, $dataType, ($parameterInfo['optional'] === FALSE), $defaultValue); } } @@ -166,11 +191,17 @@ protected function mapRequestArgumentsToControllerArguments() { $argumentName = $argument->getName(); if ($this->request->hasArgument($argumentName)) { $argument->setValue($this->request->getArgument($argumentName)); - } elseif ($argument->isRequired()) { - $commandArgumentDefinition = $this->objectManager->get(CommandArgumentDefinition::class, $argumentName, TRUE, NULL); - $exception = new \TYPO3\CMS\Extbase\Mvc\Exception\CommandException('Required argument "' . $commandArgumentDefinition->getDashedName() . '" is not set.', 1306755520); - $this->forward('error', \TYPO3\CMS\Extbase\Command\HelpCommandController::class, array('exception' => $exception)); + continue; + } + if (!$argument->isRequired()) { + continue; + } + $argumentValue = NULL; + $commandArgumentDefinition = $this->objectManager->get(CommandArgumentDefinition::class, $argumentName, TRUE, NULL); + while ($argumentValue === NULL) { + $argumentValue = $this->output->ask(sprintf('Please specify the required argument "%s": ', $commandArgumentDefinition->getDashedName())); } + $argument->setValue($argumentValue); } } @@ -183,8 +214,8 @@ protected function mapRequestArgumentsToControllerArguments() { * @param string $commandName * @param string $controllerObjectName * @param array $arguments - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException * @return void + * @throws StopActionException */ protected function forward($commandName, $controllerObjectName = NULL, array $arguments = array()) { $this->request->setDispatched(FALSE); @@ -193,8 +224,9 @@ protected function forward($commandName, $controllerObjectName = NULL, array $ar $this->request->setControllerObjectName($controllerObjectName); } $this->request->setArguments($arguments); + $this->arguments->removeAll(); - throw new \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException(); + throw new StopActionException(); } /** @@ -258,10 +290,7 @@ protected function restoreUserRole($originalRole) { * @return void */ protected function output($text, array $arguments = array()) { - if ($arguments !== array()) { - $text = vsprintf($text, $arguments); - } - $this->response->appendContent($text); + $this->output->output($text, $arguments); } /** @@ -273,7 +302,21 @@ protected function output($text, array $arguments = array()) { * @see output() */ protected function outputLine($text = '', array $arguments = array()) { - $this->output($text . PHP_EOL, $arguments); + $this->output->outputLine($text, $arguments); + } + + /** + * Formats the given text to fit into MAXIMUM_LINE_LENGTH and outputs it to the + * console window + * + * @param string $text Text to output + * @param array $arguments Optional arguments to use for sprintf + * @param int $leftPadding The number of spaces to use for indentation + * @return void + * @see outputLine() + */ + protected function outputFormatted($text = '', array $arguments = array(), $leftPadding = 0) { + $this->output->outputFormatted($text, $arguments, $leftPadding); } /** @@ -281,12 +324,12 @@ protected function outputLine($text = '', array $arguments = array()) { * An exit status code can be specified @see http://www.php.net/exit * * @param int $exitCode Exit code to return on exit - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException + * @throws StopActionException * @return void */ protected function quit($exitCode = 0) { $this->response->setExitCode($exitCode); - throw new \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException(); + throw new StopActionException; } /** @@ -298,7 +341,7 @@ protected function quit($exitCode = 0) { */ protected function sendAndExit($exitCode = 0) { $this->response->send(); - die($exitCode); + exit($exitCode); } } diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/CommandControllerTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/CommandControllerTest.php index b53499eae62d..00479ca19b3e 100644 --- a/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/CommandControllerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/CommandControllerTest.php @@ -24,17 +24,22 @@ class CommandControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase { */ protected $commandController; + /** + * \Symfony\Component\Console\Output\ConsoleOutput|\PHPUnit_Framework_MockObject_MockObject + */ + protected $mockConsoleOutput; + protected function setUp() { $this->commandController = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Controller\CommandController::class, array('dummyCommand')); + $this->mockConsoleOutput = $this->getMockBuilder(\TYPO3\CMS\Extbase\Mvc\Cli\ConsoleOutput::class)->disableOriginalConstructor()->getMock(); + $this->commandController->_set('output', $this->mockConsoleOutput); } /** * @test */ public function outputAppendsGivenStringToTheResponseContent() { - $mockResponse = $this->getMock(\TYPO3\CMS\Extbase\Mvc\Cli\Response::class); - $mockResponse->expects($this->once())->method('appendContent')->with('some text'); - $this->commandController->_set('response', $mockResponse); + $this->mockConsoleOutput->expects($this->once())->method('output')->with('some text'); $this->commandController->_call('output', 'some text'); } @@ -42,22 +47,10 @@ public function outputAppendsGivenStringToTheResponseContent() { * @test */ public function outputReplacesArgumentsInGivenString() { - $mockResponse = $this->getMock(\TYPO3\CMS\Extbase\Mvc\Cli\Response::class); - $mockResponse->expects($this->once())->method('appendContent')->with('some text'); - $this->commandController->_set('response', $mockResponse); + $this->mockConsoleOutput->expects($this->once())->method('output')->with('%2$s %1$s', array('text', 'some')); $this->commandController->_call('output', '%2$s %1$s', array('text', 'some')); } - /** - * @test - */ - public function outputLineAppendsGivenStringAndNewlineToTheResponseContent() { - $mockResponse = $this->getMock(\TYPO3\CMS\Extbase\Mvc\Cli\Response::class); - $mockResponse->expects($this->once())->method('appendContent')->with('some text' . PHP_EOL); - $this->commandController->_set('response', $mockResponse); - $this->commandController->_call('outputLine', 'some text'); - } - /** * @test * @expectedException \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException diff --git a/typo3/sysext/lang/Classes/Command/LanguageCommandController.php b/typo3/sysext/lang/Classes/Command/LanguageCommandController.php index d2c9e9843112..d42f32b0076c 100644 --- a/typo3/sysext/lang/Classes/Command/LanguageCommandController.php +++ b/typo3/sysext/lang/Classes/Command/LanguageCommandController.php @@ -59,6 +59,8 @@ public function updateCommand($localesToUpdate = '') { $packageManager = $this->objectManager->get(\TYPO3\CMS\Core\Package\PackageManager::class); $this->emitPackagesMayHaveChangedSignal(); $packages = $packageManager->getAvailablePackages(); + $this->outputLine((sprintf('Updating language packs of all activated extensions for locales "%s"', implode(', ', $locales)))); + $this->output->progressStart(count($locales)*count($packages)); foreach ($locales as $locale) { /** @var PackageInterface $package */ foreach ($packages as $package) { @@ -67,8 +69,10 @@ public function updateCommand($localesToUpdate = '') { if (empty($result[$extensionKey][$locale]['error'])) { $this->registryService->set($locale, $GLOBALS['EXEC_TIME']); } + $this->output->progressAdvance(); } } + $this->output->progressFinish(); } /**