Completed
Push — master ( ac786e...a9b395 )
by Sebastian
09:39
created

Cli   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 312
Duplicated Lines 4.49 %

Coupling/Cohesion

Components 3
Dependencies 0

Test Coverage

Coverage 93.18%

Importance

Changes 7
Bugs 1 Features 3
Metric Value
wmc 46
c 7
b 1
f 3
lcom 3
cbo 0
dl 14
loc 312
ccs 82
cts 88
cp 0.9318
rs 8.3999

13 Methods

Rating   Name   Duplication   Size   Complexity  
A registerBase() 0 7 2
A addCommandLocation() 0 4 1
A getCommandLocations() 0 4 2
A getBase() 0 7 2
D detectCmdLocation() 14 41 9
A getEnvPath() 0 10 3
A isExecutable() 0 14 4
B isAbsolutePath() 0 26 5
A isAbsoluteWindowsPath() 0 4 3
B toAbsolutePath() 0 16 5
A formatWithColor() 0 19 4
A formatWithAsterisk() 0 4 1
B removeDir() 0 14 5

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 Cli 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 Cli, and based on these observations, apply Extract Interface, too.

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 commands location.
112
     *
113
     * @param  string $cmd               The command to be located
114
     * @param  string $path              Directory where the command should be located
115 8
     * @param  array  $optionalLocations Some fallback locations where to investigate
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
        // explicit path given, so check it out
122
        if (null !== $path) {
123
            $command = $path . DIRECTORY_SEPARATOR . $cmd;
124 8
            $bin     = self::isExecutable($command);
125 8
            if (null === $bin) {
126 8
                throw new RuntimeException(sprintf('wrong path specified for \'%s\': %s', $cmd, $path));
127 8
            }
128 8
            return $bin;
129 6
        }
130
131 8
        // on nx systems use 'which' command.
132
        if (!defined('PHP_WINDOWS_VERSION_BUILD')) {
133
            $command = `which $cmd`;
134 2
            $bin     = self::isExecutable($command);
135 1
            if (null !== $bin) {
136 1
                return $bin;
137 1
            }
138 1
        }
139
140 1
        // checking environment variable.
141 1
        $pathList = explode(PATH_SEPARATOR, self::getEnvPath());
142 View Code Duplication
        foreach ($pathList as $path) {
143
            $command = $path . DIRECTORY_SEPARATOR . $cmd;
144
            $bin     = self::isExecutable($command);
145
            if (null !== $bin) {
146
                return $bin;
147
            }
148
        }
149
150 9
        // some more paths we came across that where added manually
151 View Code Duplication
        foreach ($optionalLocations as $path) {
152
            $command = $path . DIRECTORY_SEPARATOR . $cmd;
153 9
            $bin     = self::isExecutable($command);
154 9
            if (null !== $bin) {
155 8
                return $bin;
156
            }
157 1
        }
158 1
        throw new RuntimeException(sprintf('\'%s\' was nowhere to be found please specify the correct path', $cmd));
159
    }
160
161
    /**
162
     * Return local $PATH variable.
163
     *
164
     * @return string
165
     * @throws \RuntimeException
166
     */
167
    public static function getEnvPath()
168 128
    {
169
        // check for unix and windows case $_SERVER index
170 128
        foreach (['PATH', 'Path', 'path'] as $index) {
171 126
            if (isset($_SERVER[$index])) {
172
                return $_SERVER[$index];
173
            }
174 9
        }
175
        throw new RuntimeException('cant find local PATH variable');
176
    }
177
178
    /**
179
     * Returns the executable command if the command is executable, null otherwise.
180 9
     * Search for $command.exe on Windows systems.
181
     *
182
     * @param  string $command
183
     * @return string
184
     */
185
    public static function isExecutable($command)
186
    {
187
        if (is_executable($command)) {
188
            return $command;
189 21
        }
190
        // on windows check the .exe suffix
191
        if (defined('PHP_WINDOWS_VERSION_BUILD')) {
192 21
            $command .= '.exe';
193 4
            if (is_executable($command)) {
194
                return $command;
195
            }
196
        }
197
        return null;
198
    }
