Completed
Push — master ( c44fd6...7eea0d )
by Greg
02:41
created

ExecTrait::formatCommandDisplay()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

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 4
nc 1
nop 1
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 = null;
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 public function getCommandDescription();
68
69
    /** Typically provided by Timer trait via ProgressIndicatorAwareTrait. */
70
    abstract public function startTimer();
71
    abstract public function stopTimer();
72
    abstract public function getExecutionTime();
73
74
    /**
75
     * Typically provided by TaskIO Trait.
76
     */
77
    abstract public function hideTaskProgress();
78
    abstract public function showTaskProgress($inProgress);
79
    abstract public function printTaskInfo($text, $context = null);
80
81
    /**
82
     * Typically provided by VerbosityThresholdTrait.
83
     */
84
    abstract public function verbosityMeetsThreshold();
85
    abstract public function writeMessage($message);
86
87
    /**
88
     * Sets $this->interactive() based on posix_isatty().
89
     *
90
     * @return $this
91
     */
92
    public function detectInteractive()
93
    {
94
        // If the caller did not explicity set the 'interactive' mode,
95
        // and output should be produced by this task (verbosityMeetsThreshold),
96
        // then we will automatically set interactive mode based on whether
97
        // or not output was redirected when robo was executed.
98
        if (!isset($this->interactive) && function_exists('posix_isatty') && $this->verbosityMeetsThreshold()) {
99
            $this->interactive = posix_isatty(STDOUT);
100
        }
101
102
        return $this;
103
    }
104
105
    /**
106
     * Executes command in background mode (asynchronously)
107
     *
108
     * @return $this
109
     */
110
    public function background($arg = true)
111
    {
112
        $this->background = $arg;
113
        return $this;
114
    }
115
116
    /**
117
     * Stop command if it runs longer then $timeout in seconds
118
     *
119
     * @param int $timeout
120
     *
121
     * @return $this
122
     */
123
    public function timeout($timeout)
124
    {
125
        $this->timeout = $timeout;
126
        return $this;
127
    }
128
129
    /**
130
     * Stops command if it does not output something for a while
131
     *
132
     * @param int $timeout
133
     *
134
     * @return $this
135
     */
136
    public function idleTimeout($timeout)
137
    {
138
        $this->idleTimeout = $timeout;
139
        return $this;
140
    }
141
142
    /**
143
     * Set a single environment variable, or multiple.
144
     */
145
    public function env($env, $value = null)
146
    {
147
        if (!is_array($env)) {
148
            $env = [$env => ($value ? $value : true)];
149
        }
150
        return $this->envVars($env);
151
    }
152
153
    /**
154
     * Sets the environment variables for the command
155
     *
156
     * @param array $env
157
     *
158
     * @return $this
159
     */
160
    public function envVars(array $env)
161
    {
162
        $this->env = $env;
163
        return $this;
164
    }
165
166
    /**
167
     * Pass an input to the process. Can be resource created with fopen() or string
168
     *
169
     * @param resource|string $input
170
     *
171
     * @return $this
172
     */
173
    public function setInput($input)
174
    {
175
        $this->input = $input;
176
        return $this;
177
    }
178
179
    /**
180
     * Attach tty to process for interactive input
181
     *
182
     * @param $interactive bool
183
     *
184
     * @return $this
185
     */
186
    public function interactive($interactive = true)
187
    {
188
        $this->interactive = $interactive;
189
        return $this;
190
    }
191
192
193
    /**
194
     * Is command printing its output to screen
195
     *
196
     * @return bool
197
     */
198
    public function getPrinted()
199
    {
200
        return $this->isPrinted;
201
    }
202
203
    /**
204
     * Changes working directory of command
205
     *
206
     * @param string $dir
207
     *
208
     * @return $this
209
     */
210
    public function dir($dir)
211
    {
212
        $this->workingDirectory = $dir;
213
        return $this;
214
    }
215
216
    /**
217
     * Shortcut for setting isPrinted() and isMetadataPrinted() to false.
218
     *
219
     * @param bool $arg
220
     *
221
     * @return $this
222
     */
223
    public function silent($arg)
224
    {
225
        if (is_bool($arg)) {
226
            $this->isPrinted = !$arg;
227
            $this->isMetadataPrinted = !$arg;
228
        }
229
        return $this;
230
    }
231
232
    /**
233
     * Should command output be printed
234
     *
235
     * @param bool $arg
236
     *
237
     * @return $this
238
     *
239
     * @deprecated
240
     */
