Completed
Push — master ( 3164b9...6629c2 )
by Greg
02:30
created

src/Common/ExecCommand.php (1 issue)

Check for loose comparison of strings.

Best Practice Bug Major

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
    use ExecTrait;
15
16
    /**
17
     * @var \Robo\Common\TimeKeeper
18
     */
19
    protected $execTimer;
20
21
    /**
22
     * @return \Robo\Common\TimeKeeper
23
     */
24
    protected function getExecTimer()
25
    {
26
        if (!isset($this->execTimer)) {
27
            $this->execTimer = new TimeKeeper();
28
        }
29
        return $this->execTimer;
30
    }
31
32
    /**
33
     * Look for a "{$cmd}.phar" in the current working
34
     * directory; return a string to exec it if it is
35
     * found.  Otherwise, look for an executable command
36
     * of the same name via findExecutable.
37
     *
38
     * @param string $cmd
39
     *
40
     * @return bool|string
41
     */
42
    protected function findExecutablePhar($cmd)
43
    {
44
        if (file_exists("{$cmd}.phar")) {
45
            return "php {$cmd}.phar";
46
        }
47
        return $this->findExecutable($cmd);
48
    }
49
50
    /**
51
     * Return the best path to the executable program
52
     * with the provided name.  Favor vendor/bin in the
53
     * current project. If not found there, use
54
     * whatever is on the $PATH.
55
     *
56
     * @param string $cmd
57
     *
58
     * @return bool|string
59
     */
60
    protected function findExecutable($cmd)
61
    {
62
        $pathToCmd = $this->searchForExecutable($cmd);
63
        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...
64
            return $this->useCallOnWindows($pathToCmd);
65
        }
66
        return false;
67
    }
68
69
    /**
70
     * @param string $cmd
71
     *
72
     * @return string
73
     */
74
    private function searchForExecutable($cmd)
75
    {
76
        $projectBin = $this->findProjectBin();
77
78
        $localComposerInstallation = $projectBin . DIRECTORY_SEPARATOR . $cmd;
79
        if (file_exists($localComposerInstallation)) {
80
            return $localComposerInstallation;
81
        }
82
        $finder = new ExecutableFinder();
83
        return $finder->find($cmd, null, []);
84
    }
85
86
    /**
87
     * @return bool|string
88
     */
89
    protected function findProjectBin()
90
    {
91
        $cwd = getcwd();
92
        $candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin', $cwd . '/vendor/bin' ];
93
94
        // If this project is inside a vendor directory, give highest priority
95
        // to that directory.
96
        $vendorDirContainingUs = realpath(__DIR__ . '/../../../..');
97
        if (is_dir($vendorDirContainingUs) && (basename($vendorDirContainingUs) == 'vendor')) {
98
            array_unshift($candidates, $vendorDirContainingUs . '/bin');
99
        }
100
101
        foreach ($candidates as $dir) {
102
            if (is_dir("$dir")) {
103
                return realpath($dir);
104
            }
105
        }
106
        return false;
107
    }
108
109
    /**
110
     * Wrap Windows executables in 'call' per 7a88757d
111
     *
112
     * @param string $cmd
113
     *
114
     * @return string
115
     */
116
    protected function useCallOnWindows($cmd)
117
    {
118
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
119
            if (file_exists("{$cmd}.bat")) {
120
                $cmd = "{$cmd}.bat";
121
            }
122
            return "call $cmd";
123
        }
124
        return $cmd;
125
    }
126
127
    protected function getCommandDescription()
128
    {
129
        return $this->process->getCommandLine();
130
    }
131
132
    /**
133
     * @param string $command
134
     *
135
     * @return \Robo\Result
136
     */
137
    protected function executeCommand($command)
138
    {
139
        // TODO: Symfony 4 requires that we supply the working directory.
140
        $result_data = $this->execute(new Process($command, getcwd()));
141
        return new Result(
142
            $this,
143
            $result_data->getExitCode(),
144
            $result_data->getMessage(),
145
            $result_data->getData()
146
        );
147
    }
148
}
149