Completed
Push — master ( 4a1015...aba34d )
by Sebastian
03:04
created

Cli::detectCmdLocationInPaths()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 11
ccs 2
cts 2
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 2
crap 3
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       http://phpbu.de/
15
 * @since      Class available since Release 1.0.0
16
 */
17
abstract class Cli
18
{
19
    /**
20
     * List of paths
21
     *
22
     * @var array
23
     */
24
    private static $basePaths = [];
25
26
    /**
27
     * List of console color codes.
28
     *
29
     * @var array
30
     */
31
    private static $ansiCodes = [
32
        'bold'       => 1,
33
        'fg-black'   => 30,
34
        'fg-red'     => 31,
35
        'fg-yellow'  => 33,
36
        'fg-cyan'    => 36,
37
        'fg-white'   => 37,
38
        'bg-red'     => 41,
39
        'bg-green'   => 42,
40
        'bg-yellow'  => 43
41
    ];
42
43
    /**
44
     * Optional command locations
45
     *
46
     * @var array
47 3
     */
48
    private static $optionalCommandLocations = [
49 3
        'mongodump' => [],
50 1
        'mysqldump' => [
51
            '/usr/local/mysql/bin/mysqldump', // Mac OS X
52 2
            '/usr/mysql/bin/mysqldump',       // Linux
53 2
        ],
54
        'tar'       => [],
55
    ];
56
57
    /**
58
     * Register a base path.
59
     *
60
     * @param  string $name
61
     * @param  string $path
62 1
     * @throws \RuntimeException
63
     */
64 1
    public static function registerBase($name, $path)
65 1
    {
66
        if (!self::isAbsolutePath($path)) {
67
            throw new RuntimeException(sprintf('path has to be absolute: %s', $path));
68
        }
69
        self::$basePaths[$name] = $path;
70
    }
71
72
73 125
    /**
74
     * Adds a new 'path' to the list of optional command locations.
75 125
     *
76
     * @param string $command
77
     * @param string $path
78
     */
79
    public static function addCommandLocation($command, $path)
80
    {
81
        self::$optionalCommandLocations[$command][] = $path;
82
    }
83
84
    /**
85 3
     * Returns the list of optional 'mysqldump' locations.
86
     *
87 3
     * @param  string $command
88 1
     * @return array
89
     */
90 2
    public static function getCommandLocations($command)
91
    {
92
        return isset(self::$optionalCommandLocations[$command]) ? self::$optionalCommandLocations[$command] : [];
93
    }
94
95
    /**
96
     * Retrieve a registered path.
97
     *
98
     * @param  string $name
99
     * @return string array
100
     * @throws \RuntimeException
101
     */
102 128
    public static function getBase($name)
103
    {
104
        if (!isset(self::$basePaths[$name])) {
105 128
            throw new RuntimeException(sprintf('base not registered: %s', $name));
106 120
        }
107 120
        return self::$basePaths[$name];
108 120
    }
109 1
110
    /**
111 119
     * Detect a given command's location.
112
     *
113
     * @param  string $cmd               The command to locate
114
     * @param  string $path              Directory where the command should be
115 8
     * @param  array  $optionalLocations Some fallback locations where to search for the command
116 8
     * @return string                    Absolute path to detected command including command itself
117 8
     * @throws \RuntimeException
118 8
     */
119
    public static function detectCmdLocation($cmd, $path = null, $optionalLocations = [])
120
    {
121 8
        $detectionSteps = [
122
            function($cmd) use ($path) {
123
                if (null !== $path) {
124 8
                    return self::detectCmdLocationInPath($cmd, $path);
125 8
                }
126 8
                return null;
127 8
            },
128 8
            function($cmd) {
129 6
                return self::detectCmdLocationWithWhich($cmd);
130
            },
131 8
            function($cmd) {
132
                $paths = explode(PATH_SEPARATOR, self::getEnvPath());
133
                return self::detectCmdLocationInPaths($cmd, $paths);
134 2
            },
135 1
            function($cmd) use ($optionalLocations) {
136 1
                return self::detectCmdLocationInPaths($cmd, $optionalLocations);
137 1
            }
138 1
        ];
139
140 1
        foreach ($detectionSteps as $step) {
141 1
            $bin = $step($cmd);
142
            if (null !== $bin) {
143
                return $bin;
144
            }
145
        }
146
147
        throw new RuntimeException(sprintf('\'%s\' was nowhere to be found please specify the correct path', $cmd));
148
    }
149
150 9
    /**
151
     * Detect a command in a given path.
152
     *
153 9
     * @param  string $cmd
154 9
     * @param  string $path
155 8
     * @return string
156
     */
157 1
    public static function detectCmdLocationInPath($cmd, $path)
158 1
    {
159
        $command = $path . DIRECTORY_SEPARATOR . $cmd;
160
        $bin     = self::isExecutable($command);
161
        if (null === $bin) {
162
            throw new RuntimeException(sprintf('wrong path specified for \'%s\': %s', $cmd, $path));
163
        }
164
        return $bin;
165
    }
166
167
    /**
168 128
     * Detect command location using which cli command.
169
     *
170 128
     * @param  string $cmd
171 126
     * @return null|string
172
     */
173
    public static function detectCmdLocationWithWhich($cmd)
174 9
    {
175
        $bin = null;
176
        // on nx systems use 'which' command.
177
        if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
178
            $command = `which $cmd`;
179
            $bin     = self::isExecutable($command);
180 9
        }
181
        return $bin;
182
183
    }
