Completed
Push — master ( fa8877...7435ca )
by Sebastian
02:00
created

Util   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 293
Duplicated Lines 6.83 %

Coupling/Cohesion

Components 2
Dependencies 0

Test Coverage

Coverage 95.7%

Importance

Changes 0
Metric Value
wmc 42
lcom 2
cbo 0
dl 20
loc 293
ccs 89
cts 93
cp 0.957
rs 9.0399
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A detectCmdLocationInPath() 9 9 2
A detectCmdLocationWithWhich() 0 11 2
A detectCmdLocationInPaths() 11 11 3
A getEnvPath() 0 10 3
A getExecutable() 0 14 4
A isAbsolutePath() 0 26 5
A isAbsoluteWindowsPath() 0 4 3
A toAbsolutePath() 0 16 5
A formatWithColor() 0 19 4
A formatWithAsterisk() 0 4 1
A canPipe() 0 4 1
A removeDir() 0 14 5
A detectCmdLocation() 0 30 4

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Util often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Util, and based on these observations, apply Extract Interface, too.

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