Completed
Push — master ( 14cc2d...e081f2 )
by Greg
9s
created

ExecCommand::printMetadata()   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 $isPrinted = true;
18
19
    /**
20
     * @var bool
21
     */
22
    protected $isMetadataPrinted = true;
23
24
    /**
25
     * @var string
26
     */
27
    protected $workingDirectory;
28
29
    /**
30
     * @var \Robo\Common\TimeKeeper
31
     */
32
    protected $execTimer;
33
34
    /**
35
     * @return \Robo\Common\TimeKeeper
36
     */
37
    protected function getExecTimer()
38
    {
39
        if (!isset($this->execTimer)) {
40
            $this->execTimer = new TimeKeeper();
41
        }
42
        return $this->execTimer;
43
    }
44
45
    /**
46
     * Is command printing its output to screen
47
     *
48
     * @return bool
49
     */
50
    public function getPrinted()
51
    {
52
        return $this->isPrinted;
53
    }
54
55
    /**
56
     * Changes working directory of command
57
     *
58
     * @param string $dir
59
     *
60
     * @return $this
61
     */
62
    public function dir($dir)
63
    {
64
        $this->workingDirectory = $dir;
65
        return $this;
66
    }
67
68
69
    /**
70
     * Shortcut for setting isPrinted() and isMetadataPrinted() to false.
71
     *
72
     * @param bool $arg
73
     *
74
     * @return $this
75
     */
76
    public function silent($arg)
77
    {
78
        if (is_bool($arg)) {
79
            $this->isPrinted = !$arg;
80
            $this->isMetadataPrinted = !$arg;
81
        }
82
        return $this;
83
    }
84
85
    /**
86
     * Should command output be printed
87
     *
88
     * @param bool $arg
89
     *
90
     * @return $this
91
     */
92
    public function printed($arg)
93
    {
94
        $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...
95
        return $this->printOutput($arg);
96
    }
97
98
    /**
99
     * Should command output be printed
100
     *
101
     * @param bool $arg
102
     *
103
     * @return $this
104
     */
105
    public function printOutput($arg)
106
    {
107
        if (is_bool($arg)) {
108
            $this->isPrinted = $arg;
109
        }
110
        return $this;
111
    }
112
113
    /**
114
     * Should command metadata be printed. I,e., command and timer.
115
     *
116
     * @param bool $arg
117
     *
118
     * @return $this
119
     */
120
    public function printMetadata($arg)
121
    {
122
        if (is_bool($arg)) {
123
            $this->isMetadataPrinted = $arg;
124
        }
125
        return $this;
126
    }
127
128
    /**
129
     * Look for a "{$cmd}.phar" in the current working
130
     * directory; return a string to exec it if it is
131
     * found.  Otherwise, look for an executable command
132
     * of the same name via findExecutable.
133
     *
134
     * @param string $cmd
135
     *
136
     * @return bool|string
137
     */
138
    protected function findExecutablePhar($cmd)
139
    {
140
        if (file_exists("{$cmd}.phar")) {
141
            return "php {$cmd}.phar";
142
        }
143
        return $this->findExecutable($cmd);
144
    }
145
146
    /**
147
     * Return the best path to the executable program
148
     * with the provided name.  Favor vendor/bin in the
149
     * current project. If not found there, use
150
     * whatever is on the $PATH.
151
     *
152
     * @param string $cmd
153
     *
154
     * @return bool|string
155
     */
156
    protected function findExecutable($cmd)
157
    {
158
        $pathToCmd = $this->searchForExecutable($cmd);
159
        if ($pathToCmd) {
160
            return $this->useCallOnWindows($pathToCmd);
161
        }
162
        return false;
163
    }
164
165
    /**
166
     * @param string $cmd
167
     *
168
     * @return string
169
     */
170
    private function searchForExecutable($cmd)
171
    {
172
        $projectBin = $this->findProjectBin();
173
174
        $localComposerInstallation = $projectBin . DIRECTORY_SEPARATOR . $cmd;
175
        if (file_exists($localComposerInstallation)) {
176
            return $localComposerInstallation;
177
        }
178
        $finder = new ExecutableFinder();
179
        return $finder->find($cmd, null, []);
180
    }
181
182
    /**
183
     * @return bool|string
184
     */
185
    protected function findProjectBin()
186
    {
187
        $cwd = getcwd();
188
        $candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin', $cwd . '/vendor/bin' ];
189
190
        // If this project is inside a vendor directory, give highest priority
191
        // to that directory.
192
        $vendorDirContainingUs = realpath(__DIR__ . '/../../../..');
193
        if (is_dir($vendorDirContainingUs) && (basename($vendorDirContainingUs) == 'vendor')) {
194
            array_unshift($candidates, $vendorDirContainingUs . '/bin');
195
        }
196
197
        foreach ($candidates as $dir) {
198
            if (is_dir("$dir")) {
199
                return realpath($dir);
200
            }
201
        }
202
        return false;
203
    }
204
205
    /**
206
     * Wrap Windows executables in 'call' per 7a88757d
207
     *
208
     * @param string $cmd
209
     *
210
     * @return string
211
     */
212
    protected function useCallOnWindows($cmd)
213
    {
214
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
215
            if (file_exists("{$cmd}.bat")) {
216
                $cmd = "{$cmd}.bat";
217
            }
218
            return "call $cmd";
219
        }
220
        return $cmd;
221
    }
222
223
    /**
224
     * @param string $command
225
     *
226
     * @return \Robo\Result
227
     */
228
    protected function executeCommand($command)
229
    {
230
        $process = new Process($command);
231
        $process->setTimeout(null);
232
        if ($this->workingDirectory) {
233
            $process->setWorkingDirectory($this->workingDirectory);
234
        }
235
        $this->getExecTimer()->start();
236
        if ($this->isPrinted) {
237
            $process->run(function ($type, $buffer) {
238
                print $buffer;
239
            });
240
        } else {
241
            $process->run();
242
        }
243
        $this->getExecTimer()->stop();
244
245
        return new Result($this, $process->getExitCode(), $process->getOutput(), ['time' => $this->getExecTimer()->elapsed()]);
246
    }
247
}
248