Util::detectCmdLocationWithWhich()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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