Mercurial > hg > isophonics-drupal-site
view vendor/consolidation/annotated-command/src/AnnotatedCommand.php @ 17:129ea1e6d783
Update, including to Drupal core 8.6.10
author | Chris Cannam |
---|---|
date | Thu, 28 Feb 2019 13:21:36 +0000 |
parents | 4c8ae668cc8c |
children |
line wrap: on
line source
<?php namespace Consolidation\AnnotatedCommand; use Consolidation\AnnotatedCommand\Hooks\HookManager; use Consolidation\AnnotatedCommand\Parser\CommandInfo; use Consolidation\AnnotatedCommand\Help\HelpDocumentAlter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Consolidation\AnnotatedCommand\Help\HelpDocumentBuilder; /** * AnnotatedCommands are created automatically by the * AnnotatedCommandFactory. Each command method in a * command file will produce one AnnotatedCommand. These * are then added to your Symfony Console Application object; * nothing else is needed. * * Optionally, though, you may extend AnnotatedCommand directly * to make a single command. The usage pattern is the same * as for any other Symfony Console command, except that you may * omit the 'Confiure' method, and instead place your annotations * on the execute() method. * * @package Consolidation\AnnotatedCommand */ class AnnotatedCommand extends Command implements HelpDocumentAlter { protected $commandCallback; protected $commandProcessor; protected $annotationData; protected $examples = []; protected $topics = []; protected $returnType; protected $injectedClasses = []; public function __construct($name = null) { $commandInfo = false; // If this is a subclass of AnnotatedCommand, check to see // if the 'execute' method is annotated. We could do this // unconditionally; it is a performance optimization to skip // checking the annotations if $this is an instance of // AnnotatedCommand. Alternately, we break out a new subclass. // The command factory instantiates the subclass. if (get_class($this) != 'Consolidation\AnnotatedCommand\AnnotatedCommand') { $commandInfo = CommandInfo::create($this, 'execute'); if (!isset($name)) { $name = $commandInfo->getName(); } } parent::__construct($name); if ($commandInfo && $commandInfo->hasAnnotation('command')) { $this->setCommandInfo($commandInfo); $this->setCommandOptions($commandInfo); } } public function setCommandCallback($commandCallback) { $this->commandCallback = $commandCallback; return $this; } public function setCommandProcessor($commandProcessor) { $this->commandProcessor = $commandProcessor; return $this; } public function commandProcessor() { // If someone is using an AnnotatedCommand, and is NOT getting // it from an AnnotatedCommandFactory OR not correctly injecting // a command processor via setCommandProcessor() (ideally via the // DI container), then we'll just give each annotated command its // own command processor. This is not ideal; preferably, there would // only be one instance of the command processor in the application. if (!isset($this->commandProcessor)) { $this->commandProcessor = new CommandProcessor(new HookManager()); } return $this->commandProcessor; } public function getReturnType() { return $this->returnType; } public function setReturnType($returnType) { $this->returnType = $returnType; return $this; } public function getAnnotationData() { return $this->annotationData; } public function setAnnotationData($annotationData) { $this->annotationData = $annotationData; return $this; } public function getTopics() { return $this->topics; } public function setTopics($topics) { $this->topics = $topics; return $this; } public function setCommandInfo($commandInfo) { $this->setDescription($commandInfo->getDescription()); $this->setHelp($commandInfo->getHelp()); $this->setAliases($commandInfo->getAliases()); $this->setAnnotationData($commandInfo->getAnnotations()); $this->setTopics($commandInfo->getTopics()); foreach ($commandInfo->getExampleUsages() as $usage => $description) { $this->addUsageOrExample($usage, $description); } $this->setCommandArguments($commandInfo); $this->setReturnType($commandInfo->getReturnType()); // Hidden commands available since Symfony 3.2 // http://symfony.com/doc/current/console/hide_commands.html if (method_exists($this, 'setHidden')) { $this->setHidden($commandInfo->getHidden()); } return $this; } public function getExampleUsages() { return $this->examples; } protected function addUsageOrExample($usage, $description) { $this->addUsage($usage); if (!empty($description)) { $this->examples[$usage] = $description; } } public function helpAlter(\DomDocument $originalDom) { return HelpDocumentBuilder::alter($originalDom, $this); } protected function setCommandArguments($commandInfo) { $this->injectedClasses = $commandInfo->getInjectedClasses(); $this->setCommandArgumentsFromParameters($commandInfo); return $this; } protected function setCommandArgumentsFromParameters($commandInfo) { $args = $commandInfo->arguments()->getValues(); foreach ($args as $name => $defaultValue) { $description = $commandInfo->arguments()->getDescription($name); $hasDefault = $commandInfo->arguments()->hasDefault($name); $parameterMode = $this->getCommandArgumentMode($hasDefault, $defaultValue); $this->addArgument($name, $parameterMode, $description, $defaultValue); } return $this; } protected function getCommandArgumentMode($hasDefault, $defaultValue) { if (!$hasDefault) { return InputArgument::REQUIRED; } if (is_array($defaultValue)) { return InputArgument::IS_ARRAY; } return InputArgument::OPTIONAL; } public function setCommandOptions($commandInfo, $automaticOptions = []) { $inputOptions = $commandInfo->inputOptions(); $this->addOptions($inputOptions + $automaticOptions, $automaticOptions); return $this; } public function addOptions($inputOptions, $automaticOptions = []) { foreach ($inputOptions as $name => $inputOption) { $description = $inputOption->getDescription(); if (empty($description) && isset($automaticOptions[$name])) { $description = $automaticOptions[$name]->getDescription(); $inputOption = static::inputOptionSetDescription($inputOption, $description); } $this->getDefinition()->addOption($inputOption); } } protected static function inputOptionSetDescription($inputOption, $description) { // Recover the 'mode' value, because Symfony is stubborn $mode = 0; if ($inputOption->isValueRequired()) { $mode |= InputOption::VALUE_REQUIRED; } if ($inputOption->isValueOptional()) { $mode |= InputOption::VALUE_OPTIONAL; } if ($inputOption->isArray()) { $mode |= InputOption::VALUE_IS_ARRAY; } if (!$mode) { $mode = InputOption::VALUE_NONE; } $inputOption = new InputOption( $inputOption->getName(), $inputOption->getShortcut(), $mode, $description, $inputOption->getDefault() ); return $inputOption; } /** * Returns all of the hook names that may be called for this command. * * @return array */ public function getNames() { return HookManager::getNames($this, $this->commandCallback); } /** * Add any options to this command that are defined by hook implementations */ public function optionsHook() { $this->commandProcessor()->optionsHook( $this, $this->getNames(), $this->annotationData ); } public function optionsHookForHookAnnotations($commandInfoList) { foreach ($commandInfoList as $commandInfo) { $inputOptions = $commandInfo->inputOptions(); $this->addOptions($inputOptions); foreach ($commandInfo->getExampleUsages() as $usage => $description) { if (!in_array($usage, $this->getUsages())) { $this->addUsageOrExample($usage, $description); } } } } /** * {@inheritdoc} */ protected function interact(InputInterface $input, OutputInterface $output) { $this->commandProcessor()->interact( $input, $output, $this->getNames(), $this->annotationData ); } protected function initialize(InputInterface $input, OutputInterface $output) { // Allow the hook manager a chance to provide configuration values, // if there are any registered hooks to do that. $this->commandProcessor()->initializeHook($input, $this->getNames(), $this->annotationData); } /** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { // Validate, run, process, alter, handle results. return $this->commandProcessor()->process( $output, $this->getNames(), $this->commandCallback, $this->createCommandData($input, $output) ); } /** * This function is available for use by a class that may * wish to extend this class rather than use annotations to * define commands. Using this technique does allow for the * use of annotations to define hooks. */ public function processResults(InputInterface $input, OutputInterface $output, $results) { $commandData = $this->createCommandData($input, $output); $commandProcessor = $this->commandProcessor(); $names = $this->getNames(); $results = $commandProcessor->processResults( $names, $results, $commandData ); return $commandProcessor->handleResults( $output, $names, $results, $commandData ); } protected function createCommandData(InputInterface $input, OutputInterface $output) { $commandData = new CommandData( $this->annotationData, $input, $output ); // Fetch any classes (e.g. InputInterface / OutputInterface) that // this command's callback wants passed as a parameter and inject // it into the command data. $this->commandProcessor()->injectIntoCommandData($commandData, $this->injectedClasses); // Allow the commandData to cache the list of options with // special default values ('null' and 'true'), as these will // need special handling. @see CommandData::options(). $commandData->cacheSpecialDefaults($this->getDefinition()); return $commandData; } }