Completed
Push — master ( 1c5efd...75f82a )
by Greg
12s
created

CommandProcessor::handleResults()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 19
rs 8.8571
cc 5
eloc 12
nc 3
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
 * 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 result is an integer and no separate status code was provided, then use the result as the status and do no output.
97
        if (is_integer($result) && !isset($status)) {
98
            return $result;
99
        }
100
        $status = $this->interpretStatusCode($status);
101
102
        // Get the structured output, the output stream and the formatter
103
        $structuredOutput = $this->hookManager()->extractOutput($names, $result);
104
        $output = $this->chooseOutputStream($output, $status);
105
        if (($status == 0) && isset($this->formatterManager)) {
106
            $this->writeUsingFormatter($output, $structuredOutput, $annotationData, $options);
107
        } else {
108
            $this->writeCommandOutput($output, $structuredOutput);
109
        }
110
        return $status;
111
    }
112
113
    /**
114
     * Run the main command callback
115
     */
116
    protected function runCommandCallback($commandCallback, $args)
117
    {
118
        $result = false;
119
        try {
120
            $result = call_user_func_array($commandCallback, $args);
121
        } catch (\Exception $e) {
122
            $result = new CommandError($e->getMessage(), $e->getCode());
123
        }
124
        return $result;
125
    }
126
127
    /**
128
     * Determine the formatter that should be used to render
129
     * output.
130
     *
131
     * If the user specified a format via the --format option,
132
     * then always return that.  Otherwise, return the default
133
     * format, unless --pipe was specified, in which case
134
     * return the default pipe format, format-pipe.
135
     *
136
     * n.b. --pipe is a handy option introduced in Drush 2
137
     * (or perhaps even Drush 1) that indicates that the command
138
     * should select the output format that is most appropriate
139
     * for use in scripts (e.g. to pipe to another command).
140
     */
141
    protected function getFormat($options)
142
    {
143
        $options += [
144
            'default-format' => false,
145
            'pipe' => false,
146
        ];
147
        $options += [
148
            'format' => $options['default-format'],
149
            'format-pipe' => $options['default-format'],
150
        ];
151
152
        $format = $options['format'];
153
        if ($options['pipe']) {
154
            $format = $options['format-pipe'];
155
        }
156
        return $format;
157
    }
158
159
    /**
160
     * Determine whether we should use stdout or stderr.
161
     */
162
    protected function chooseOutputStream(OutputInterface $output, $status)
163
    {
164
        // If the status code indicates an error, then print the
165
        // result to stderr rather than stdout
166
        if ($status && ($output instanceof ConsoleOutputInterface)) {
167
            return $output->getErrorOutput();
168
        }
169
        return $output;
170
    }
171
172
    /**
173
     * Call the formatter to output the provided data.
174
     */
175
    protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, $annotationData, $options)
176
    {
177
        $format = $this->getFormat($options);
178
        $this->formatterManager->write(
179
            $output,
180
            $format,
181
            $structuredOutput,
182
            $annotationData,
183
            $options
184
        );
185
    }
186
187
    /**
188
     * If the result object is a string, then print it.
189
     */
190
    protected function writeCommandOutput(
191
        OutputInterface $output,
192
        $structuredOutput
193
    ) {
194
        // If there is no formatter, we will print strings,
195
        // but can do no more than that.
196
        if (is_string($structuredOutput)) {
197
            $output->writeln($structuredOutput);
198
        }
199
    }
200
201
    /**
202
     * If a status code was set, then return it; otherwise,
203
     * presume success.
204
     */
205
    protected function interpretStatusCode($status)
206
    {
207
        if (isset($status)) {
208
            return $status;
209
        }
210
        return 0;
211
    }
212
}
213