Mercurial > hg > isophonics-drupal-site
comparison vendor/consolidation/annotated-command/src/CommandProcessor.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
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 /** var boolean */ | |
43 protected $passExceptions; | |
44 | |
45 public function __construct(HookManager $hookManager) | |
46 { | |
47 $this->hookManager = $hookManager; | |
48 } | |
49 | |
50 /** | |
51 * Return the hook manager | |
52 * @return HookManager | |
53 */ | |
54 public function hookManager() | |
55 { | |
56 return $this->hookManager; | |
57 } | |
58 | |
59 public function addPrepareFormatter(PrepareFormatter $preparer) | |
60 { | |
61 $this->prepareOptionsList[] = $preparer; | |
62 } | |
63 | |
64 public function setFormatterManager(FormatterManager $formatterManager) | |
65 { | |
66 $this->formatterManager = $formatterManager; | |
67 return $this; | |
68 } | |
69 | |
70 public function setDisplayErrorFunction(callable $fn) | |
71 { | |
72 $this->displayErrorFunction = $fn; | |
73 return $this; | |
74 } | |
75 | |
76 /** | |
77 * Set a mode to make the annotated command library re-throw | |
78 * any exception that it catches while processing a command. | |
79 * | |
80 * The default behavior in the current (2.x) branch is to catch | |
81 * the exception and replace it with a CommandError object that | |
82 * may be processed by the normal output processing passthrough. | |
83 * | |
84 * In the 3.x branch, exceptions will never be caught; they will | |
85 * be passed through, as if setPassExceptions(true) were called. | |
86 * This is the recommended behavior. | |
87 */ | |
88 public function setPassExceptions($passExceptions) | |
89 { | |
90 $this->passExceptions = $passExceptions; | |
91 return $this; | |
92 } | |
93 | |
94 public function commandErrorForException(\Exception $e) | |
95 { | |
96 if ($this->passExceptions) { | |
97 throw $e; | |
98 } | |
99 return new CommandError($e->getMessage(), $e->getCode()); | |
100 } | |
101 | |
102 /** | |
103 * Return the formatter manager | |
104 * @return FormatterManager | |
105 */ | |
106 public function formatterManager() | |
107 { | |
108 return $this->formatterManager; | |
109 } | |
110 | |
111 public function initializeHook( | |
112 InputInterface $input, | |
113 $names, | |
114 AnnotationData $annotationData | |
115 ) { | |
116 $initializeDispatcher = new InitializeHookDispatcher($this->hookManager(), $names); | |
117 return $initializeDispatcher->initialize($input, $annotationData); | |
118 } | |
119 | |
120 public function optionsHook( | |
121 AnnotatedCommand $command, | |
122 $names, | |
123 AnnotationData $annotationData | |
124 ) { | |
125 $optionsDispatcher = new OptionsHookDispatcher($this->hookManager(), $names); | |
126 $optionsDispatcher->getOptions($command, $annotationData); | |
127 } | |
128 | |
129 public function interact( | |
130 InputInterface $input, | |
131 OutputInterface $output, | |
132 $names, | |
133 AnnotationData $annotationData | |
134 ) { | |
135 $interactDispatcher = new InteractHookDispatcher($this->hookManager(), $names); | |
136 return $interactDispatcher->interact($input, $output, $annotationData); | |
137 } | |
138 | |
139 public function process( | |
140 OutputInterface $output, | |
141 $names, | |
142 $commandCallback, | |
143 CommandData $commandData | |
144 ) { | |
145 $result = []; | |
146 try { | |
147 $result = $this->validateRunAndAlter( | |
148 $names, | |
149 $commandCallback, | |
150 $commandData | |
151 ); | |
152 return $this->handleResults($output, $names, $result, $commandData); | |
153 } catch (\Exception $e) { | |
154 $result = $this->commandErrorForException($e); | |
155 return $this->handleResults($output, $names, $result, $commandData); | |
156 } | |
157 } | |
158 | |
159 public function validateRunAndAlter( | |
160 $names, | |
161 $commandCallback, | |
162 CommandData $commandData | |
163 ) { | |
164 // Validators return any object to signal a validation error; | |
165 // if the return an array, it replaces the arguments. | |
166 $validateDispatcher = new ValidateHookDispatcher($this->hookManager(), $names); | |
167 $validated = $validateDispatcher->validate($commandData); | |
168 if (is_object($validated)) { | |
169 return $validated; | |
170 } | |
171 | |
172 $replaceDispatcher = new ReplaceCommandHookDispatcher($this->hookManager(), $names); | |
173 if ($this->logger) { | |
174 $replaceDispatcher->setLogger($this->logger); | |
175 } | |
176 if ($replaceDispatcher->hasReplaceCommandHook()) { | |
177 $commandCallback = $replaceDispatcher->getReplacementCommand($commandData); | |
178 } | |
179 | |
180 // Run the command, alter the results, and then handle output and status | |
181 $result = $this->runCommandCallback($commandCallback, $commandData); | |
182 return $this->processResults($names, $result, $commandData); | |
183 } | |
184 | |
185 public function processResults($names, $result, CommandData $commandData) | |
186 { | |
187 $processDispatcher = new ProcessResultHookDispatcher($this->hookManager(), $names); | |
188 return $processDispatcher->process($result, $commandData); | |
189 } | |
190 | |
191 /** | |
192 * Handle the result output and status code calculation. | |
193 */ | |
194 public function handleResults(OutputInterface $output, $names, $result, CommandData $commandData) | |
195 { | |
196 $statusCodeDispatcher = new StatusDeterminerHookDispatcher($this->hookManager(), $names); | |
197 $status = $statusCodeDispatcher->determineStatusCode($result); | |
198 // If the result is an integer and no separate status code was provided, then use the result as the status and do no output. | |
199 if (is_integer($result) && !isset($status)) { | |
200 return $result; | |
201 } | |
202 $status = $this->interpretStatusCode($status); | |
203 | |
204 // Get the structured output, the output stream and the formatter | |
205 $extractDispatcher = new ExtracterHookDispatcher($this->hookManager(), $names); | |
206 $structuredOutput = $extractDispatcher->extractOutput($result); | |
207 $output = $this->chooseOutputStream($output, $status); | |
208 if ($status != 0) { | |
209 return $this->writeErrorMessage($output, $status, $structuredOutput, $result); | |
210 } | |
211 if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) { | |
212 return $this->writeUsingFormatter($output, $structuredOutput, $commandData); | |
213 } | |
214 return $this->writeCommandOutput($output, $structuredOutput); | |
215 } | |
216 | |
217 protected function dataCanBeFormatted($structuredOutput) | |
218 { | |
219 if (!isset($this->formatterManager)) { | |
220 return false; | |
221 } | |
222 return | |
223 is_object($structuredOutput) || | |
224 is_array($structuredOutput); | |
225 } | |
226 | |
227 /** | |
228 * Run the main command callback | |
229 */ | |
230 protected function runCommandCallback($commandCallback, CommandData $commandData) | |
231 { | |
232 $result = false; | |
233 try { | |
234 $args = $commandData->getArgsAndOptions(); | |
235 $result = call_user_func_array($commandCallback, $args); | |
236 } catch (\Exception $e) { | |
237 $result = $this->commandErrorForException($e); | |
238 } | |
239 return $result; | |
240 } | |
241 | |
242 /** | |
243 * Determine the formatter that should be used to render | |
244 * output. | |
245 * | |
246 * If the user specified a format via the --format option, | |
247 * then always return that. Otherwise, return the default | |
248 * format, unless --pipe was specified, in which case | |
249 * return the default pipe format, format-pipe. | |
250 * | |
251 * n.b. --pipe is a handy option introduced in Drush 2 | |
252 * (or perhaps even Drush 1) that indicates that the command | |
253 * should select the output format that is most appropriate | |
254 * for use in scripts (e.g. to pipe to another command). | |
255 * | |
256 * @return string | |
257 */ | |
258 protected function getFormat(FormatterOptions $options) | |
259 { | |
260 // In Symfony Console, there is no way for us to differentiate | |
261 // between the user specifying '--format=table', and the user | |
262 // not specifying --format when the default value is 'table'. | |
263 // Therefore, we must make --field always override --format; it | |
264 // cannot become the default value for --format. | |
265 if ($options->get('field')) { | |
266 return 'string'; | |
267 } | |
268 $defaults = []; | |
269 if ($options->get('pipe')) { | |
270 return $options->get('pipe-format', [], 'tsv'); | |
271 } | |
272 return $options->getFormat($defaults); | |
273 } | |
274 | |
275 /** | |
276 * Determine whether we should use stdout or stderr. | |
277 */ | |
278 protected function chooseOutputStream(OutputInterface $output, $status) | |
279 { | |
280 // If the status code indicates an error, then print the | |
281 // result to stderr rather than stdout | |
282 if ($status && ($output instanceof ConsoleOutputInterface)) { | |
283 return $output->getErrorOutput(); | |
284 } | |
285 return $output; | |
286 } | |
287 | |
288 /** | |
289 * Call the formatter to output the provided data. | |
290 */ | |
291 protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, CommandData $commandData) | |
292 { | |
293 $formatterOptions = $this->createFormatterOptions($commandData); | |
294 $format = $this->getFormat($formatterOptions); | |
295 $this->formatterManager->write( | |
296 $output, | |
297 $format, | |
298 $structuredOutput, | |
299 $formatterOptions | |
300 ); | |
301 return 0; | |
302 } | |
303 | |
304 /** | |
305 * Create a FormatterOptions object for use in writing the formatted output. | |
306 * @param CommandData $commandData | |
307 * @return FormatterOptions | |
308 */ | |
309 protected function createFormatterOptions($commandData) | |
310 { | |
311 $options = $commandData->input()->getOptions(); | |
312 $formatterOptions = new FormatterOptions($commandData->annotationData()->getArrayCopy(), $options); | |
313 foreach ($this->prepareOptionsList as $preparer) { | |
314 $preparer->prepare($commandData, $formatterOptions); | |
315 } | |
316 return $formatterOptions; | |
317 } | |
318 | |
319 /** | |
320 * Description | |
321 * @param OutputInterface $output | |
322 * @param int $status | |
323 * @param string $structuredOutput | |
324 * @param mixed $originalResult | |
325 * @return type | |
326 */ | |
327 protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult) | |
328 { | |
329 if (isset($this->displayErrorFunction)) { | |
330 call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult); | |
331 } else { | |
332 $this->writeCommandOutput($output, $structuredOutput); | |
333 } | |
334 return $status; | |
335 } | |
336 | |
337 /** | |
338 * If the result object is a string, then print it. | |
339 */ | |
340 protected function writeCommandOutput( | |
341 OutputInterface $output, | |
342 $structuredOutput | |
343 ) { | |
344 // If there is no formatter, we will print strings, | |
345 // but can do no more than that. | |
346 if (is_string($structuredOutput)) { | |
347 $output->writeln($structuredOutput); | |
348 } | |
349 return 0; | |
350 } | |
351 | |
352 /** | |
353 * If a status code was set, then return it; otherwise, | |
354 * presume success. | |
355 */ | |
356 protected function interpretStatusCode($status) | |
357 { | |
358 if (isset($status)) { | |
359 return $status; | |
360 } | |
361 return 0; | |
362 } | |
363 } |