Completed
Pull Request — master (#14)
by Greg
02:21
created

CommandProcessor::process()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 21
rs 9.3142
cc 2
eloc 16
nc 2
nop 5
1
<?php
2
namespace Consolidation\AnnotatedCommand;
3
4
use Symfony\Component\Console\Command\Command;
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
11
/**
12
 * Process a command, including hooks and other callbacks
13
 */
14
class CommandProcessor
15
{
16
    protected $hookManager;
17
    protected $formatterManager;
18
19
    public function __construct($hookManager)
20
    {
21
        $this->hookManager = $hookManager;
22
    }
23
24
    public function hookManager()
25
    {
26
        return $this->hookManager;
27
    }
28
29
    public function setFormatterManager(FormatterManager $formatterManager)
30
    {
31
        $this->formatterManager = $formatterManager;
32
    }
33
34
    public function formatterManager()
35
    {
36
        return $this->formatterManager;
37
    }
38
39
    public function getFormatter($format, $annotationData)
40
    {
41
        if (!isset($this->formatterManager)) {
42
            return;
43
        }
44
        return $this->formatterManager->getFormatter($format, $annotationData);
45
    }
46
47
    public function process(
48
        OutputInterface $output,
49
        $names,
50
        $commandCallback,
51
        $annotationData,
52
        $args
53
    ) {
54
        $result = [];
55
        try {
56
            $result = $this->validateRunAndAlter(
57
                $names,
58
                $commandCallback,
59
                $args
60
            );
61
        } catch (\Exception $e) {
62
            $result = new CommandError($e->getCode(), $e->getMessage());
63
        }
64
        // Recover options from the end of the args
65
        $options = end($args);
66
        return $this->handleResults($output, $names, $result, $annotationData, $options);
67
    }
68
69
    public function validateRunAndAlter(
70
        $names,
71
        $commandCallback,
72
        $args
73
    ) {
74
        // Validators return any object to signal a validation error;
75
        // if the return an array, it replaces the arguments.
76
        $validated = $this->hookManager()->validateArguments($names, $args);
77
        if (is_object($validated)) {
78
            return $validated;
79
        }
80
        if (is_array($validated)) {
81
            $args = $validated;
82
        }
83
84
        // Run the command, alter the results, and then handle output and status
85
        $result = $this->runCommandCallback($commandCallback, $args);
86
        return $this->processResults($names, $result, $args);
87
    }
88
89
    public function processResults($names, $result, $args = [])
90
    {
91
        return $this->hookManager()->alterResult($names, $result, $args);
92
    }
93
94
    /**
95
     * Handle the result output and status code calculation.
96
     */
97
    public function handleResults(OutputInterface $output, $names, $result, $annotationData, $options = [])
98
    {
99
        $status = $this->hookManager()->determineStatusCode($names, $result);
100
        if (is_integer($result) && !isset($status)) {
101
            $status = $result;
102
            $result = null;
103
        }
104
        $status = $this->interpretStatusCode($status);
105
106
        // Get the structured output, the output stream and the formatter
107
        $outputText = $this->hookManager()->extractOutput($names, $result);
108
        $output = $this->chooseOutputStream($output, $status);
109
        $formatter = $this->chooseFormatter($annotationData, $options, $status);
110
111
        // Output the result text and return status code.
112
        $this->writeCommandOutput($outputText, $formatter, $options, $output);
113
        return $status;
114
    }
115
116
    /**
117
     * Run the main command callback
118
     */
119
    protected function runCommandCallback($commandCallback, $args)
120
    {
121
        $result = false;
122
        try {
123
            $result = call_user_func_array($commandCallback, $args);
124
        } catch (\Exception $e) {
125
            $result = new CommandError($e->getMessage(), $e->getCode());
126
        }
127
        return $result;
128
    }
129
130
    /**
131
     * Select the formatter to use.
132
     *
133
     * Note that if there is an error (status code is nonzero),
134
     * then the result object is going to be an error object. This
135
     * object may have a string that may be extracted and printed,
136
     * but it should never be formatted per the --format option.
137
     */
138
    protected function chooseFormatter($annotationData, $options, $status)
139
    {
140
        if ($status) {
141
            return;
142
        }
143
        $format = $this->getFormat($options);
144
        return $this->getFormatter($format, $annotationData);
145
    }
146
147
    /**
148
     * Determine the formatter that should be used to render
149
     * output.
150
     *
151
     * If the user specified a format via the --format option,
152
     * then always return that.  Otherwise, return the default
153
     * format, unless --pipe was specified, in which case
154
     * return the default pipe format, format-pipe.
155
     *
156
     * n.b. --pipe is a handy option introduced in Drush 2
157
     * (or perhaps even Drush 1) that indicates that the command
158
     * should select the output format that is most appropriate
159
     * for use in scripts (e.g. to pipe to another command).
160
     */
161
    protected function getFormat($options)
162
    {
163
        $options += [
164
            'default-format' => false,
165
            'pipe' => false,
166
        ];
167
        $options += [
168
            'format' => $options['default-format'],
169
            'format-pipe' => $options['default-format'],
170
        ];
171
172
        $format = $options['format'];
173
        if ($options['pipe']) {
174
            $format = $options['format-pipe'];
175
        }
176
        return $format;
177
    }
178
179
    /**
180
     * Determine whether we should use stdout or stderr.
181
     */
182
    protected function chooseOutputStream(OutputInterface $output, $status)
183
    {
184
        // If the status code indicates an error, then print the
185
        // result to stderr rather than stdout
186
        if ($status && ($output instanceof ConsoleOutputInterface)) {
187
            return $output->getErrorOutput();
188
        }
189
        return $output;
190
    }
191
192
    /**
193
     * If the result object is a string, then print it.
194
     */
195
    protected function writeCommandOutput(
196
        $outputText,
197
        $formatter,
198
        $options,
199
        OutputInterface $output
200
    ) {
201
        // If there is a formatter, use it.
202
        if (isset($outputText) && $formatter) {
203
            $formatter->write($outputText, $options, $output);
204
            return;
205
        }
206
        // If there is no formatter, we will print strings,
207
        // but can do no more than that.
208
        if (is_string($outputText)) {
209
            $output->writeln($outputText);
210
        }
211
    }
212
213
    /**
214
     * If a status code was set, then return it; otherwise,
215
     * presume success.
216
     */
217
    protected function interpretStatusCode($status)
218
    {
219
        if (isset($status)) {
220
            return $status;
221
        }
222
        return 0;
223
    }
224
}
225