Completed
Push — master ( b4dc89...73a181 )
by Sebastian
02:57
created

Cli::canPipe()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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 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($command, $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 209
    public static function getCommandLocations($command) : array
69
    {
70 209
        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
    public static function detectCmdLocation(string $cmd, string $path = '', array $optionalLocations = []) : string
83
    {
84
        $detectionSteps = [
85 212
            function($cmd) use ($path) {
86 212
                if (!empty($path)) {
87 206
                    return self::detectCmdLocationInPath($cmd, $path);
88
                }
89 6
                return '';
90 212
            },
91 212
            function($cmd) {
92 6
                return self::detectCmdLocationWithWhich($cmd);
93 212
            },
94 212
            function($cmd) {
95 2
                $paths = explode(PATH_SEPARATOR, self::getEnvPath());
96 2
                return self::detectCmdLocationInPaths($cmd, $paths);
97 212
            },
98 212
            function($cmd) use ($optionalLocations) {
99 2
                return self::detectCmdLocationInPaths($cmd, $optionalLocations);
100 212
            }
101
        ];
102
103 212
        foreach ($detectionSteps as $step) {
104 212
            $bin = $step($cmd);
105 211
            if (!empty($bin)) {
106 211
                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 206 View Code Duplication
    public static function detectCmdLocationInPath(string $cmd, string $path) : string
122
    {
123 206
        $command = $path . DIRECTORY_SEPARATOR . $cmd;
124 206
        $bin     = self::isExecutable($command);
125 206
        if (empty($bin)) {
126 1
            throw new RuntimeException(sprintf('wrong path specified for \'%s\': %s', $cmd, $path));
127
        }
128 205
        return $bin;
129
    }
130
131
    /**
132
     * Detect command location using which cli command.
133
     *
134
     * @param  string $cmd
135
     * @return string
136
     */
137 6
    public static function detectCmdLocationWithWhich($cmd) : string
138
    {
139 6
        $bin = '';
140
        // on nx systems use 'which' command.
141 6
        if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
142 6
            $command = trim(`which $cmd`);
143 6
            $bin     = self::isExecutable($command);
144
        }
145 6
        return $bin;
146
147
    }
148
149
    /**
150
     * Check path list for executable command.
151
     *
152
     * @param  string $cmd
153
     * @param  array  $paths
154
     * @return string
155
     */
156 2 View Code Duplication
    public static function detectCmdLocationInPaths($cmd, array $paths) : string
157
    {
158 2
        foreach ($paths as $path) {
159 2
            $command = $path . DIRECTORY_SEPARATOR . $cmd;
160 2
            $bin     = self::isExecutable($command);
161 2
            if (null !== $bin) {
162 2
                return $bin;
163
            }
164
        }
165 1
        return '';
166
    }
167
168
    /**
169
     * Return local $PATH variable.
170
     *
171
     * @return string
172
     * @throws \RuntimeException
173
     */
174 3
    public static function getEnvPath() : string
175
    {
176
        // check for unix and windows case $_SERVER index
177 3
        foreach (['PATH', 'Path', 'path'] as $index) {
178 3
            if (isset($_SERVER[$index])) {
179 3
                return $_SERVER[$index];
180
            }
181
        }
182 1
        throw new RuntimeException('cant find local PATH variable');
183
    }
184
185
    /**
186
     * Returns the executable command if the command is executable, null otherwise.
187
     * Search for $command.exe on Windows systems.
188
     *
189
     * @param  string $command
190
     * @return string
191
     */
192 212
    public static function isExecutable($command) : string
193
    {
194 212
        if (is_executable($command)) {
195 210
            return $command;
196
        }
197
        // on windows check the .exe suffix
198 3
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
199
            $command .= '.exe';
200
            if (is_executable($command)) {
201
                return $command;
202
            }
203
        }
204 3
        return '';
205
    }
206
207
    /**
208
     * Is given path absolute.
209
     *
210
     * @param  string $path
211
     * @return bool
212
     */
213 44
    public static function isAbsolutePath($path) : bool
214
    {
215
        // path already absolute?
216 44
        if ($path[0] === '/') {
217 13
            return true;
218
        }
219
220
        // Matches the following on Windows:
221
        //  - \\NetworkComputer\Path
222
        //  - \\.\D:
223
        //  - \\.\c:
224
        //  - C:\Windows
225
        //  - C:\windows
226
        //  - C:/windows
227
        //  - c:/windows
228 31
        if (defined('PHP_WINDOWS_VERSION_BUILD') && self::isAbsoluteWindowsPath($path)) {
229
            return true;
230
        }
231
232
        // Stream
233 31
        if (strpos($path, '://') !== false) {
234 1
            return true;
235
        }
236
237 30
        return false;
238
    }
239
240
    /**
241
     * Is given path an absolute windows path.
242
     *
243
     * @param  string $path
244
     * @return bool
245
     */
246 3
    public static function isAbsoluteWindowsPath($path) : bool
247
    {
248 3
        return ($path[0] === '\\' || (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))));
249
    }
250
251
    /**
252
     * Converts a path to an absolute one if necessary relative to a given base path.
253
     *
254
     * @param  string  $path
255
     * @param  string  $base
256
     * @param  boolean $useIncludePath
257
     * @return string
258
     */
259 41
    public static function toAbsolutePath(string $path, string $base, bool $useIncludePath = false) : string
260
    {
261 41
        if (self::isAbsolutePath($path)) {
262 12
            return $path;
263
        }
264
265 29
        $file = $base . DIRECTORY_SEPARATOR . $path;
266
267 29
        if ($useIncludePath && !file_exists($file)) {
268 1
            $includePathFile = stream_resolve_include_path($path);
269 1
            if ($includePathFile) {
270 1
                $file = $includePathFile;
271
            }
272
        }
273 29
        return $file;
274
    }
275
276
    /**
277
     * Formats a buffer with a specified ANSI color sequence if colors are enabled.
278
     *
279
     * @author Sebastian Bergmann <[email protected]>
280
     * @param  string $color
281
     * @param  string $buffer
282
     * @return string
283
     */
284 3
    public static function formatWithColor(string $color, string $buffer) : string
285
    {
286 3
        $codes   = array_map('trim', explode(',', $color));
287 3
        $lines   = explode("\n", $buffer);
288 3
        $padding = max(array_map('strlen', $lines));
289
290 3
        $styles = [];
291 3
        foreach ($codes as $code) {
292 3
            $styles[] = self::$ansiCodes[$code];
293
        }
294 3
        $style = sprintf("\x1b[%sm", implode(';', $styles));
295
296 3
        $styledLines = [];
297 3
        foreach ($lines as $line) {
298 3
            $styledLines[] = strlen($line) ? $style . str_pad($line, $padding) . "\x1b[0m" : '';
299
        }
300
301 3
        return implode(PHP_EOL, $styledLines);
302
    }
303
304
    /**
305
     * Fills up a text buffer with '*' to consume 72 chars.
306
     *
307
     * @param  string $buffer
308
     * @param  int    $length
309
     * @return string
310
     */
311 14
    public static function formatWithAsterisk(string $buffer, int $length = 75) : string
312
    {
313 14
        return $buffer . str_repeat('*', $length - strlen($buffer)) . PHP_EOL;
314
    }
315
316
    /**
317
     * Can command pipe operator be used.
318
     *
319
     * @return bool
320
     */
321 3
    public static function canPipe() : bool
322
    {
323 3
        return !defined('PHP_WINDOWS_VERSION_BUILD');
324
    }
325
326
    /**
327
     * Removes a directory that is not empty.
328
     *
329
     * @param string $dir
330
     */
331 1
    public static function removeDir(string $dir)
332
    {
333 1
        foreach (scandir($dir) as $file) {
334 1
            if ('.' === $file || '..' === $file) {
335 1
                continue;
336
            }
337 1
            if (is_dir($dir . '/' . $file)) {
338 1
                self::removeDir($dir . '/' . $file);
339
            } else {
340 1
                unlink($dir . '/' . $file);
341
            }
342
        }
343 1
        rmdir($dir);
344 1
    }
345
}
346