Cli::detectCmdLocation()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 29
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 17
nc 3
nop 3
dl 0
loc 29
ccs 17
cts 17
cp 1
crap 4
rs 9.7
c 0
b 0
f 0
1
<?php
2
namespace phpbu\App\Util;
3
4
use RuntimeException;
5
6
/**
7
 * Cli utility
8
 *
9
 * @package    phpbu
10
 * @subpackage Util
11
 * @author     Sebastian Feldmann <[email protected]>
12
 * @copyright  Sebastian Feldmann <[email protected]>
13
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
14
 * @link       https://phpbu.de/
15
 * @since      Class available since Release 1.0.0
16
 */
17
abstract class Cli
18
{
19
    /**
20
     * List of console color codes
21
     *
22
     * @var array
23
     */
24
    private static $ansiCodes = [
25
        'bold'       => 1,
26
        'fg-black'   => 30,
27
        'fg-red'     => 31,
28
        'fg-green'   => 32,
29
        'fg-yellow'  => 33,
30
        'fg-cyan'    => 36,
31
        'fg-white'   => 37,
32
        'bg-red'     => 41,
33
        'bg-green'   => 42,
34
        'bg-yellow'  => 43
35
    ];
36
37
    /**
38
     * Optional command locations
39
     *
40
     * @var array
41
     */
42
    private static $optionalCommandLocations = [
43
        'mongodump' => [],
44
        'mysqldump' => [
45
            '/usr/local/mysql/bin', // Mac OS X
46
            '/usr/mysql/bin',       // Linux
47
        ],
48
        'tar'       => [],
49
    ];
50
51
    /**
52
     * Adds a new 'path' to the list of optional command locations
53
     *
54
     * @param string $command
55
     * @param string $path
56
     */
57 1
    public static function addCommandLocation(string $command, string $path)
58
    {
59 1
        self::$optionalCommandLocations[$command][] = $path;
60 1
    }
61
62
    /**
63
     * Returns the list of optional 'mysqldump' locations
64
     *
65
     * @param  string $command
66
     * @return array
67
     */
68 257
    public static function getCommandLocations(string $command) : array
69
    {
70 257
        return isset(self::$optionalCommandLocations[$command]) ? self::$optionalCommandLocations[$command] : [];
71
    }
72
73
    /**
74
     * Detect a given command's location
75
     *
76
     * @param  string $cmd               The command to locate
77
     * @param  string $path              Directory where the command should be
78
     * @param  array  $optionalLocations Some fallback locations where to search for the command
79
     * @return string                    Absolute path to detected command including command itself
80
     * @throws \RuntimeException
81
     */
82 260
    public static function detectCmdLocation(string $cmd, string $path = '', array $optionalLocations = []) : string
83
    {
84
        $detectionSteps = [
85
            function ($cmd) use ($path) {
86 260
                if (!empty($path)) {
87 249
                    return self::detectCmdLocationInPath($cmd, $path);
88
                }
89 11
                return '';
90 260
            },
91
            function ($cmd) {
92 11
                return self::detectCmdLocationWithWhich($cmd);
93 260
            },
94
            function ($cmd) {
95 2
                $paths = explode(PATH_SEPARATOR, self::getEnvPath());
96 2
                return self::detectCmdLocationInPaths($cmd, $paths);
97 260
            },
98
            function ($cmd) use ($optionalLocations) {
99 2
                return self::detectCmdLocationInPaths($cmd, $optionalLocations);
100 260
            }
101
        ];
102
103 260
        foreach ($detectionSteps as $step) {
104 260
            $bin = $step($cmd);
105 259
            if (!empty($bin)) {
106 259
                return $bin;
107
            }
108
        }
109
110 1
        throw new RuntimeException(sprintf('\'%s\' was nowhere to be found please specify the correct path', $cmd));
111
    }
112
113
    /**
114
     * Detect a command in a given path
115
     *
116
     * @param  string $cmd
117
     * @param  string $path
118
     * @return string
119
     * @throws \RuntimeException
120
     */
121 249
    public static function detectCmdLocationInPath(string $cmd, string $path) : string
122
    {
123 249
        $command = $path . DIRECTORY_SEPARATOR . $cmd;
124 249
        $bin     = self::isExecutable($command);
125 249
        if (empty($bin)) {
126 1
            throw new RuntimeException(sprintf('wrong path specified for \'%s\': %s', $cmd, $path));
127
        }
128 248
        return $bin;
129
    }
130
131
    /**
132
     * Detect command location using which cli command
133
     *
134
     * @param  string $cmd
135
     * @return string
136
     */
137 11
    public static function detectCmdLocationWithWhich(string $cmd) : string
138
    {
139 11
        $bin = '';
140
        // on nx systems use 'which' command.
141 11
        if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
142 11
            $command = trim(`which $cmd`);
143 11
            $bin     = self::isExecutable($command);
144
        }
145 11
        return $bin;
146
    }
