ExecCommand::executeCommand()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace Robo\Common;
4
5
use Robo\Result;
6
use Symfony\Component\Process\ExecutableFinder;
7
use Symfony\Component\Process\Process;
8
9
/**
10
 * This task is supposed to be executed as shell command.
11
 * You can specify working directory and if output is printed.
12
 */
13
trait ExecCommand
14
{
15
    use ExecTrait;
16
17
    /**
18
     * @var \Robo\Common\TimeKeeper
19
     */
20
    protected $execTimer;
21
22
    /**
23
     * @return \Robo\Common\TimeKeeper
24
     */
25
    protected function getExecTimer()
26
    {
27
        if (!isset($this->execTimer)) {
28
            $this->execTimer = new TimeKeeper();
29
        }
30
        return $this->execTimer;
31
    }
32
33
    /**
34
     * Look for a "{$cmd}.phar" in the current working
35
     * directory; return a string to exec it if it is
36
     * found.  Otherwise, look for an executable command
37
     * of the same name via findExecutable.
38
     *
39
     * @param string $cmd
40
     *
41
     * @return bool|string
42
     */
43
    protected function findExecutablePhar($cmd)
44
    {
45
        if (file_exists("{$cmd}.phar")) {
46
            return "php {$cmd}.phar";
47
        }
48
        return $this->findExecutable($cmd);
49
    }
50
51
    /**
52
     * Return the best path to the executable program
53
     * with the provided name.  Favor vendor/bin in the
54
     * current project. If not found there, use
55
     * whatever is on the $PATH.
56
     *
57
     * @param string $cmd
58
     *
59
     * @return bool|string
60
     */
61
    protected function findExecutable($cmd)
62
    {
63
        $pathToCmd = $this->searchForExecutable($cmd);
64
        if ($pathToCmd) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $pathToCmd of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
65
            return $this->useCallOnWindows($pathToCmd);
66
        }
67
        return false;
68
    }
69
70
    /**
71
     * @param string $cmd
72
     *
73
     * @return string
74
     */
75
    private function searchForExecutable($cmd)
76
    {
77
        $projectBin = $this->findProjectBin();
78
79
        $localComposerInstallation = $projectBin . DIRECTORY_SEPARATOR . $cmd;
80
        if (file_exists($localComposerInstallation)) {
81
            return $localComposerInstallation;
82
        }
83
        $finder = new ExecutableFinder();
84
        return $finder->find($cmd, null, []);
85
    }
86
87
    /**
88
     * @return bool|string
89
     */
90
    protected function findProjectBin()
91
    {
92
        $cwd = getcwd();
93
        $candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin', $cwd . '/vendor/bin' ];
94
95
        // If this project is inside a vendor directory, give highest priority
96
        // to that directory.
97
        $vendorDirContainingUs = realpath(__DIR__ . '/../../../..');
98
        if (is_dir($vendorDirContainingUs) && (basename($vendorDirContainingUs) == 'vendor')) {
99
            array_unshift($candidates, $vendorDirContainingUs . '/bin');
100
        }
101
102
        foreach ($candidates as $dir) {
103
            if (is_dir("$dir")) {
104
                return realpath($dir);
105
            }
106
        }
107
        return false;
108
    }
109
110
    /**
111
     * Wrap Windows executables in 'call' per 7a88757d
112
     *
113
     * @param string $cmd
114
     *
115
     * @return string
116
     */
117
    protected function useCallOnWindows($cmd)
118
    {
119
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
120
            if (file_exists("{$cmd}.bat")) {
121
                $cmd = "{$cmd}.bat";
122
            }
123
            return "call $cmd";
124
        }
125
        return $cmd;
126
    }
127
128
    /**
129
     * {@inheritdoc}
130
     */
131
    protected function getCommandDescription()
132
    {
133
        return $this->process->getCommandLine();
134
    }
135
136
    /**
137
     * @param string $command
138
     *
139
     * @return \Robo\Result
140
     */
141
    protected function executeCommand($command)
142
    {
143
        // TODO: Symfony 4 requires that we supply the working directory.
144
        $result_data = $this->execute(Process::fromShellCommandline($command, getcwd()));
145
        return new Result(
146
            $this,
147
            $result_data->getExitCode(),
148
            $result_data->getMessage(),
149
            $result_data->getData()
150
        );
151
    }
152
}
153