public function setUp() { $this->commandController = $this->getAccessibleMock(\TYPO3\Flow\Cli\CommandController::class, array('resolveCommandMethodName', 'callCommandMethod')); $this->mockCommandManager = $this->getMockBuilder(CommandManager::class)->disableOriginalConstructor()->getMock(); $this->mockCommandManager->expects($this->any())->method('getCommandMethodParameters')->will($this->returnValue(array())); $this->inject($this->commandController, 'commandManager', $this->mockCommandManager); $this->mockConsoleOutput = $this->getMockBuilder(\TYPO3\Flow\Cli\ConsoleOutput::class)->disableOriginalConstructor()->getMock(); $this->inject($this->commandController, 'output', $this->mockConsoleOutput); }
/** * @test */ public function ifCommandCantBeResolvedTheHelpScreenIsShown() { // The following call is only made to satisfy PHPUnit. For some weird reason PHPUnit complains that the // mocked method ("getObjectNameByClassName") does not exist _if the mock object is not used_. $this->mockObjectManager->getObjectNameByClassName('Acme\\Test\\Command\\DefaultCommandController'); $this->mockCommandManager->getCommandByIdentifier('acme.test:default:list'); $mockCommandManager = $this->getMock(\TYPO3\Flow\Cli\CommandManager::class); $mockCommandManager->expects($this->any())->method('getCommandByIdentifier')->with('test:default:list')->will($this->throwException(new \TYPO3\Flow\Mvc\Exception\NoSuchCommandException())); $this->requestBuilder->injectCommandManager($mockCommandManager); $request = $this->requestBuilder->build('test:default:list'); $this->assertSame(\TYPO3\Flow\Command\HelpCommandController::class, $request->getControllerObjectName()); }
/** * Builds a CLI request object from a command line. * * The given command line may be a string (e.g. "mypackage:foo do-that-thing --force") or * an array consisting of the individual parts. The array must not include the script * name (like in $argv) but start with command right away. * * @param mixed $commandLine The command line, either as a string or as an array * @return \TYPO3\Flow\Cli\Request The CLI request as an object */ public function build($commandLine) { $request = new Request(); $request->setControllerObjectName('TYPO3\\Flow\\Command\\HelpCommandController'); $rawCommandLineArguments = is_array($commandLine) ? $commandLine : explode(' ', $commandLine); if (count($rawCommandLineArguments) === 0) { $request->setControllerCommandName('helpStub'); return $request; } $commandIdentifier = trim(array_shift($rawCommandLineArguments)); try { $command = $this->commandManager->getCommandByIdentifier($commandIdentifier); } catch (\TYPO3\Flow\Mvc\Exception\CommandException $exception) { $request->setArgument('exception', $exception); $request->setControllerCommandName('error'); return $request; } $controllerObjectName = $this->objectManager->getObjectNameByClassName($command->getControllerClassName()); $controllerCommandName = $command->getControllerCommandName(); $request->setControllerObjectName($controllerObjectName); $request->setControllerCommandName($controllerCommandName); list($commandLineArguments, $exceedingCommandLineArguments) = $this->parseRawCommandLineArguments($rawCommandLineArguments, $controllerObjectName, $controllerCommandName); $request->setArguments($commandLineArguments); $request->setExceedingArguments($exceedingCommandLineArguments); return $request; }
/** * @test * @dataProvider quotedValues */ public function quotedArgumentValuesAreCorrectlyParsedWhenPassingTheCommandAsString($quotedArgument, $expectedResult) { $methodParameters = array('requiredArgument1' => array('optional' => false, 'type' => 'string'), 'requiredArgument2' => array('optional' => false, 'type' => 'string')); $this->mockCommandManager->expects($this->once())->method('getCommandMethodParameters')->with('Acme\\Test\\Command\\DefaultCommandController', 'listCommand')->will($this->returnValue($methodParameters)); $expectedArguments = array('requiredArgument1' => 'firstArgumentValue', 'requiredArgument2' => $expectedResult); $request = $this->requestBuilder->build('acme.test:default:list firstArgumentValue ' . $quotedArgument); $this->assertEquals($expectedArguments, $request->getArguments()); }
/** * Takes an array of unparsed command line arguments and options and converts it separated * by named arguments, options and unnamed arguments. * * @param array $rawCommandLineArguments The unparsed command parts (such as "--foo") as an array * @param string $controllerObjectName Object name of the designated command controller * @param string $controllerCommandName Command name of the recognized command (ie. method name without "Command" suffix) * @return array All and exceeding command line arguments * @throws InvalidArgumentMixingException */ protected function parseRawCommandLineArguments(array $rawCommandLineArguments, $controllerObjectName, $controllerCommandName) { $commandLineArguments = []; $exceedingArguments = []; $commandMethodName = $controllerCommandName . 'Command'; $commandMethodParameters = $this->commandManager->getCommandMethodParameters($controllerObjectName, $commandMethodName); $requiredArguments = []; $optionalArguments = []; $argumentNames = []; foreach ($commandMethodParameters as $parameterName => $parameterInfo) { $argumentNames[] = $parameterName; if ($parameterInfo['optional'] === false) { $requiredArguments[strtolower($parameterName)] = ['parameterName' => $parameterName, 'type' => $parameterInfo['type']]; } else { $optionalArguments[strtolower($parameterName)] = ['parameterName' => $parameterName, 'type' => $parameterInfo['type']]; } } $decidedToUseNamedArguments = false; $decidedToUseUnnamedArguments = false; $argumentIndex = 0; while (count($rawCommandLineArguments) > 0) { $rawArgument = array_shift($rawCommandLineArguments); if ($rawArgument !== '' && $rawArgument[0] === '-') { if ($rawArgument[1] === '-') { $rawArgument = substr($rawArgument, 2); } else { $rawArgument = substr($rawArgument, 1); } $argumentName = $this->extractArgumentNameFromCommandLinePart($rawArgument); if (isset($optionalArguments[$argumentName])) { $argumentValue = $this->getValueOfCurrentCommandLineOption($rawArgument, $rawCommandLineArguments, $optionalArguments[$argumentName]['type']); $commandLineArguments[$optionalArguments[$argumentName]['parameterName']] = $argumentValue; } elseif (isset($requiredArguments[$argumentName])) { if ($decidedToUseUnnamedArguments) { throw new InvalidArgumentMixingException(sprintf('Unexpected named argument "%s". If you use unnamed arguments, all required arguments must be passed without a name.', $argumentName), 1309971821); } $decidedToUseNamedArguments = true; $argumentValue = $this->getValueOfCurrentCommandLineOption($rawArgument, $rawCommandLineArguments, $requiredArguments[$argumentName]['type']); $commandLineArguments[$requiredArguments[$argumentName]['parameterName']] = $argumentValue; unset($requiredArguments[$argumentName]); } } else { if (count($requiredArguments) > 0) { if ($decidedToUseNamedArguments) { throw new InvalidArgumentMixingException(sprintf('Unexpected unnamed argument "%s". If you use named arguments, all required arguments must be passed named.', $rawArgument), 1309971820); } $argument = array_shift($requiredArguments); $commandLineArguments[$argument['parameterName']] = $rawArgument; $decidedToUseUnnamedArguments = true; } else { $exceedingArguments[] = $rawArgument; } } $argumentIndex++; } return [$commandLineArguments, $exceedingArguments]; }
/** * @test */ public function getShortestIdentifierForCommandReturnsCompleteCommandIdentifierForCommandsWithTheSameControllerAndCommandName() { $mockCommand1 = $this->getMock(\TYPO3\Flow\Cli\Command::class, array(), array(), '', false); $mockCommand1->expects($this->atLeastOnce())->method('getCommandIdentifier')->will($this->returnValue('package.key:controller:command')); $mockCommand2 = $this->getMock(\TYPO3\Flow\Cli\Command::class, array(), array(), '', false); $mockCommand2->expects($this->atLeastOnce())->method('getCommandIdentifier')->will($this->returnValue('otherpackage.key:controller:command')); $mockCommands = array($mockCommand1, $mockCommand2); $this->commandManager->expects($this->atLeastOnce())->method('getAvailableCommands')->will($this->returnValue($mockCommands)); $this->assertSame('package.key:controller:command', $this->commandManager->getShortestIdentifierForCommand($mockCommand1)); $this->assertSame('otherpackage.key:controller:command', $this->commandManager->getShortestIdentifierForCommand($mockCommand2)); }
/** * Initializes the arguments array of this controller by creating an empty argument object for each of the * method arguments found in the designated command method. * * @return void * @throws InvalidArgumentTypeException */ protected function initializeCommandMethodArguments() { $this->arguments->removeAll(); $methodParameters = $this->commandManager->getCommandMethodParameters(get_class($this), $this->commandMethodName); foreach ($methodParameters as $parameterName => $parameterInfo) { $dataType = null; if (isset($parameterInfo['type'])) { $dataType = $parameterInfo['type']; } elseif ($parameterInfo['array']) { $dataType = 'array'; } if ($dataType === null) { 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); } }
/** * Builds a CLI request object from a command line. * * The given command line may be a string (e.g. "mypackage:foo do-that-thing --force") or * an array consisting of the individual parts. The array must not include the script * name (like in $argv) but start with command right away. * * @param mixed $commandLine The command line, either as a string or as an array * @return \TYPO3\Flow\Cli\Request The CLI request as an object * @throws \TYPO3\Flow\Mvc\Exception\InvalidArgumentNameException */ public function build($commandLine) { $request = new Request(); $request->setControllerObjectName(\TYPO3\Flow\Command\HelpCommandController::class); if (is_array($commandLine) === TRUE) { $rawCommandLineArguments = $commandLine; } else { preg_match_all(self::ARGUMENT_MATCHING_EXPRESSION, $commandLine, $commandLineMatchings, PREG_SET_ORDER); $rawCommandLineArguments = array(); foreach ($commandLineMatchings as $match) { if (isset($match['NoQuotes'])) { $rawCommandLineArguments[] = str_replace(array('\\ ', '\\"', "\\'", '\\\\'), array(' ', '"', "'", '\\'), $match['NoQuotes']); } elseif (isset($match['DoubleQuotes'])) { $rawCommandLineArguments[] = str_replace('\\"', '"', $match['DoubleQuotes']); } elseif (isset($match['SingleQuotes'])) { $rawCommandLineArguments[] = str_replace('\\\'', '\'', $match['SingleQuotes']); } else { throw new InvalidArgumentNameException(sprintf('Could not parse the command line "%s" - specifically the part "%s".', $commandLine, $match[0])); } } } if (count($rawCommandLineArguments) === 0) { $request->setControllerCommandName('helpStub'); return $request; } $commandIdentifier = trim(array_shift($rawCommandLineArguments)); try { $command = $this->commandManager->getCommandByIdentifier($commandIdentifier); } catch (\TYPO3\Flow\Mvc\Exception\CommandException $exception) { $request->setArgument('exception', $exception); $request->setControllerCommandName('error'); return $request; } $controllerObjectName = $this->objectManager->getObjectNameByClassName($command->getControllerClassName()); $controllerCommandName = $command->getControllerCommandName(); $request->setControllerObjectName($controllerObjectName); $request->setControllerCommandName($controllerCommandName); list($commandLineArguments, $exceedingCommandLineArguments) = $this->parseRawCommandLineArguments($rawCommandLineArguments, $controllerObjectName, $controllerCommandName); $request->setArguments($commandLineArguments); $request->setExceedingArguments($exceedingCommandLineArguments); return $request; }
/** * Render help text for a single command * * @param Command $command * @return void */ protected function displayHelpForCommand(Command $command) { $this->outputLine(); $this->outputLine('<u>' . $command->getShortDescription() . '</u>'); $this->outputLine(); $this->outputLine('<b>COMMAND:</b>'); $name = '<i>' . $command->getCommandIdentifier() . '</i>'; $this->outputLine('%-2s%s', array(' ', $name)); $commandArgumentDefinitions = $command->getArgumentDefinitions(); $usage = ''; $hasOptions = false; /** @var CommandArgumentDefinition $commandArgumentDefinition */ foreach ($commandArgumentDefinitions as $commandArgumentDefinition) { if (!$commandArgumentDefinition->isRequired()) { $hasOptions = true; } else { $usage .= sprintf(' <%s>', strtolower(preg_replace('/([A-Z])/', ' $1', $commandArgumentDefinition->getName()))); } } $usage = $this->commandManager->getShortestIdentifierForCommand($command) . ($hasOptions ? ' [<options>]' : '') . $usage; $this->outputLine(); $this->outputLine('<b>USAGE:</b>'); $this->outputLine(' %s %s', array($this->getFlowInvocationString(), $usage)); $argumentDescriptions = array(); $optionDescriptions = array(); if ($command->hasArguments()) { foreach ($commandArgumentDefinitions as $commandArgumentDefinition) { $argumentDescription = $commandArgumentDefinition->getDescription(); $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 { $optionDescriptions[] = vsprintf(' %-20s %s', array($commandArgumentDefinition->getDashedName(), $argumentDescription)); } } } if (count($argumentDescriptions) > 0) { $this->outputLine(); $this->outputLine('<b>ARGUMENTS:</b>'); foreach ($argumentDescriptions as $argumentDescription) { $this->outputLine($argumentDescription); } } if (count($optionDescriptions) > 0) { $this->outputLine(); $this->outputLine('<b>OPTIONS:</b>'); foreach ($optionDescriptions as $optionDescription) { $this->outputLine($optionDescription); } } if ($command->getDescription() !== '') { $this->outputLine(); $this->outputLine('<b>DESCRIPTION:</b>'); $descriptionLines = explode(chr(10), $command->getDescription()); foreach ($descriptionLines as $descriptionLine) { $this->outputLine('%-2s%s', array(' ', $descriptionLine)); } } $relatedCommandIdentifiers = $command->getRelatedCommandIdentifiers(); if ($relatedCommandIdentifiers !== array()) { $this->outputLine(); $this->outputLine('<b>SEE ALSO:</b>'); foreach ($relatedCommandIdentifiers as $commandIdentifier) { try { $command = $this->commandManager->getCommandByIdentifier($commandIdentifier); $this->outputLine('%-2s%s (%s)', array(' ', $commandIdentifier, $command->getShortDescription())); } catch (CommandException $exception) { $this->outputLine('%-2s%s (%s)', array(' ', $commandIdentifier, '<i>Command not available</i>')); } } } $this->outputLine(); }