Completed
Push — master ( 11c9b6...2b5f0a )
by Greg
03:59
created

CommandProcessor   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 273
Duplicated Lines 7.33 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 37
c 2
b 0
f 0
lcom 1
cbo 7
dl 20
loc 273
rs 8.6

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A hookManager() 0 4 1
A setFormatterManager() 0 4 1
A setDisplayErrorFunction() 0 4 1
A formatterManager() 0 4 1
A initializeHook() 0 7 1
A interact() 0 8 1
A process() 0 23 2
A validateRunAndAlter() 20 20 3
A processResults() 0 4 1
B handleResults() 0 20 6
A dataCanBeFormatted() 0 9 3
A runCommandCallback() 0 10 2
B getFormat() 0 25 3
A chooseOutputStream() 0 9 3
A writeUsingFormatter() 0 12 1
A writeErrorMessage() 0 9 2
A writeCommandOutput() 0 11 2
A interpretStatusCode() 0 7 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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
        // In Symfony Console, there is no way for us to differentiate
195
        // between the user specifying '--format=table', and the user
196
        // not specifying --format when the default value is 'table'.
197
        // Therefore, we must make --field always override --format; it
198
        // cannot become the default value for --format.
199
        if ($options['field']) {
200
            return 'string';
201
        }
202
        $options += [
203
            'default-format' => false,
204
            'pipe' => false,
205
        ];
206
        $options += [
207
            'format' => $options['default-format'],
208
            'format-pipe' => $options['default-format'],
209
        ];
210
211
        $format = $options['format'];
212
        if ($options['pipe']) {
213
            $format = $options['format-pipe'];
214
        }
215
        return $format;
216
    }
217
218
    /**
219
     * Determine whether we should use stdout or stderr.
220
     */
221
    protected function chooseOutputStream(OutputInterface $output, $status)
222
    {
223
        // If the status code indicates an error, then print the
224
        // result to stderr rather than stdout
225
        if ($status && ($output instanceof ConsoleOutputInterface)) {
226
            return $output->getErrorOutput();
227
        }
228
        return $output;
229
    }
230
231
    /**
232
     * Call the formatter to output the provided data.
233
     */
234
    protected function writeUsingFormatter(OutputInterface $output, $structuredOutput, AnnotationData $annotationData, $options)
235
    {
236
        $format = $this->getFormat($options);
237
        $formatterOptions = new FormatterOptions($annotationData->getArrayCopy(), $options);
238
        $this->formatterManager->write(
239
            $output,
240
            $format,
0 ignored issues
show
Security Bug introduced by
It seems like $format defined by $this->getFormat($options) on line 236 can also be of type false; however, Consolidation\OutputForm...rmatterManager::write() does only seem to accept string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
241
            $structuredOutput,
242
            $formatterOptions
243
        );
244
        return 0;
245
    }
246
247
    /**
248
     * Description
249
     * @param OutputInterface $output
250
     * @param int $status
251
     * @param string $structuredOutput
252
     * @param mixed $originalResult
253
     * @return type
254
     */
255
    protected function writeErrorMessage($output, $status, $structuredOutput, $originalResult)
256
    {
257
        if (isset($this->displayErrorFunction)) {
258
            call_user_func($this->displayErrorFunction, $output, $structuredOutput, $status, $originalResult);
259
        } else {
260
            $this->writeCommandOutput($output, $structuredOutput);
261
        }
262
        return $status;
263
    }
264
265
    /**
266
     * If the result object is a string, then print it.
267
     */
268
    protected function writeCommandOutput(
269
        OutputInterface $output,
270
        $structuredOutput
271
    ) {
272
        // If there is no formatter, we will print strings,
273
        // but can do no more than that.
274
        if (is_string($structuredOutput)) {
275
            $output->writeln($structuredOutput);
276
        }
277
        return 0;
278
    }
279
280
    /**
281
     * If a status code was set, then return it; otherwise,
282
     * presume success.
283
     */
284
    protected function interpretStatusCode($status)
285
    {
286
        if (isset($status)) {
287
            return $status;
288
        }
289
        return 0;
290
    }
291
}
292