composer.json in the wrong place. Gone now.
[yaffs-website] / vendor / consolidation / annotated-command / src / CommandProcessor.php
1 <?php
2 namespace Consolidation\AnnotatedCommand;
3
4 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ReplaceCommandHookDispatcher;
5 use Psr\Log\LoggerAwareInterface;
6 use Psr\Log\LoggerAwareTrait;
7 use Symfony\Component\Console\Input\InputInterface;
8 use Symfony\Component\Console\Output\OutputInterface;
9 use Symfony\Component\Console\Output\ConsoleOutputInterface;
10
11 use Consolidation\OutputFormatters\FormatterManager;
12 use Consolidation\OutputFormatters\Options\FormatterOptions;
13 use Consolidation\AnnotatedCommand\Hooks\HookManager;
14 use Consolidation\AnnotatedCommand\Options\PrepareFormatter;
15
16 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher;
17 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\OptionsHookDispatcher;
18 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InteractHookDispatcher;
19 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ValidateHookDispatcher;
20 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ProcessResultHookDispatcher;
21 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\StatusDeterminerHookDispatcher;
22 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ExtracterHookDispatcher;
23
24 /**
25  * Process a command, including hooks and other callbacks.
26  * There should only be one command processor per application.
27  * Provide your command processor to the AnnotatedCommandFactory
28  * via AnnotatedCommandFactory::setCommandProcessor().
29  */
30 class CommandProcessor implements LoggerAwareInterface
31 {
32     use LoggerAwareTrait;
33
34     /** var HookManager */
35     protected $hookManager;
36     /** var FormatterManager */
37     protected $formatterManager;
38     /** var callable */
39     protected $displayErrorFunction;
40     /** var PrepareFormatterOptions[] */
41     protected $prepareOptionsList = [];
42
43     public function __construct(HookManager $hookManager)
44     {
45         $this->hookManager = $hookManager;
46     }
47
48     /**
49      * Return the hook manager
50      * @return HookManager
51      */
52     public function hookManager()
53     {
54         return $this->hookManager;
55     }
56
57     public function addPrepareFormatter(PrepareFormatter $preparer)
58     {
59         $this->prepareOptionsList[] = $preparer;
60     }
61
62     public function setFormatterManager(FormatterManager $formatterManager)
63     {
64         $this->formatterManager = $formatterManager;
65         return $this;
66     }
67
68     public function setDisplayErrorFunction(callable $fn)
69     {
70         $this->displayErrorFunction = $fn;
71         return $this;
72     }
73
74     /**
75      * Return the formatter manager
76      * @return FormatterManager
77      */
78     public function formatterManager()
79     {
80         return $this->formatterManager;
81     }
82
83     public function initializeHook(
84         InputInterface $input,
85         $names,
86         AnnotationData $annotationData
87     ) {
88         $initializeDispatcher = new InitializeHookDispatcher($this->hookManager(), $names);
89         return $initializeDispatcher->initialize($input, $annotationData);
90     }
91
92     public function optionsHook(
93         AnnotatedCommand $command,
94         $names,
95         AnnotationData $annotationData
96     ) {
97         $optionsDispatcher = new OptionsHookDispatcher($this->hookManager(), $names);
98         $optionsDispatcher->getOptions($command, $annotationData);
99     }
100
101     public function interact(
102         InputInterface $input,
103         OutputInterface $output,
104         $names,
105         AnnotationData $annotationData
106     ) {
107         $interactDispatcher = new InteractHookDispatcher($this->hookManager(), $names);
108         return $interactDispatcher->interact($input, $output, $annotationData);
109     }
110
111     public function process(
112         OutputInterface $output,
113         $names,
114         $commandCallback,
115         CommandData $commandData
116     ) {
117         $result = [];
118         try {
119             $result = $this->validateRunAndAlter(
120                 $names,
121                 $commandCallback,
122                 $commandData
123             );
124             return $this->handleResults($output, $names, $result, $commandData);
125         } catch (\Exception $e) {
126             $result = new CommandError($e->getMessage(), $e->getCode());
127             return $this->handleResults($output, $names, $result, $commandData);
128         }
129     }
130
131     public function validateRunAndAlter(
132         $names,
133         $commandCallback,
134         CommandData $commandData
135     ) {
136         // Validators return any object to signal a validation error;
137         // if the return an array, it replaces the arguments.
138         $validateDispatcher = new ValidateHookDispatcher($this->hookManager(), $names);
139         $validated = $validateDispatcher->validate($commandData);
140         if (is_object($validated)) {
141             return $validated;
142         }
143
144         $replaceDispatcher = new ReplaceCommandHookDispatcher($this->hookManager(), $names);
145         if ($this->logger) {
146             $replaceDispatcher->setLogger($this->logger);
147         }
148         if ($replaceDispatcher->hasReplaceCommandHook()) {
149             $commandCallback = $replaceDispatcher->getReplacementCommand($commandData);
150         }
151
152         // Run the command, alter the results, and then handle output and status
153         $result = $this->runCommandCallback($commandCallback, $commandData);
154         return $this->processResults($names, $result, $commandData);
155     }
156
157     public function processResults($names, $result, CommandData $commandData)
158     {
159         $processDispatcher = new ProcessResultHookDispatcher($this->hookManager(), $names);
160         return $processDispatcher->process($result, $commandData);
161     }
162
163     /**
164      * Handle the result output and status code calculation.
165      */
166     public function handleResults(OutputInterface $output, $names, $result, CommandData $commandData)
167     {
168         $statusCodeDispatcher = new StatusDeterminerHookDispatcher($this->hookManager(), $names);
169         $status = $statusCodeDispatcher->determineStatusCode($result);
170         // If the result is an integer and no separate status code was provided, then use the result as the status and do no output.
171         if (is_integer($result) && !isset($status)) {
172             return $result;
173         }
174         $status = $this->interpretStatusCode($status);
175
176         // Get the structured output, the output stream and the formatter
177         $extractDispatcher = new ExtracterHookDispatcher($this->hookManager(), $names);
178         $structuredOutput = $extractDispatcher->extractOutput($result);
179         $output = $this->chooseOutputStream($output, $status);
180         if ($status != 0) {
181             return $this->writeErrorMessage($output, $status, $structuredOutput, $result);
182         }
183         if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) {
184             return $this->writeUsingFormatter($output, $structuredOutput, $commandData);
185         }
186         return $this->writeCommandOutput($output, $structuredOutput);
187     }
188
189     protected function dataCanBeFormatted($structuredOutput)
190     {
191         if (!isset($this->formatterManager)) {
192             return false;
193         }
194         return
195             is_object($structuredOutput) ||
196             is_array($structuredOutput);
197     }
198
199     /**
200      * Run the main command callback
201      */
202     protected function runCommandCallback($commandCallback, CommandData $commandData)
203     {
204         $result = false;
205         try {
206             $args = $commandData->getArgsAndOptions();
207             $result = call_user_func_array($commandCallback, $args);
208         } catch (\Exception $e) {
209             $result = new CommandError($e->getMessage(), $e->getCode());
210         }
211         return $result;
212     }
213
214     /**
215      * Determine the formatter that should be used to render
216      * output.
217      *
218      * If the user specified a format via the --format option,
219      * then always return that.  Otherwise, return the default
220      * format, unless --pipe was specified, in which case
221      * return the default pipe format, format-pipe.
222      *
223      * n.b. --pipe is a handy option introduced in Drush 2
224      * (or perhaps even Drush 1) that indicates that the command
225      * should select the output format that is most appropriate
226      * for use in scripts (e.g. to pipe to another command).
227      *
228      * @return string
229      */
230     protected function getFormat(FormatterOptions $options)
231     {
232         // In Symfony Console, there is no way for us to differentiate
233         // between the user specifying '--format=table', and the user
234         // not specifying --format when the default value is 'table'.
235         // Therefore, we must make --field always override --format; it
236         // cannot become the default value for --format.
237         if ($options->get('field')) {
238             return 'string';
239         }
240         $defaults = [];
241         if ($options->get('pipe')) {
242             return $options->get('pipe-format', [], 'tsv');
243         }
244         return $options->getFormat($defaults);
245     }
246
247     /**
248      * Determine whether we should use stdout or stderr.
249      */
250     protected function chooseOutputStream(OutputInterface $output, $status)
251     {
252         // If the status code indicates an error, then print the
253         // result to stderr rather than stdout
254         if ($status && ($output instanceof ConsoleOutputInterface)) {
255             return $output->getErrorOutput();
256         }
257         return $output;
258     }
259
260     /**
261      * Call the formatter to output the provided data.
262      */
263     protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, CommandData $commandData)
264     {
265         $formatterOptions = $this->createFormatterOptions($commandData);
266         $format = $this->getFormat($formatterOptions);
267         $this->formatterManager->write(
268             $output,
269             $format,
270             $structuredOutput,
271             $formatterOptions
272         );
273         return 0;
274     }
275
276     /**
277      * Create a FormatterOptions object for use in writing the formatted output.
278      * @param CommandData $commandData
279      * @return FormatterOptions
280      */
281     protected function createFormatterOptions($commandData)
282     {
283         $options = $commandData->input()->getOptions();
284         $formatterOptions = new FormatterOptions($commandData->annotationData()->getArrayCopy(), $options);
285         foreach ($this->prepareOptionsList as $preparer) {
286             $preparer->prepare($commandData, $formatterOptions);
287         }
288         return $formatterOptions;
289     }
290
291     /**
292      * Description
293      * @param OutputInterface $output
294      * @param int $status
295      * @param string $structuredOutput
296      * @param mixed $originalResult
297      * @return type
298      */
299     protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
300     {
301         if (isset($this->displayErrorFunction)) {
302             call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
303         } else {
304             $this->writeCommandOutput($output, $structuredOutput);
305         }
306         return $status;
307     }
308
309     /**
310      * If the result object is a string, then print it.
311      */
312     protected function writeCommandOutput(
313         OutputInterface $output,
314         $structuredOutput
315     ) {
316         // If there is no formatter, we will print strings,
317         // but can do no more than that.
318         if (is_string($structuredOutput)) {
319             $output->writeln($structuredOutput);
320         }
321         return 0;
322     }
323
324     /**
325      * If a status code was set, then return it; otherwise,
326      * presume success.
327      */
328     protected function interpretStatusCode($status)
329     {
330         if (isset($status)) {
331             return $status;
332         }
333         return 0;
334     }
335 }