241
    public function printed($arg)
242
    {
243
        $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...
244
        return $this->printOutput($arg);
245
    }
246
247
    /**
248
     * Should command output be printed
249
     *
250
     * @param bool $arg
251
     *
252
     * @return $this
253
     */
254
    public function printOutput($arg)
255
    {
256
        if (is_bool($arg)) {
257
            $this->isPrinted = $arg;
258
        }
259
        return $this;
260
    }
261
262
    /**
263
     * Should command metadata be printed. I,e., command and timer.
264
     *
265
     * @param bool $arg
266
     *
267
     * @return $this
268
     */
269
    public function printMetadata($arg)
270
    {
271
        if (is_bool($arg)) {
272
            $this->isMetadataPrinted = $arg;
273
        }
274
        return $this;
275
    }
276
277
    /**
278
     * @param Process $process
279
     * @param callable $output_callback
280
     *
281
     * @return \Robo\ResultData
282
     */
283
    protected function execute($process, $output_callback = null)
284
    {
285
        $this->process = $process;
286
287
        if (!$output_callback) {
288
            $output_callback = function ($type, $buffer) {
289
                $progressWasVisible = $this->hideTaskProgress();
290
                $this->writeMessage($buffer);
291
                $this->showTaskProgress($progressWasVisible);
292
            };
293
        }
294
295
        $this->detectInteractive();
296
297
        if ($this->isMetadataPrinted) {
298
            $this->printAction();
299
        }
300
        $this->process->setTimeout($this->timeout);
301
        $this->process->setIdleTimeout($this->idleTimeout);
302
        $this->process->setWorkingDirectory($this->workingDirectory);
303
304
        if ($this->input) {
305
            $this->process->setInput($this->input);
306
        }
307
308
        if ($this->interactive && $this->isPrinted) {
309
            $this->process->setTty(true);
310
        }
311
312
        if (isset($this->env)) {
313
            $this->process->setEnv($this->env);
314
        }
315
316 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...
317
            $this->startTimer();
318
            $this->process->run();
319
            $this->stopTimer();
320
            $output = rtrim($this->process->getOutput());
321
            return new ResultData(
322
                $this->process->getExitCode(),
323
                $output,
324
                $this->getResultData()
325
            );
326
        }
327
328 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...
329
            $this->startTimer();
330
            $this->process->run($output_callback);
331
            $this->stopTimer();
332
            return new ResultData(
333
                $this->process->getExitCode(),
334
                $this->process->getOutput(),
335
                $this->getResultData()
336
            );
337
        }
338
339
        try {
340
            $this->process->start();
341
        } catch (\Exception $e) {
342
            return new ResultData(
343
                $this->process->getExitCode(),
344
                $e->getMessage(),
345
                $this->getResultData()
346
            );
347
        }
348
        return new ResultData($this->process->getExitCode());
349
    }
350
351
    /**
352
     *
353
     */
354
    protected function stop()
355
    {
356
        if ($this->background && isset($this->process) && $this->process->isRunning()) {
357
            $this->process->stop();
358
            $this->printTaskInfo(
359
                "Stopped {command}",
360
                ['command' => $this->getCommandDescription()]
361
            );
362
        }
363
    }
364
365
    /**
366
     * @param array $context
367
     */
368
    protected function printAction($context = [])
369
    {
370
        $command = $this->getCommandDescription();
371
        $formatted_command = $this->formatCommandDisplay($command);
372
373
        $dir = $this->workingDirectory ? " in {dir}" : "";
374
        $this->printTaskInfo("Running {command}$dir", [
375
                'command' => $formatted_command,
376
                'dir' => $this->workingDirectory
377
            ] + $context);
378
    }
379
380
    /**
381
     * @param $command
382
     *
383
     * @return mixed
384
     */
385
    protected function formatCommandDisplay($command)
386
    {
387
        $formatted_command = str_replace("&&", "&&\n", $command);
388
        $formatted_command = str_replace("||", "||\n", $formatted_command);
389
390
        return $formatted_command;
391
    }
392
393
    /**
394
     * Gets the data array to be passed to Result().
395
     *
396
     * @return array
397
     *   The data array passed to Result().
398
     */
399
    protected function getResultData()
400
    {
401
        if ($this->isMetadataPrinted) {
402
            return ['time' => $this->getExecutionTime()];
403
        }
404
405
        return [];
406
    }
407
}
408