Completed
Push — master ( efd4b5...cdc15c )
by Greg
11s
created

ExecTrait::execute()   C

Complexity

Conditions 11
Paths 128

Size

Total Lines 66
Code Lines 43

Duplication

Lines 20
Ratio 30.3 %

Importance

Changes 0
Metric Value
dl 20
loc 66
rs 5.5371
c 0
b 0
f 0
cc 11
eloc 43
nc 128
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Robo\Common;
4
5
use Robo\ResultData;
6
use Symfony\Component\Process\Process;
7
8
/**
9
 * Class ExecTrait
10
 * @package Robo\Common
11
 */
12
trait ExecTrait
13
{
14
    /**
15
     * @var bool
16
     */
17
    protected $background = false;
18
19
    /**
20
     * @var null|int
21
     */
22
    protected $timeout = null;
23
24
    /**
25
     * @var null|int
26
     */
27
    protected $idleTimeout = null;
28
29
    /**
30
     * @var null|array
31
     */
32
    protected $env = null;
33
34
    /**
35
     * @var Process
36
     */
37
    protected $process;
38
39
    /**
40
     * @var resource|string
41
     */
42
    protected $input;
43
44
    /**
45
     * @var boolean
46
     */
47
    protected $interactive = false;
48
49
    /**
50
     * @var bool
51
     */
52
    protected $isPrinted = true;
53
54
    /**
55
     * @var bool
56
     */
57
    protected $isMetadataPrinted = true;
58
59
    /**
60
     * @var string
61
     */
62
    protected $workingDirectory;
63
64
    /**
65
     * @return string
66
     */
67
    abstract function getCommandDescription();
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
68
69
    /** Typically provided by Timer trait via ProgressIndicatorAwareTrait. */
70
    abstract function startTimer();
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
71
    abstract function stopTimer();
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
72
    abstract function getExecutionTime();
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
73
74
    /**
75
     * Typically provided by TaskIO Trait.
76
     */
77
    abstract function hideTaskProgress();
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
78
    abstract function showTaskProgress($inProgress);
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
79
    abstract function printTaskInfo($text, $context = null);
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
80
81
    /**
82
     * Typically provided by VerbosityThresholdTrait.
83
     */
84
    abstract function verbosityMeetsThreshold();
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
85
    abstract function writeMessage($message);
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
86
87
    /**
88
     * Sets $this->interactive() based on posix_isatty().
89
     */
90
    public function detectInteractive()
91
    {
92
        // If the caller did not explicity set the 'interactive' mode,
93
        // and output should be produced by this task (verbosityMeetsThreshold),
94
        // then we will automatically set interactive mode based on whether
95
        // or not output was redirected when robo was executed.
96
        if (!isset($this->interactive) && function_exists('posix_isatty') && $this->verbosityMeetsThreshold()) {
97
            $this->interactive = posix_isatty(STDOUT);
98
        }
99
    }
100
101
    /**
102
     * Executes command in background mode (asynchronously)
103
     *
104
     * @return $this
105
     */
106
    public function background($arg = true)
107
    {
108
        $this->background = $arg;
109
        return $this;
110
    }
111
112
    /**
113
     * Stop command if it runs longer then $timeout in seconds
114
     *
115
     * @param int $timeout
116
     *
117
     * @return $this
118
     */
119
    public function timeout($timeout)
120
    {
121
        $this->timeout = $timeout;
122
        return $this;
123
    }
124
125
    /**
126
     * Stops command if it does not output something for a while
127
     *
128
     * @param int $timeout
129
     *
130
     * @return $this
131
     */
132
    public function idleTimeout($timeout)
133
    {
134
        $this->idleTimeout = $timeout;
135
        return $this;
136
    }
137
138
    /**
139
     * Sets the environment variables for the command
140
     *
141
     * @param array $env
142
     *
143
     * @return $this
144
     */
145
    public function env(array $env)
146
    {
147
        $this->env = $env;
148
        return $this;
149
    }
150
151
    /**
152
     * Pass an input to the process. Can be resource created with fopen() or string
153
     *
154
     * @param resource|string $input
155
     *
156
     * @return $this
157
     */
158
    public function setInput($input)
159
    {
160
        $this->input = $input;
161
        return $this;
162
    }
163
164
    /**
165
     * Attach tty to process for interactive input
166
     *
167
     * @param $interactive bool
168
     *
169
     * @return $this
170
     */
171
    public function interactive($interactive)
172
    {
173
        $this->interactive = $interactive;
174
        return $this;
175
    }
176
177
178
    /**
179
     * Is command printing its output to screen
180
     *
181
     * @return bool
182
     */
183
    public function getPrinted()
184
    {
185
        return $this->isPrinted;
186
    }
187
188
    /**
189
     * Changes working directory of command
190
     *
191
     * @param string $dir
192
     *
193
     * @return $this
194
     */
195
    public function dir($dir)
196
    {
197
        $this->workingDirectory = $dir;
198
        return $this;
199
    }
200
201
    /**
202
     * Shortcut for setting isPrinted() and isMetadataPrinted() to false.
203
     *
204
     * @param bool $arg
205
     *
206
     * @return $this
207
     */
208
    public function silent($arg)
209
    {
210
        if (is_bool($arg)) {
211
            $this->isPrinted = !$arg;
212
            $this->isMetadataPrinted = !$arg;
213
        }
214
        return $this;
215
    }
216
217
    /**
218
     * Should command output be printed
219
     *
220
     * @param bool $arg
221
     *
222
     * @return $this
223
     *
224
     * @deprecated
225
     */
226
    public function printed($arg)
227
    {
228
        $this->logger->warning("printed() is deprecated. Please use printOutput().");
0 ignored issues
show
Bug introduced by
The property logger does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
229
        return $this->printOutput($arg);
230
    }
231
232
    /**
233
     * Should command output be printed
234
     *
235
     * @param bool $arg
236
     *
237
     * @return $this
238
     */
239
    public function printOutput($arg)
240
    {
241
        if (is_bool($arg)) {
242
            $this->isPrinted = $arg;
243
        }
244
        return $this;
245
    }
246
247
    /**
248
     * Should command metadata be printed. I,e., command and timer.
249
     *
250
     * @param bool $arg
251
     *
252
     * @return $this
253
     */
254
    public function printMetadata($arg)
255
    {
256
        if (is_bool($arg)) {
257
            $this->isMetadataPrinted = $arg;
258
        }
259
        return $this;
260
    }
261
262
    /**
263
     * @param Process $process
264
     * @param callable $output_callback
265
     *
266
     * @return \Robo\ResultData
267
     */
268
    protected function execute($process, $output_callback = null)
269
    {
270
        $this->process = $process;
271
272
        if (!$output_callback) {
273
            $output_callback = function ($type, $buffer) {
274
                $progressWasVisible = $this->hideTaskProgress();
275
                $this->writeMessage($buffer);
276
                $this->showTaskProgress($progressWasVisible);
277
            };
278
        }
279
280
        $this->detectInteractive();
281
282
        if ($this->isMetadataPrinted) {
283
            $this->printAction();
284
        }
285
        $this->process->setTimeout($this->timeout);
286
        $this->process->setIdleTimeout($this->idleTimeout);
287
        $this->process->setWorkingDirectory($this->workingDirectory);
288
289
        if ($this->input) {
290
            $this->process->setInput($this->input);
291
        }
292
293
        if ($this->interactive) {
294
            $this->process->setTty(true);
295
        }
296
297
        if (isset($this->env)) {
298
            $this->process->setEnv($this->env);
299
        }
300
301 View Code Duplication
        if (!$this->background && !$this->isPrinted) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
302
            $this->startTimer();
303
            $this->process->run();
304
            $this->stopTimer();
305
            return new ResultData(
306
                $this->process->getExitCode(),
307
                $this->process->getOutput(),
308
                $this->getResultData()
309
            );
310
        }
311
312 View Code Duplication
        if (!$this->background && $this->isPrinted) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
313
            $this->startTimer();
314
            $this->process->run($output_callback);
315
            $this->stopTimer();
316
            return new ResultData(
317
                $this->process->getExitCode(),
318
                $this->process->getOutput(),
319
                $this->getResultData()
320
            );
321
        }