147
148
    /**
149
     * Check path list for executable command
150
     *
151
     * @param  string $cmd
152
     * @param  array  $paths
153
     * @return string
154
     */
155 2
    public static function detectCmdLocationInPaths(string $cmd, array $paths) : string
156
    {
157 2
        foreach ($paths as $path) {
158 2
            $command = $path . DIRECTORY_SEPARATOR . $cmd;
159 2
            $bin     = self::isExecutable($command);
160 2
            if (!empty($bin)) {
161 2
                return $bin;
162
            }
163
        }
164 2
        return '';
165
    }
166
167
    /**
168
     * Return local $PATH variable
169
     *
170
     * @return string
171
     * @throws \RuntimeException
172
     */
173 3
    public static function getEnvPath() : string
174
    {
175
        // check for unix and windows case $_SERVER index
176 3
        foreach (['PATH', 'Path', 'path'] as $index) {
177 3
            if (isset($_SERVER[$index])) {
178 3
                return $_SERVER[$index];
179
            }
180
        }
181 1
        throw new RuntimeException('cant find local PATH variable');
182
    }
183
184
    /**
185
     * Returns the executable command if the command is executable, null otherwise
186
     * Search for $command.exe on Windows systems.
187
     *
188
     * @param  string $command
189
     * @return string
190
     */
191 260
    public static function isExecutable(string $command) : string
192
    {
193 260
        if (is_executable($command)) {
194 258
            return $command;
195
        }
196
        // on windows check the .exe suffix
197 3
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
198
            $command .= '.exe';
199
            if (is_executable($command)) {
200
                return $command;
201
            }
202
        }
203 3
        return '';
204
    }
205
206
    /**
207
     * Formats a buffer with a specified ANSI color sequence if colors are enabled
208
     *
209
     * @author Sebastian Bergmann <[email protected]>
210
     * @param  string $color
211
     * @param  string $buffer
212
     * @return string
213
     */
214 3
    public static function formatWithColor(string $color, string $buffer) : string
215
    {
216 3
        $codes   = array_map('trim', explode(',', $color));
217 3
        $lines   = explode("\n", $buffer);
218 3
        $padding = max(array_map('strlen', $lines));
219
220 3
        $styles = [];
221 3
        foreach ($codes as $code) {
222 3
            $styles[] = self::$ansiCodes[$code];
223
        }
224 3
        $style = sprintf("\x1b[%sm", implode(';', $styles));
225
226 3
        $styledLines = [];
227 3
        foreach ($lines as $line) {
228 3
            $styledLines[] = strlen($line) ? $style . str_pad($line, $padding) . "\x1b[0m" : '';
229
        }
230
231 3
        return implode(PHP_EOL, $styledLines);
232
    }
233
234
    /**
235
     * Fills up a text buffer with '*' to consume 72 chars
236
     *
237
     * @param  string $buffer
238
     * @param  int    $length
239
     * @return string
240
     */
241 14
    public static function formatWithAsterisk(string $buffer, int $length = 75) : string
242
    {
243 14
        return $buffer . str_repeat('*', $length - strlen($buffer)) . PHP_EOL;
244
    }
245
246
    /**
247
     * Can command pipe operator be used
248
     *
249
     * @return bool
250
     */
251 3
    public static function canPipe() : bool
252
    {
253 3
        return !defined('PHP_WINDOWS_VERSION_BUILD');
254
    }
255
256
    /**
257
     * Removes a directory that is not empty
258
     *
259
     * @param string $dir
260
     */
261 1
    public static function removeDir(string $dir)
262
    {
263 1
        foreach (scandir($dir) as $file) {
264 1
            if ('.' === $file || '..' === $file) {
265 1
                continue;
266
            }
267 1
            if (is_dir($dir . '/' . $file)) {
268 1
                self::removeDir($dir . '/' . $file);
269
            } else {
270 1
                unlink($dir . '/' . $file);
271
            }
272
        }
273 1
        rmdir($dir);
274 1
    }
275
}
276