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

CommandProcessor::chooseOutputStream()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
c 0
b 0
f 0
rs 9.6666
cc 3
eloc 4
nc 2
nop 2
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
use Consolidation\OutputFormatters\FormatterOptions;
11
use Consolidation\AnnotatedCommand\Hooks\HookManager;
12
13
/**
14
 * Process a command, including hooks and other callbacks.
15
 * There should only be one command processor per application.
16
 * Provide your command processor to the AnnotatedCommandFactory
17
 * via AnnotatedCommandFactory::setCommandProcessor().
18
 */
19
class CommandProcessor
20
{
21
    /** var HookManager */
22
    protected $hookManager;
23
    /** var FormatterManager */
24
    protected $formatterManager;
25
    /** var callable */
26
    protected $displayErrorFunction;
27
28
    public function __construct(HookManager $hookManager)
29
    {
30
        $this->hookManager = $hookManager;
31
    }
32
33
    /**
34
     * Return the hook manager
35
     * @return HookManager
36
     */
37
    public function hookManager()
38
    {
39
        return $this->hookManager;
40
    }
41
42
    public function setFormatterManager(FormatterManager $formatterManager)
43
    {
44
        $this->formatterManager = $formatterManager;
45
    }
46
47
    public function setDisplayErrorFunction(callable $fn)
48
    {
49
        $this->displayErrorFunction = $fn;
50
    }
51
52
    /**
53
     * Return the formatter manager
54
     * @return FormatterManager
55
     */
56
    public function formatterManager()
57
    {
58
        return $this->formatterManager;
59
    }
60
61
    public function process(
62
        OutputInterface $output,
63
        $names,
64
        $commandCallback,
65
        AnnotationData $annotationData,
66
        $args
67
    ) {
68
        $result = [];
69
        // Recover options from the end of the args
70
        $options = end($args);
71
        try {
72
            $result = $this->validateRunAndAlter(
73
                $names,
74
                $commandCallback,
75
                $args,
76
                $annotationData
77
            );
78
            return $this->handleResults($output, $names, $result, $annotationData, $options);
79
        } catch (\Exception $e) {
80
            $result = new CommandError($e->getMessage(), $e->getCode());
81
            return $this->handleResults($output, $names, $result, $annotationData, $options);
82
        }
83
    }
84
85 View Code Duplication
    public function validateRunAndAlter(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
86
        $names,
87
        $commandCallback,
88
        $args,
89
        AnnotationData $annotationData
90
    ) {
91
        // Validators return any object to signal a validation error;
92
        // if the return an array, it replaces the arguments.
93
        $validated = $this->hookManager()->validateArguments($names, $args, $annotationData);
94
        if (is_object($validated)) {
95
            return $validated;
96
        }
97
        if (is_array($validated)) {
98
            $args = $validated;
99
        }
100
101
        // Run the command, alter the results, and then handle output and status
102
        $result = $this->runCommandCallback($commandCallback, $args);
103
        return $this->processResults($names, $result, $args, $annotationData);
104
    }
105
106
    public function processResults($names, $result, $args, $annotationData)
107
    {
108
        return $this->hookManager()->alterResult($names, $result, $args, $annotationData);
109
    }
110
111
    /**
112
     * Handle the result output and status code calculation.
113
     */
114
    public function handleResults(OutputInterface $output, $names, $result, AnnotationData $annotationData, $options = [])
115
    {
116
        $status = $this->hookManager()->determineStatusCode($names, $result);
117
        // If the result is an integer and no separate status code was provided, then use the result as the status and do no output.
118
        if (is_integer($result) && !isset($status)) {
119
            return $result;
120
        }
121
        $status = $this->interpretStatusCode($status);
122
123
        // Get the structured output, the output stream and the formatter
124
        $structuredOutput = $this->hookManager()->extractOutput($names, $result);
125
        $output = $this->chooseOutputStream($output, $status);
126
        if ($status != 0) {
127
            return $this->writeErrorMessage($output, $status, $structuredOutput, $result);
0 ignored issues
show
Documentation introduced by
$output is of type object<Symfony\Component...Output\OutputInterface>, but the function expects a object<Consolidation\AnnotatedCommand\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
128
        }
129
        if (isset($this->formatterManager)) {
130
            return $this->writeUsingFormatter($output, $structuredOutput, $annotationData, $options);
131
        }
132
        return $this->writeCommandOutput($output, $structuredOutput);
133
    }
134
135
    /**
136
     * Run the main command callback
137
     */
138
    protected function runCommandCallback($commandCallback, $args)
139
    {
140
        $result = false;
141
        try {
142
            $result = call_user_func_array($commandCallback, $args);
143
        } catch (\Exception $e) {
144
            $result = new CommandError($e->getMessage(), $e->getCode());
145
        }
146
        return $result;
147
    }
148
149
    /**
150
     * Determine the formatter that should be used to render
151
     * output.
152
     *
153
     * If the user specified a format via the --format option,
154
     * then always return that.  Otherwise, return the default
155
     * format, unless --pipe was specified, in which case
156
     * return the default pipe format, format-pipe.
157
     *
158
     * n.b. --pipe is a handy option introduced in Drush 2
159
     * (or perhaps even Drush 1) that indicates that the command
160
     * should select the output format that is most appropriate
161
     * for use in scripts (e.g. to pipe to another command).
162
     *
163
     * @return string
164
     */
165
    protected function getFormat($options)
166
    {
167
        $options += [
168
            'default-format' => false,
169
            'pipe' => false,
170
        ];
171
        $options += [
172
            'format' => $options['default-format'],
173
            'format-pipe' => $options['default-format'],
174
        ];
175
176
        $format = $options['format'];
177
        if ($options['pipe']) {
178
            $format = $options['format-pipe'];
179
        }
180
        return $format;
181
    }
182
183
    /**
184
     * Determine whether we should use stdout or stderr.
185
     */
186
    protected function chooseOutputStream(OutputInterface $output, $status)
187
    {
188
        // If the status code indicates an error, then print the
189
        // result to stderr rather than stdout
190
        if ($status && ($output instanceof ConsoleOutputInterface)) {
191
            return $output->getErrorOutput();
192
        }
193
        return $output;
194
    }
195
196
    /**
197
     * Call the formatter to output the provided data.
198
     */
199
    protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, AnnotationData $annotationData, $options)