199
200
    /**
201
     * Is given path absolute.
202
     *
203
     * @param  string $path
204 17
     * @return boolean
205
     */
206
    public static function isAbsolutePath($path)
207
    {
208
        // path already absolute?
209 17
        if ($path[0] === '/') {
210 1
            return true;
211
        }
212
213 16
        // Matches the following on Windows:
214
        //  - \\NetworkComputer\Path
215
        //  - \\.\D:
216
        //  - \\.\c:
217
        //  - C:\Windows
218
        //  - C:\windows
219
        //  - C:/windows
220
        //  - c:/windows
221
        if (defined('PHP_WINDOWS_VERSION_BUILD') && self::isAbsoluteWindowsPath($path)) {
222 3
            return true;
223
        }
224 3
225
        // Stream
226
        if (strpos($path, '://') !== false) {
227
            return true;
228
        }
229
230
        return false;
231
    }
232
233
    /**
234
     * Is given path an absolute windows path.
235 17
     *
236
     * @param  string $path
237 17
     * @return bool
238 3
     */
239
    public static function isAbsoluteWindowsPath($path)
240
    {
241 14
        return ($path[0] === '\\' || (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))));
242
    }
243 14
244 1
    /**
245 1
     * Converts a path to an absolute one if necessary relative to a given base path.
246 1
     *
247 1
     * @param  string  $path
248 1
     * @param  string  $base
249 14
     * @param  boolean $useIncludePath
250
     * @return string
251
     */
252
    public static function toAbsolutePath($path, $base, $useIncludePath = false)
253
    {
254
        if (self::isAbsolutePath($path)) {
255
            return $path;
256
        }
257 1
258
        $file = $base . DIRECTORY_SEPARATOR . $path;
259 1
260 1
        if ($useIncludePath && !file_exists($file)) {
261 1
            $includePathFile = stream_resolve_include_path($path);
262
            if ($includePathFile) {
263 1
                $file = $includePathFile;
264 1
            }
265 1
        }
266 1
        return $file;
267
    }
268 1
269 1
    /**
270 1
     * Formats a buffer with a specified ANSI color sequence if colors are enabled.
271
     *
272
     * @author Sebastian Bergmann <[email protected]>
273
     * @param  string $color
274
     * @param  string $buffer
275
     * @return string
276
     */
277
    public static function formatWithColor($color, $buffer)
278
    {
279
        $codes   = array_map('trim', explode(',', $color));
280
        $lines   = explode("\n", $buffer);
281
        $padding = max(array_map('strlen', $lines));
282
283
        $styles = [];
284
        foreach ($codes as $code) {
285
            $styles[] = self::$ansiCodes[$code];
286
        }
287
        $style = sprintf("\x1b[%sm", implode(';', $styles));
288
289
        $styledLines = [];
290
        foreach ($lines as $line) {
291
            $styledLines[] = strlen($line) ? $style . str_pad($line, $padding) . "\x1b[0m" : '';
292
        }
293
294
        return implode(PHP_EOL, $styledLines);
295
    }
296
297
    /**
298
     * Fills up a text buffer with '*' to consume 72 chars.
299
     *
300
     * @param  string $buffer
301
     * @param  int    $length
302
     * @return string
303
     */
304
    public static function formatWithAsterisk($buffer, $length = 75)
305
    {
306
        return $buffer . str_repeat('*', $length - strlen($buffer)) . PHP_EOL;
307
    }
308
309
    /**
310
     * Removes a directory that is not empty.
311
     *
312
     * @param $dir
313
     */
314
    public static function removeDir($dir)
315
    {
316
        foreach (scandir($dir) as $file) {
317
            if ('.' === $file || '..' === $file) {
318
                continue;
319
            }
320
            if (is_dir($dir . '/' . $file)) {
321
                self::removeDir($dir . '/' . $file);
322
            } else {
323
                unlink($dir . '/' . $file);
324
            }
325
        }
326
        rmdir($dir);
327
    }
328
}
329