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

CommandProcessor::interpretStatusCode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

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