Completed
Pull Request — master (#525)
by
unknown
05:02 queued 01:46
created

ExecCommand::logMetadata()   A

Complexity

Conditions 2
Paths 2

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 2
eloc 4
nc 2
nop 1
1
<?php
2
namespace Robo\Common;
3
4
use Robo\Result;
5
use Symfony\Component\Process\ExecutableFinder;
6
use Symfony\Component\Process\Process;
7
8
/**
9
 * This task is supposed to be executed as shell command.
10
 * You can specify working directory and if output is printed.
11
 */
12
trait ExecCommand
13
{
14
    /**
15
     * @var bool
16
     */
17
    protected $isOutputPrinted = true;
18
19
    /**
20
     * @var bool
21
     */
22
    protected $isOutputLogged = true;
23
24
    /**
25
     * @var bool
26
     */
27
    protected $isMetadataPrinted = true;
28
29
    /**
30
     * @var string
31
     */
32
    protected $workingDirectory;
33
34
    /**
35
     * @var \Robo\Common\TimeKeeper
36
     */
37
    protected $execTimer;
38
39
    /**
40
     * @return \Robo\Common\TimeKeeper
41
     */
42
    protected function getExecTimer()
43
    {
44
        if (!isset($this->execTimer)) {
45
            $this->execTimer = new TimeKeeper();
46
        }
47
        return $this->execTimer;
48
    }
49
50
    /**
51
     * Is command printing its output to screen
52
     *
53
     * @return bool
54
     */
55
    public function getPrinted()
56
    {
57
        return $this->isOutputPrinted;
58
    }
59
60
    /**
61
     * Changes working directory of command
62
     *
63
     * @param string $dir
64
     *
65
     * @return $this
66
     */
67
    public function dir($dir)
68
    {
69
        $this->workingDirectory = $dir;
70
        return $this;
71
    }
72
73
74
    /**
75
     * Shortcut for setting isOutputPrinted, isOutputLogged, isMetadataPrinted.
76
     *
77
     * @param bool $arg
78
     *
79
     * @return $this
80
     */
81
    public function silent($arg)
82
    {
83
        if (is_bool($arg)) {
84
            $this->isOutputPrinted = !$arg;
85
            $this->isOutputLogged = !$arg;
86
            $this->isMetadataPrinted = !$arg;
87
        }
88
        return $this;
89
    }
90
91
    /**
92
     * Should command output be printed
93
     *
94
     * @param bool $arg
95
     *
96
     * @return $this
97
     */
98
    public function printed($arg)
99
    {
100
        $this->logger()->warning("printed() is deprecated. Please use printOutput().");
0 ignored issues
show
Bug introduced by
It seems like logger() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
101
        return $this->printOutput($arg);
102
    }
103
104
    /**
105
     * Should command output be printed
106
     *
107
     * @param bool $arg
108
     *
109
     * @return $this
110
     */
111
    public function printOutput($arg)
112
    {
113
        if (is_bool($arg)) {
114
            $this->isOutputPrinted = $arg;
115
        }
116
        return $this;
117
    }
118
119
    /**
120
     * Should command output be printed
121
     *
122
     * @param bool $arg
123
     *
124
     * @return $this
125
     */
126
    public function logOutput($arg)
127
    {
128
        if (is_bool($arg)) {
129
            $this->isOutputLogged = $arg;
130
        }
131
        return $this;
132
    }
133
134
    /**
135
     * Should command metadata be printed. I,e., command and timer.
136
     *
137
     * @param bool $arg
138
     *
139
     * @return $this
140
     */
141
    public function logMetadata($arg)
142
    {
143
        if (is_bool($arg)) {
144
            $this->isMetadataPrinted = $arg;
145
        }
146
        return $this;
147
    }
148
149
    /**
150
     * Look for a "{$cmd}.phar" in the current working
151
     * directory; return a string to exec it if it is
152
     * found.  Otherwise, look for an executable command
153
     * of the same name via findExecutable.
154
     *
155
     * @param string $cmd
156
     *
157
     * @return bool|string
158
     */
159
    protected function findExecutablePhar($cmd)
160
    {
161
        if (file_exists("{$cmd}.phar")) {
162
            return "php {$cmd}.phar";
163
        }
164
        return $this->findExecutable($cmd);
165
    }
166
167
    /**
168
     * Return the best path to the executable program
169
     * with the provided name.  Favor vendor/bin in the
170
     * current project. If not found there, use
171
     * whatever is on the $PATH.
172
     *
173
     * @param string $cmd
174
     *
175
     * @return bool|string
176
     */
177
    protected function findExecutable($cmd)
178
    {
179
        $pathToCmd = $this->searchForExecutable($cmd);
180
        if ($pathToCmd) {
181
            return $this->useCallOnWindows($pathToCmd);
182
        }
183
        return false;
184
    }
185
186
    /**
187
     * @param string $cmd
188
     *
189
     * @return string
190
     */
191
    private function searchForExecutable($cmd)
192
    {
193
        $projectBin = $this->findProjectBin();
194
195
        $localComposerInstallation = $projectBin . DIRECTORY_SEPARATOR . $cmd;
196
        if (file_exists($localComposerInstallation)) {
197
            return $localComposerInstallation;
198
        }
199
        $finder = new ExecutableFinder();
200
        return $finder->find($cmd, null, []);
201
    }
202
203
    /**
204
     * @return bool|string
205
     */
206
    protected function findProjectBin()
207
    {
208
        $cwd = getcwd();
209
        $candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin', $cwd . '/vendor/bin' ];
210
211
        // If this project is inside a vendor directory, give highest priority
212
        // to that directory.
213
        $vendorDirContainingUs = realpath(__DIR__ . '/../../../..');
214
        if (is_dir($vendorDirContainingUs) && (basename($vendorDirContainingUs) == 'vendor')) {
215
            array_unshift($candidates, $vendorDirContainingUs . '/bin');
216
        }
217
218
        foreach ($candidates as $dir) {
219
            if (is_dir("$dir")) {
220
                return realpath($dir);
221
            }
222
        }
223
        return false;
224
    }
225
226
    /**
227
     * Wrap Windows executables in 'call' per 7a88757d
228
     *
229
     * @param string $cmd
230
     *
231
     * @return string
232
     */
233
    protected function useCallOnWindows($cmd)
234
    {
235
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
236
            if (file_exists("{$cmd}.bat")) {
237
                $cmd = "{$cmd}.bat";
238
            }
239
            return "call $cmd";
240
        }
241
        return $cmd;
242
    }
243
244
    /**
245
     * @param string $command
246
     *
247
     * @return \Robo\Result
248
     */
249
    protected function executeCommand($command)
250
    {
251
        $process = new Process($command);
252
        $process->setTimeout(null);
253
        if ($this->workingDirectory) {
254
            $process->setWorkingDirectory($this->workingDirectory);
255
        }
256
        $this->getExecTimer()->start();
257
        if ($this->isOutputPrinted) {
258
            $process->run(function ($type, $buffer) {
259
                print $buffer;
260
            });
261
        } else {
262
            $process->run();
263
        }
264
        $this->getExecTimer()->stop();
265
266
        return new Result($this, $process->getExitCode(), $process->getOutput(), ['time' => $this->getExecTimer()->elapsed()]);
267
    }
268
}
269