Completed
Push — master ( 6d71ae...f255f3 )
by Greg
01:49
created

ResultWriter::writeCommandOutput()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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