Completed
Push — master ( 331704...66c218 )
by Greg
03:18
created

ExecTrait::envVars()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
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 = 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 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
    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
     * Set a single environment variable, or multiple.
140
     */
141
    public function env($env, $value = null)
142
    {
143
        if (!is_array($env)) {
144
            $env = [$env => ($value ? $value : true)];
145
        }
146
        return $this->envVars($env);
147
    }
148
149
    /**
150
     * Sets the environment variables for the command
151
     *
152
     * @param array $env
153
     *
154
     * @return $this
155
     */
156
    public function envVars(array $env)
157
    {
158
        $this->env = $env;
159
        return $this;
160
    }
161
162
    /**
163
     * Pass an input to the process. Can be resource created with fopen() or string
164
     *
165
     * @param resource|string $input
166
     *
167
     * @return $this
168
     */
169
    public function setInput($input)
170
    {
171
        $this->input = $input;
172
        return $this;
173
    }
174
175
    /**
176
     * Attach tty to process for interactive input
177
     *
178
     * @param $interactive bool
179
     *
180
     * @return $this
181
     */
182
    public function interactive($interactive = true)
183
    {
184
        $this->interactive = $interactive;
185
        return $this;
186
    }
187
188
189
    /**
190
     * Is command printing its output to screen
191
     *
192
     * @return bool
193
     */
194
    public function getPrinted()
195
    {
196
        return $this->isPrinted;
197
    }
198
199
    /**
200
     * Changes working directory of command
201
     *
202
     * @param string $dir
203
     *
204
     * @return $this
205
     */
206
    public function dir($dir)
207
    {
208
        $this->workingDirectory = $dir;
209
        return $this;
210
    }
211
212
    /**
213
     * Shortcut for setting isPrinted() and isMetadataPrinted() to false.
214
     *
215
     * @param bool $arg
216
     *
217
     * @return $this
218
     */
219
    public function silent($arg)
220
    {
221
        if (is_bool($arg)) {
222
            $this->isPrinted = !$arg;
223
            $this->isMetadataPrinted = !$arg;
224
        }
225
        return $this;
226
    }
227
228
    /**
229
     * Should command output be printed
230
     *
231
     * @param bool $arg
232
     *
233
     * @return $this
234
     *
235
     * @deprecated
236
     */
237
    public function printed($arg)
238
    {
239
        $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...
240
        return $this->printOutput($arg);
241
    }
242
243
    /**
244
     * Should command output be printed
245
     *
246
     * @param bool $arg
247
     *
248
     * @return $this
249
     */
250
    public function printOutput($arg)
251
    {
252
        if (is_bool($arg)) {
253
            $this->isPrinted = $arg;
254
        }
255
        return $this;
256
    }
257
258
    /**
259
     * Should command metadata be printed. I,e., command and timer.
260
     *
261
     * @param bool $arg
262
     *
263
     * @return $this
264
     */
265
    public function printMetadata($arg)
266
    {
267
        if (is_bool($arg)) {
268
            $this->isMetadataPrinted = $arg;
269
        }
270
        return $this;
271
    }
272
273
    /**
274
     * @param Process $process
275
     * @param callable $output_callback
276
     *
277
     * @return \Robo\ResultData
278
     */
279
    protected function execute($process, $output_callback = null)
280
    {
281
        $this->process = $process;
282
283
        if (!$output_callback) {
284
            $output_callback = function ($type, $buffer) {
285
                $progressWasVisible = $this->hideTaskProgress();
286
                $this->writeMessage($buffer);
287
                $this->showTaskProgress($progressWasVisible);
288
            };
289
        }
290
291
        $this->detectInteractive();
292
293
        if ($this->isMetadataPrinted) {
294
            $this->printAction();
295
        }
296
        $this->process->setTimeout($this->timeout);
297
        $this->process->setIdleTimeout($this->idleTimeout);
298
        $this->process->setWorkingDirectory($this->workingDirectory);
299
300
        if ($this->input) {
301
            $this->process->setInput($this->input);
302
        }
303
304
        if ($this->interactive) {
305
            $this->process->setTty(true);
306
        }
307
308
        if (isset($this->env)) {
309
            $this->process->setEnv($this->env);
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();
315
            $this->stopTimer();
316
            return new ResultData(
317
                $this->process->getExitCode(),
318
                $this->process->getOutput(),
319
                $this->getResultData()
320
            );
321
        }
322
323 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...
324
            $this->startTimer();
325
            $this->process->run($output_callback);
326
            $this->stopTimer();
327
            return new ResultData(
328
                $this->process->getExitCode(),
329
                $this->process->getOutput(),
330
                $this->getResultData()
331
            );
332
        }
333
334
        try {
335
            $this->process->start();
336
        } catch (\Exception $e) {
337
            return new ResultData(
338
                $this->process->getExitCode(),
339
                $e->getMessage(),
340
                $this->getResultData()
341
            );
342
        }
343
        return new ResultData($this->process->getExitCode());
344
    }
345
346
    /**
347
     *
348
     */
349
    protected function stop()
350
    {
351
        if ($this->background && isset($this->process) && $this->process->isRunning()) {
352
            $this->process->stop();
353
            $this->printTaskInfo(
354
                "Stopped {command}",
355
                ['command' => $this->getCommandDescription()]
356
            );
357
        }
358
    }
359
360
    /**
361
     * @param array $context
362
     */
363
    protected function printAction($context = [])
364
    {
365
        $command = $this->getCommandDescription();
366
        $dir = $this->workingDirectory ? " in {dir}" : "";
367
        $this->printTaskInfo("Running {command}$dir", [
368
                'command' => $command,
369
                'dir' => $this->workingDirectory
370
            ] + $context);
371
    }
372
373
    /**
374
     * Gets the data array to be passed to Result().
375
     *
376
     * @return array
377
     *   The data array passed to Result().
378
     */
379
    protected function getResultData()
380
    {
381
        if ($this->isMetadataPrinted) {
382
            return ['time' => $this->getExecutionTime()];
383
        }
384
385
        return [];
386
    }
387
}
388