200
    {
201
        $format = $this->getFormat($options);
202
        $formatterOptions = new FormatterOptions($annotationData->getArrayCopy(), $options);
203
        $this->formatterManager->write(
204
            $output,
205
            $format,
0 ignored issues
show
Documentation introduced by
$format is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
206
            $structuredOutput,
207
            $formatterOptions
208
        );
209
        return 0;
210
    }
211
212
    /**
213
     * Description
214
     * @param type $output
215
     * @param type $status
216
     * @param type $structuredOutput
217
     * @return type
218
     */
219
    protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
220
    {
221
        if (isset($this->displayErrorFunction)) {
222
            call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
223
        } else {
224
            $this->writeCommandOutput($output, $structuredOutput);
225
        }
226
        return $status;
227
    }
228
229
    /**
230
     * If the result object is a string, then print it.
231
     */
232
    protected function writeCommandOutput(
233
        OutputInterface $output,
234
        $structuredOutput
235
    ) {
236
        // If there is no formatter, we will print strings,
237
        // but can do no more than that.
238
        if (is_string($structuredOutput)) {
239
            $output->writeln($structuredOutput);
240
        }
241
        return 0;
242
    }
243
244
    /**
245
     * If a status code was set, then return it; otherwise,
246
     * presume success.
247
     */
248
    protected function interpretStatusCode($status)
249
    {
250
        if (isset($status)) {
251
            return $status;
252
        }
253
        return 0;
254
    }
255
}
256