Chris@17
|
1 <?php
|
Chris@17
|
2 namespace Consolidation\AnnotatedCommand;
|
Chris@17
|
3
|
Chris@17
|
4 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ReplaceCommandHookDispatcher;
|
Chris@17
|
5 use Symfony\Component\Console\Input\InputInterface;
|
Chris@17
|
6 use Symfony\Component\Console\Output\OutputInterface;
|
Chris@17
|
7 use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
Chris@17
|
8
|
Chris@17
|
9 use Consolidation\OutputFormatters\FormatterManager;
|
Chris@17
|
10 use Consolidation\OutputFormatters\Options\FormatterOptions;
|
Chris@17
|
11 use Consolidation\AnnotatedCommand\Hooks\HookManager;
|
Chris@17
|
12 use Consolidation\AnnotatedCommand\Options\PrepareFormatter;
|
Chris@17
|
13
|
Chris@17
|
14 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InitializeHookDispatcher;
|
Chris@17
|
15 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\OptionsHookDispatcher;
|
Chris@17
|
16 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\InteractHookDispatcher;
|
Chris@17
|
17 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ValidateHookDispatcher;
|
Chris@17
|
18 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ProcessResultHookDispatcher;
|
Chris@17
|
19 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\StatusDeterminerHookDispatcher;
|
Chris@17
|
20 use Consolidation\AnnotatedCommand\Hooks\Dispatchers\ExtracterHookDispatcher;
|
Chris@17
|
21
|
Chris@17
|
22 /**
|
Chris@17
|
23 * Write the results of a command. Inject your ResultWriter
|
Chris@17
|
24 * into the CommandProcessor.
|
Chris@17
|
25 */
|
Chris@17
|
26 class ResultWriter
|
Chris@17
|
27 {
|
Chris@17
|
28 /** var FormatterManager */
|
Chris@17
|
29 protected $formatterManager;
|
Chris@17
|
30 /** @var callable */
|
Chris@17
|
31 protected $displayErrorFunction;
|
Chris@17
|
32
|
Chris@17
|
33 public function setFormatterManager(FormatterManager $formatterManager)
|
Chris@17
|
34 {
|
Chris@17
|
35 $this->formatterManager = $formatterManager;
|
Chris@17
|
36 return $this;
|
Chris@17
|
37 }
|
Chris@17
|
38
|
Chris@17
|
39 /**
|
Chris@17
|
40 * Return the formatter manager
|
Chris@17
|
41 * @return FormatterManager
|
Chris@17
|
42 */
|
Chris@17
|
43 public function formatterManager()
|
Chris@17
|
44 {
|
Chris@17
|
45 return $this->formatterManager;
|
Chris@17
|
46 }
|
Chris@17
|
47
|
Chris@17
|
48 public function setDisplayErrorFunction(callable $fn)
|
Chris@17
|
49 {
|
Chris@17
|
50 $this->displayErrorFunction = $fn;
|
Chris@17
|
51 return $this;
|
Chris@17
|
52 }
|
Chris@17
|
53
|
Chris@17
|
54 /**
|
Chris@17
|
55 * Handle the result output and status code calculation.
|
Chris@17
|
56 */
|
Chris@17
|
57 public function handle(OutputInterface $output, $result, CommandData $commandData, $statusCodeDispatcher = null, $extractDispatcher = null)
|
Chris@17
|
58 {
|
Chris@17
|
59 // A little messy, for backwards compatibility: if the result implements
|
Chris@17
|
60 // ExitCodeInterface, then use that as the exit code. If a status code
|
Chris@17
|
61 // dispatcher returns a non-zero result, then we will never print a
|
Chris@17
|
62 // result.
|
Chris@17
|
63 $status = null;
|
Chris@17
|
64 if ($result instanceof ExitCodeInterface) {
|
Chris@17
|
65 $status = $result->getExitCode();
|
Chris@17
|
66 } elseif (isset($statusCodeDispatcher)) {
|
Chris@17
|
67 $status = $statusCodeDispatcher->determineStatusCode($result);
|
Chris@17
|
68 if (isset($status) && ($status != 0)) {
|
Chris@17
|
69 return $status;
|
Chris@17
|
70 }
|
Chris@17
|
71 }
|
Chris@17
|
72 // If the result is an integer and no separate status code was provided, then use the result as the status and do no output.
|
Chris@17
|
73 if (is_integer($result) && !isset($status)) {
|
Chris@17
|
74 return $result;
|
Chris@17
|
75 }
|
Chris@17
|
76 $status = $this->interpretStatusCode($status);
|
Chris@17
|
77
|
Chris@17
|
78 // Get the structured output, the output stream and the formatter
|
Chris@17
|
79 $structuredOutput = $result;
|
Chris@17
|
80 if (isset($extractDispatcher)) {
|
Chris@17
|
81 $structuredOutput = $extractDispatcher->extractOutput($result);
|
Chris@17
|
82 }
|
Chris@17
|
83 if (($status != 0) && is_string($structuredOutput)) {
|
Chris@17
|
84 $output = $this->chooseOutputStream($output, $status);
|
Chris@17
|
85 return $this->writeErrorMessage($output, $status, $structuredOutput, $result);
|
Chris@17
|
86 }
|
Chris@17
|
87 if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) {
|
Chris@17
|
88 return $this->writeUsingFormatter($output, $structuredOutput, $commandData, $status);
|
Chris@17
|
89 }
|
Chris@17
|
90 return $this->writeCommandOutput($output, $structuredOutput, $status);
|
Chris@17
|
91 }
|
Chris@17
|
92
|
Chris@17
|
93 protected function dataCanBeFormatted($structuredOutput)
|
Chris@17
|
94 {
|
Chris@17
|
95 if (!isset($this->formatterManager)) {
|
Chris@17
|
96 return false;
|
Chris@17
|
97 }
|
Chris@17
|
98 return
|
Chris@17
|
99 is_object($structuredOutput) ||
|
Chris@17
|
100 is_array($structuredOutput);
|
Chris@17
|
101 }
|
Chris@17
|
102
|
Chris@17
|
103 /**
|
Chris@17
|
104 * Determine the formatter that should be used to render
|
Chris@17
|
105 * output.
|
Chris@17
|
106 *
|
Chris@17
|
107 * If the user specified a format via the --format option,
|
Chris@17
|
108 * then always return that. Otherwise, return the default
|
Chris@17
|
109 * format, unless --pipe was specified, in which case
|
Chris@17
|
110 * return the default pipe format, format-pipe.
|
Chris@17
|
111 *
|
Chris@17
|
112 * n.b. --pipe is a handy option introduced in Drush 2
|
Chris@17
|
113 * (or perhaps even Drush 1) that indicates that the command
|
Chris@17
|
114 * should select the output format that is most appropriate
|
Chris@17
|
115 * for use in scripts (e.g. to pipe to another command).
|
Chris@17
|
116 *
|
Chris@17
|
117 * @return string
|
Chris@17
|
118 */
|
Chris@17
|
119 protected function getFormat(FormatterOptions $options)
|
Chris@17
|
120 {
|
Chris@17
|
121 // In Symfony Console, there is no way for us to differentiate
|
Chris@17
|
122 // between the user specifying '--format=table', and the user
|
Chris@17
|
123 // not specifying --format when the default value is 'table'.
|
Chris@17
|
124 // Therefore, we must make --field always override --format; it
|
Chris@17
|
125 // cannot become the default value for --format.
|
Chris@17
|
126 if ($options->get('field')) {
|
Chris@17
|
127 return 'string';
|
Chris@17
|
128 }
|
Chris@17
|
129 $defaults = [];
|
Chris@17
|
130 if ($options->get('pipe')) {
|
Chris@17
|
131 return $options->get('pipe-format', [], 'tsv');
|
Chris@17
|
132 }
|
Chris@17
|
133 return $options->getFormat($defaults);
|
Chris@17
|
134 }
|
Chris@17
|
135
|
Chris@17
|
136 /**
|
Chris@17
|
137 * Determine whether we should use stdout or stderr.
|
Chris@17
|
138 */
|
Chris@17
|
139 protected function chooseOutputStream(OutputInterface $output, $status)
|
Chris@17
|
140 {
|
Chris@17
|
141 // If the status code indicates an error, then print the
|
Chris@17
|
142 // result to stderr rather than stdout
|
Chris@17
|
143 if ($status && ($output instanceof ConsoleOutputInterface)) {
|
Chris@17
|
144 return $output->getErrorOutput();
|
Chris@17
|
145 }
|
Chris@17
|
146 return $output;
|
Chris@17
|
147 }
|
Chris@17
|
148
|
Chris@17
|
149 /**
|
Chris@17
|
150 * Call the formatter to output the provided data.
|
Chris@17
|
151 */
|
Chris@17
|
152 protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, CommandData $commandData, $status = 0)
|
Chris@17
|
153 {
|
Chris@17
|
154 $formatterOptions = $commandData->formatterOptions();
|
Chris@17
|
155 $format = $this->getFormat($formatterOptions);
|
Chris@17
|
156 $this->formatterManager->write(
|
Chris@17
|
157 $output,
|
Chris@17
|
158 $format,
|
Chris@17
|
159 $structuredOutput,
|
Chris@17
|
160 $formatterOptions
|
Chris@17
|
161 );
|
Chris@17
|
162 return $status;
|
Chris@17
|
163 }
|
Chris@17
|
164
|
Chris@17
|
165 /**
|
Chris@17
|
166 * Description
|
Chris@17
|
167 * @param OutputInterface $output
|
Chris@17
|
168 * @param int $status
|
Chris@17
|
169 * @param string $structuredOutput
|
Chris@17
|
170 * @param mixed $originalResult
|
Chris@17
|
171 * @return type
|
Chris@17
|
172 */
|
Chris@17
|
173 protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
|
Chris@17
|
174 {
|
Chris@17
|
175 if (isset($this->displayErrorFunction)) {
|
Chris@17
|
176 call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
|
Chris@17
|
177 } else {
|
Chris@17
|
178 $this->writeCommandOutput($output, $structuredOutput);
|
Chris@17
|
179 }
|
Chris@17
|
180 return $status;
|
Chris@17
|
181 }
|
Chris@17
|
182
|
Chris@17
|
183 /**
|
Chris@17
|
184 * If the result object is a string, then print it.
|
Chris@17
|
185 */
|
Chris@17
|
186 protected function writeCommandOutput(
|
Chris@17
|
187 OutputInterface $output,
|
Chris@17
|
188 $structuredOutput,
|
Chris@17
|
189 $status = 0
|
Chris@17
|
190 ) {
|
Chris@17
|
191 // If there is no formatter, we will print strings,
|
Chris@17
|
192 // but can do no more than that.
|
Chris@17
|
193 if (is_string($structuredOutput)) {
|
Chris@17
|
194 $output->writeln($structuredOutput);
|
Chris@17
|
195 }
|
Chris@17
|
196 return $status;
|
Chris@17
|
197 }
|
Chris@17
|
198
|
Chris@17
|
199 /**
|
Chris@17
|
200 * If a status code was set, then return it; otherwise,
|
Chris@17
|
201 * presume success.
|
Chris@17
|
202 */
|
Chris@17
|
203 protected function interpretStatusCode($status)
|
Chris@17
|
204 {
|
Chris@17
|
205 if (isset($status)) {
|
Chris@17
|
206 return $status;
|
Chris@17
|
207 }
|
Chris@17
|
208 return 0;
|
Chris@17
|
209 }
|
Chris@17
|
210 }
|