322
323
        try {
324
            $this->process->start();
325
        } catch (\Exception $e) {
326
            return new ResultData(
327
                $this->process->getExitCode(),
328
                $e->getMessage(),
329
                $this->getResultData()
330
            );
331
        }
332
        return new ResultData($this->process->getExitCode());
333
    }
334
335
    /**
336
     *
337
     */
338
    protected function stop()
339
    {
340
        if ($this->background && isset($this->process) && $this->process->isRunning()) {
341
            $this->process->stop();
342
            $this->printTaskInfo(
343
                "Stopped {command}",
344
                ['command' => $this->getCommandDescription()]
345
            );
346
        }
347
    }
348
349
    /**
350
     * @param array $context
351
     */
352
    protected function printAction($context = [])
353
    {
354
        $command = $this->getCommandDescription();
355
        $dir = $this->workingDirectory ? " in {dir}" : "";
356
        $this->printTaskInfo("Running {command}$dir", [
357
                'command' => $command,
358
                'dir' => $this->workingDirectory
359
            ] + $context);
360
    }
361
362
    /**
363
     * Gets the data array to be passed to Result().
364
     *
365
     * @return array
366
     *   The data array passed to Result().
367
     */
368
    protected function getResultData()
369
    {
370
        if ($this->isMetadataPrinted) {
371
            return ['time' => $this->getExecutionTime()];
372
        }
373
374
        return [];
375
    }
376
}
377