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

CommandProcessor::initializeHook()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 3
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\Options\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 initializeHook(
62
        InputInterface $input,
63
        $names,
64
        AnnotationData $annotationData
65
    ) {
66
        return $this->hookManager()->initializeHook($input, $names, $annotationData);
67
    }
68
69
    public function interact(
70
        InputInterface $input,
71
        OutputInterface $output,
72
        $names,
73
        AnnotationData $annotationData
74
    ) {
75
        return $this->hookManager()->interact($input, $output, $names, $annotationData);
76
    }
77
78
    public function process(
79
        OutputInterface $output,
80
        $names,
81
        $commandCallback,
82
        AnnotationData $annotationData,
83
        $args
84
    ) {
85
        $result = [];
86
        // Recover options from the end of the args
87
        $options = end($args);
88
        try {
89
            $result = $this->validateRunAndAlter(
90
                $names,
91
                $commandCallback,
92
                $args,
93
                $annotationData
94
            );
95
            return $this->handleResults($output, $names, $result, $annotationData, $options);
96
        } catch (\Exception $e) {
97
            $result = new CommandError($e->getMessage(), $e->getCode());
98
            return $this->handleResults($output, $names, $result, $annotationData, $options);
99
        }
100
    }
101
102 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...
103
        $names,
104
        $commandCallback,
105
        $args,
106
        AnnotationData $annotationData
107
    ) {
108
        // Validators return any object to signal a validation error;
109
        // if the return an array, it replaces the arguments.
110
        $validated = $this->hookManager()->validateArguments($names, $args, $annotationData);
111
        if (is_object($validated)) {
112
            return $validated;
113
        }
114
        if (is_array($validated)) {
115
            $args = $validated;
116
        }
117
118
        // Run the command, alter the results, and then handle output and status
119
        $result = $this->runCommandCallback($commandCallback, $args);
120
        return $this->processResults($names, $result, $args, $annotationData);
121
    }
122
123
    public function processResults($names, $result, $args, $annotationData)
124
    {
125
        return $this->hookManager()->alterResult($names, $result, $args, $annotationData);
126
    }
127
128
    /**
129
     * Handle the result output and status code calculation.
130
     */
131
    public function handleResults(OutputInterface $output, $names, $result, AnnotationData $annotationData, $options = [])
132
    {
133
        $status = $this->hookManager()->determineStatusCode($names, $result);
134
        // If the result is an integer and no separate status code was provided, then use the result as the status and do no output.
135
        if (is_integer($result) && !isset($status)) {
136
            return $result;
137
        }
138
        $status = $this->interpretStatusCode($status);
139
140
        // Get the structured output, the output stream and the formatter
141
        $structuredOutput = $this->hookManager()->extractOutput($names, $result);
142
        $output = $this->chooseOutputStream($output, $status);
143
        if ($status != 0) {
144
            return $this->writeErrorMessage($output, $status, $structuredOutput, $result);
145
        }
146
        if ($this->dataCanBeFormatted($structuredOutput) && isset($this->formatterManager)) {
147
            return $this->writeUsingFormatter($output, $structuredOutput, $annotationData, $options);
148
        }
149
        return $this->writeCommandOutput($output, $structuredOutput);
150
    }
151
152
    protected function dataCanBeFormatted($structuredOutput)
153
    {
154
        if (!isset($this->formatterManager)) {
155
            return false;
156
        }
157
        return
158
            is_object($structuredOutput) ||
159
            is_array($structuredOutput);
160
    }
161
162
    /**
163
     * Run the main command callback
164
     */
165
    protected function runCommandCallback($commandCallback, $args)
166
    {
167
        $result = false;
168
        try {
169
            $result = call_user_func_array($commandCallback, $args);
170
        } catch (\Exception $e) {
171
            $result = new CommandError($e->getMessage(), $e->getCode());
172
        }
173
        return $result;
174
    }
175
176
    /**
177
     * Determine the formatter that should be used to render
178
     * output.
179
     *
180
     * If the user specified a format via the --format option,
181
     * then always return that.  Otherwise, return the default
182
     * format, unless --pipe was specified, in which case
183
     * return the default pipe format, format-pipe.
184
     *
185
     * n.b. --pipe is a handy option introduced in Drush 2
186
     * (or perhaps even Drush 1) that indicates that the command
187
     * should select the output format that is most appropriate
188
     * for use in scripts (e.g. to pipe to another command).
189
     *
190
     * @return string
191
     */
192
    protected function getFormat($options)
193
    {
194
        $options += [
195
            'default-format' => false,
196
            'pipe' => false,
197
        ];
198
        $options += [
199
            'format' => $options['default-format'],
200
            'format-pipe' => $options['default-format'],
201
        ];
202
203
        $format = $options['format'];
204
        if ($options['pipe']) {
205
            $format = $options['format-pipe'];
206
        }
207
        return $format;
208
    }
209
210
    /**
211
     * Determine whether we should use stdout or stderr.
212
     */
213
    protected function chooseOutputStream(OutputInterface $output, $status)
214
    {
215
        // If the status code indicates an error, then print the
216
        // result to stderr rather than stdout
217
        if ($status && ($output instanceof ConsoleOutputInterface)) {
218
            return $output->getErrorOutput();
219
        }
220
        return $output;
221
    }
222
223
    /**
224
     * Call the formatter to output the provided data.
225
     */
226
    protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, AnnotationData $annotationData, $options)
227
    {
228
        $format = $this->getFormat($options);
229
        $formatterOptions = new FormatterOptions($annotationData->getArrayCopy(), $options);
230
        $this->formatterManager->write(
231
            $output,
232
            $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...
233
            $structuredOutput,
234
            $formatterOptions
235
        );
236
        return 0;
237
    }
238
239
    /**
240
     * Description
241
     * @param OutputInterface $output
242
     * @param int $status
243
     * @param string $structuredOutput
244
     * @param mixed $originalResult
245
     * @return type
246
     */
247
    protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
248
    {
249
        if (isset($this->displayErrorFunction)) {
250
            call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
251
        } else {
252
            $this->writeCommandOutput($output, $structuredOutput);
253
        }
254
        return $status;
255
    }
256
257
    /**
258
     * If the result object is a string, then print it.
259
     */
260
    protected function writeCommandOutput(
261
        OutputInterface $output,
262
        $structuredOutput
263
    ) {
264
        // If there is no formatter, we will print strings,
265
        // but can do no more than that.
266
        if (is_string($structuredOutput)) {
267
            $output->writeln($structuredOutput);
268
        }
269
        return 0;
270
    }
271
272
    /**
273
     * If a status code was set, then return it; otherwise,
274
     * presume success.
275
     */
276
    protected function interpretStatusCode($status)
277
    {
278
        if (isset($status)) {
279
            return $status;
280
        }
281
        return 0;
282
    }
283
}
284