Version 1
[yaffs-website] / vendor / consolidation / annotated-command / src / CommandProcessor.php
diff --git a/vendor/consolidation/annotated-command/src/CommandProcessor.php b/vendor/consolidation/annotated-command/src/CommandProcessor.php
new file mode 100644 (file)
index 0000000..9610b4a
--- /dev/null
@@ -0,0 +1,335 @@
+<?php
+namespace Consolidation\AnnotatedCommand;
+
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ReplaceCommandHookDispatcher;
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerAwareTrait;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Output\ConsoleOutputInterface;
+
+use Consolidation\OutputFormatters\FormatterManager;
+use Consolidation\OutputFormatters\Options\FormatterOptions;
+use Consolidation\AnnotatedCommand\Hooks\HookManager;
+use Consolidation\AnnotatedCommand\Options\PrepareFormatter;
+
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher;
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\OptionsHookDispatcher;
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InteractHookDispatcher;
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ValidateHookDispatcher;
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ProcessResultHookDispatcher;
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\StatusDeterminerHookDispatcher;
+use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ExtracterHookDispatcher;
+
+/**
+ * Process a command, including hooks and other callbacks.
+ * There should only be one command processor per application.
+ * Provide your command processor to the AnnotatedCommandFactory
+ * via AnnotatedCommandFactory::setCommandProcessor().
+ */
+class CommandProcessor implements LoggerAwareInterface
+{
+    use LoggerAwareTrait;
+
+    /** var HookManager */
+    protected $hookManager;
+    /** var FormatterManager */
+    protected $formatterManager;
+    /** var callable */
+    protected $displayErrorFunction;
+    /** var PrepareFormatterOptions[] */
+    protected $prepareOptionsList = [];
+
+    public function __construct(HookManager $hookManager)
+    {
+        $this->hookManager = $hookManager;
+    }
+
+    /**
+     * Return the hook manager
+     * @return HookManager
+     */
+    public function hookManager()
+    {
+        return $this->hookManager;
+    }
+
+    public function addPrepareFormatter(PrepareFormatter $preparer)
+    {
+        $this->prepareOptionsList[] = $preparer;
+    }
+
+    public function setFormatterManager(FormatterManager $formatterManager)
+    {
+        $this->formatterManager = $formatterManager;
+        return $this;
+    }
+
+    public function setDisplayErrorFunction(callable $fn)
+    {
+        $this->displayErrorFunction = $fn;
+        return $this;
+    }
+
+    /**
+     * Return the formatter manager
+     * @return FormatterManager
+     */
+    public function formatterManager()
+    {
+        return $this->formatterManager;
+    }
+
+    public function initializeHook(
+        InputInterface $input,
+        $names,
+        AnnotationData $annotationData
+    ) {
+        $initializeDispatcher = new InitializeHookDispatcher($this->hookManager(), $names);
+        return $initializeDispatcher->initialize($input, $annotationData);
+    }
+
+    public function optionsHook(
+        AnnotatedCommand $command,
+        $names,
+        AnnotationData $annotationData
+    ) {
+        $optionsDispatcher = new OptionsHookDispatcher($this->hookManager(), $names);
+        $optionsDispatcher->getOptions($command, $annotationData);
+    }
+
+    public function interact(
+        InputInterface $input,
+        OutputInterface $output,
+        $names,
+        AnnotationData $annotationData
+    ) {
+        $interactDispatcher = new InteractHookDispatcher($this->hookManager(), $names);
+        return $interactDispatcher->interact($input, $output, $annotationData);
+    }
+
+    public function process(
+        OutputInterface $output,
+        $names,
+        $commandCallback,
+        CommandData $commandData
+    ) {
+        $result = [];
+        try {
+            $result = $this->validateRunAndAlter(
+                $names,
+                $commandCallback,
+                $commandData
+            );
+            return $this->handleResults($output, $names, $result, $commandData);
+        } catch (\Exception $e) {
+            $result = new CommandError($e->getMessage(), $e->getCode());
+            return $this->handleResults($output, $names, $result, $commandData);
+        }
+    }
+
+    public function validateRunAndAlter(
+        $names,
+        $commandCallback,
+        CommandData $commandData
+    ) {
+        // Validators return any object to signal a validation error;
+        // if the return an array, it replaces the arguments.
+        $validateDispatcher = new ValidateHookDispatcher($this->hookManager(), $names);
+        $validated = $validateDispatcher->validate($commandData);
+        if (is_object($validated)) {
+            return $validated;
+        }
+
+        $replaceDispatcher = new ReplaceCommandHookDispatcher($this->hookManager(), $names);
+        if ($this->logger) {
+            $replaceDispatcher->setLogger($this->logger);
+        }
+        if ($replaceDispatcher->hasReplaceCommandHook()) {
+            $commandCallback = $replaceDispatcher->getReplacementCommand($commandData);
+        }
+
+        // Run the command, alter the results, and then handle output and status
+        $result = $this->runCommandCallback($commandCallback, $commandData);
+        return $this->processResults($names, $result, $commandData);
+    }
+
+    public function processResults($names, $result, CommandData $commandData)
+    {
+        $processDispatcher = new ProcessResultHookDispatcher($this->hookManager(), $names);
+        return $processDispatcher->process($result, $commandData);
+    }
+
+    /**
+     * Handle the result output and status code calculation.
+     */
+    public function handleResults(OutputInterface $output, $names, $result, CommandData $commandData)
+    {
+        $statusCodeDispatcher = new StatusDeterminerHookDispatcher($this->hookManager(), $names);
+        $status = $statusCodeDispatcher->determineStatusCode($result);
+        // If the result is an integer and no separate status code was provided, then use the result as the status and do no output.
+        if (is_integer($result) && !isset($status)) {
+            return $result;
+        }
+        $status = $this->interpretStatusCode($status);
+
+        // Get the structured output, the output stream and the formatter
+        $extractDispatcher = new ExtracterHookDispatcher($this->hookManager(), $names);
+        $structuredOutput = $extractDispatcher->extractOutput($result);
+        $output = $this->chooseOutputStream($output, $status);
+        if ($status != 0) {
+            return $this->writeErrorMessage($output, $status, $structuredOutput, $result);
+        }
+        if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) {
+            return $this->writeUsingFormatter($output, $structuredOutput, $commandData);
+        }
+        return $this->writeCommandOutput($output, $structuredOutput);
+    }
+
+    protected function dataCanBeFormatted($structuredOutput)
+    {
+        if (!isset($this->formatterManager)) {
+            return false;
+        }
+        return
+            is_object($structuredOutput) ||
+            is_array($structuredOutput);
+    }
+
+    /**
+     * Run the main command callback
+     */
+    protected function runCommandCallback($commandCallback, CommandData $commandData)
+    {
+        $result = false;
+        try {
+            $args = $commandData->getArgsAndOptions();
+            $result = call_user_func_array($commandCallback, $args);
+        } catch (\Exception $e) {
+            $result = new CommandError($e->getMessage(), $e->getCode());
+        }
+        return $result;
+    }
+
+    /**
+     * Determine the formatter that should be used to render
+     * output.
+     *
+     * If the user specified a format via the --format option,
+     * then always return that.  Otherwise, return the default
+     * format, unless --pipe was specified, in which case
+     * return the default pipe format, format-pipe.
+     *
+     * n.b. --pipe is a handy option introduced in Drush 2
+     * (or perhaps even Drush 1) that indicates that the command
+     * should select the output format that is most appropriate
+     * for use in scripts (e.g. to pipe to another command).
+     *
+     * @return string
+     */
+    protected function getFormat(FormatterOptions $options)
+    {
+        // In Symfony Console, there is no way for us to differentiate
+        // between the user specifying '--format=table', and the user
+        // not specifying --format when the default value is 'table'.
+        // Therefore, we must make --field always override --format; it
+        // cannot become the default value for --format.
+        if ($options->get('field')) {
+            return 'string';
+        }
+        $defaults = [];
+        if ($options->get('pipe')) {
+            return $options->get('pipe-format', [], 'tsv');
+        }
+        return $options->getFormat($defaults);
+    }
+
+    /**
+     * Determine whether we should use stdout or stderr.
+     */
+    protected function chooseOutputStream(OutputInterface $output, $status)
+    {
+        // If the status code indicates an error, then print the
+        // result to stderr rather than stdout
+        if ($status && ($output instanceof ConsoleOutputInterface)) {
+            return $output->getErrorOutput();
+        }
+        return $output;
+    }
+
+    /**
+     * Call the formatter to output the provided data.
+     */
+    protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, CommandData $commandData)
+    {
+        $formatterOptions = $this->createFormatterOptions($commandData);
+        $format = $this->getFormat($formatterOptions);
+        $this->formatterManager->write(
+            $output,
+            $format,
+            $structuredOutput,
+            $formatterOptions
+        );
+        return 0;
+    }
+
+    /**
+     * Create a FormatterOptions object for use in writing the formatted output.
+     * @param CommandData $commandData
+     * @return FormatterOptions
+     */
+    protected function createFormatterOptions($commandData)
+    {
+        $options = $commandData->input()->getOptions();
+        $formatterOptions = new FormatterOptions($commandData->annotationData()->getArrayCopy(), $options);
+        foreach ($this->prepareOptionsList as $preparer) {
+            $preparer->prepare($commandData, $formatterOptions);
+        }
+        return $formatterOptions;
+    }
+
+    /**
+     * Description
+     * @param OutputInterface $output
+     * @param int $status
+     * @param string $structuredOutput
+     * @param mixed $originalResult
+     * @return type
+     */
+    protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
+    {
+        if (isset($this->displayErrorFunction)) {
+            call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
+        } else {
+            $this->writeCommandOutput($output, $structuredOutput);
+        }
+        return $status;
+    }
+
+    /**
+     * If the result object is a string, then print it.
+     */
+    protected function writeCommandOutput(
+        OutputInterface $output,
+        $structuredOutput
+    ) {
+        // If there is no formatter, we will print strings,
+        // but can do no more than that.
+        if (is_string($structuredOutput)) {
+            $output->writeln($structuredOutput);
+        }
+        return 0;
+    }
+
+    /**
+     * If a status code was set, then return it; otherwise,
+     * presume success.
+     */
+    protected function interpretStatusCode($status)
+    {
+        if (isset($status)) {
+            return $status;
+        }
+        return 0;
+    }
+}