184
185
    /**
186
     * Check path list for executable command.
187
     *
188
     * @param string $cmd
189 21
     * @param array  $paths
190
     * @return null|string
191
     */
192 21
    public static function detectCmdLocationInPaths($cmd, array $paths)
193 4
    {
194
        foreach ($paths as $path) {
195
            $command = $path . DIRECTORY_SEPARATOR . $cmd;
196
            $bin     = self::isExecutable($command);
197
            if (null !== $bin) {
198
                return $bin;
199
            }
200
        }
201
        return null;
202
    }
203
204 17
    /**
205
     * Return local $PATH variable.
206
     *
207
     * @return string
208
     * @throws \RuntimeException
209 17
     */
210 1
    public static function getEnvPath()
211
    {
212
        // check for unix and windows case $_SERVER index
213 16
        foreach (['PATH', 'Path', 'path'] as $index) {
214
            if (isset($_SERVER[$index])) {
215
                return $_SERVER[$index];
216
            }
217
        }
218
        throw new RuntimeException('cant find local PATH variable');
219
    }
220
221
    /**
222 3
     * Returns the executable command if the command is executable, null otherwise.
223
     * Search for $command.exe on Windows systems.
224 3
     *
225
     * @param  string $command
226
     * @return string
227
     */
228
    public static function isExecutable($command)
229
    {
230
        if (is_executable($command)) {
231
            return $command;
232
        }
233
        // on windows check the .exe suffix
234
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
235 17
            $command .= '.exe';
236
            if (is_executable($command)) {
237 17
                return $command;
238 3
            }
239
        }
240
        return null;
241 14
    }
242
243 14
    /**
244 1
     * Is given path absolute.
245 1
     *
246 1
     * @param  string $path
247 1
     * @return boolean
248 1
     */
249 14
    public static function isAbsolutePath($path)
250
    {
251
        // path already absolute?
252
        if ($path[0] === '/') {
253
            return true;
254
        }
255
256
        // Matches the following on Windows:
257 1
        //  - \\NetworkComputer\Path
258
        //  - \\.\D:
259 1
        //  - \\.\c:
260 1
        //  - C:\Windows
261 1
        //  - C:\windows
262
        //  - C:/windows
263 1
        //  - c:/windows
264 1
        if (defined('PHP_WINDOWS_VERSION_BUILD') && self::isAbsoluteWindowsPath($path)) {
265 1
            return true;
266 1
        }
267
268 1
        // Stream
269 1
        if (strpos($path, '://') !== false) {
270 1
            return true;
271
        }
272
273
        return false;
274
    }
275
276
    /**
277
     * Is given path an absolute windows path.
278
     *
279
     * @param  string $path
280
     * @return bool
281
     */
282
    public static function isAbsoluteWindowsPath($path)
283
    {
284
        return ($path[0] === '\\' || (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))));
285
    }
286
287
    /**
288
     * Converts a path to an absolute one if necessary relative to a given base path.
289
     *
290
     * @param  string  $path
291
     * @param  string  $base
292
     * @param  boolean $useIncludePath
293
     * @return string
294
     */
295
    public static function toAbsolutePath($path, $base, $useIncludePath = false)
296
    {
297
        if (self::isAbsolutePath($path)) {
298
            return $path;
299
        }
300
301
        $file = $base . DIRECTORY_SEPARATOR . $path;
302
303
        if ($useIncludePath && !file_exists($file)) {
304
            $includePathFile = stream_resolve_include_path($path);
305
            if ($includePathFile) {
306
                $file = $includePathFile;
307
            }
308
        }
309
        return $file;
310
    }
311
312
    /**
313
     * Formats a buffer with a specified ANSI color sequence if colors are enabled.
314
     *
315
     * @author Sebastian Bergmann <[email protected]>
316
     * @param  string $color
317
     * @param  string $buffer
318
     * @return string
319
     */
320
    public static function formatWithColor($color, $buffer)
321
    {
322
        $codes   = array_map('trim', explode(',', $color));
323
        $lines   = explode("\n", $buffer);
324
        $padding = max(array_map('strlen', $lines));
325
326
        $styles = [];
327
        foreach ($codes as $code) {
328
            $styles[] = self::$ansiCodes[$code];
329
        }
330
        $style = sprintf("\x1b[%sm", implode(';', $styles));
331
332
        $styledLines = [];
333
        foreach ($lines as $line) {
334
            $styledLines[] = strlen($line) ? $style . str_pad($line, $padding) . "\x1b[0m" : '';
335
        }
336
337
        return implode(PHP_EOL, $styledLines);
338
    }
339
340
    /**
341
     * Fills up a text buffer with '*' to consume 72 chars.
342
     *
343
     * @param  string $buffer
344
     * @param  int    $length
345
     * @return string
346
     */
347
    public static function formatWithAsterisk($buffer, $length = 75)
348
    {
349
        return $buffer . str_repeat('*', $length - strlen($buffer)) . PHP_EOL;
350
    }
351
352
    /**
353
     * Removes a directory that is not empty.
354
     *
355
     * @param $dir
356
     */
357
    public static function removeDir($dir)
358
    {
359
        foreach (scandir($dir) as $file) {
360
            if ('.' === $file || '..' === $file) {
361
                continue;
362
            }
363
            if (is_dir($dir . '/' . $file)) {
364
                self::removeDir($dir . '/' . $file);
365
            } else {
366
                unlink($dir . '/' . $file);
367
            }
368
        }
369
        rmdir($dir);
370
    }
371
}
372