Completed
Push — master ( 3b50e0...d26cb9 )
by Lorenzo
01:59
created

DirHelper   D

Complexity

Total Complexity 105

Size/Duplication

Total Lines 556
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 1

Importance

Changes 0
Metric Value
wmc 105
lcom 2
cbo 1
dl 0
loc 556
rs 4.8717
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A isDirSafe() 0 8 2
A checkDirExistOrCreate() 0 13 4
A addFinalSlash() 0 11 3
A addFinalSlashToAllPaths() 0 8 2
A endsWithSlash() 0 4 1
A endsWithStar() 0 4 1
B endsWith() 0 13 6
A startsWithSlash() 0 4 1
B startsWith() 0 13 5
B findDirs() 0 18 8
C delete() 0 28 8
A removeFinalSlash() 0 7 2
A removeFinalSlashToAllPaths() 0 8 2
A removeStartSlash() 0 7 2
A removeStartSlashToAllPaths() 0 8 2
C copy() 0 41 11
A isLocal() 0 4 3
A isAbsoluteUnix() 0 4 2
A isAbsoluteWindows() 0 11 3
B isAbsoluteWindowsRoot() 0 14 7
A isAbsolute() 0 4 2
A isRelative() 0 4 1
B join() 0 18 6
A njoin() 0 4 1
A canonicalize() 0 17 3
B collapseDotFolder() 0 18 7
C split() 0 24 10

How to fix   Complexity   

Complex Class

Complex classes like DirHelper 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 DirHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Padosoft\Io;
4
5
/**
6
 * Helper Class DirHelper
7
 * @package Padosoft\Io
8
 */
9
class DirHelper
10
{
11
    /**
12
     * Check if passed path exists or not.
13
     * @param string $filePath
14
     * @return bool
15
     */
16
    public static function isDirSafe(string $filePath) : bool
17
    {
18
        if (!$filePath) {
19
            return false;
20
        }
21
22
        return is_dir($filePath);
23
    }
24
25
    /**
26
     * Check if passed path exists or try to create it.
27
     * Return false if it fails to create it or if a file (and not a dir) passed as argument.
28
     * @param string $filePath
29
     * @param string $modeMask default '0755'
30
     * @return bool
31
     */
32
    public static function checkDirExistOrCreate(string $filePath, string $modeMask = '0755') : bool
33
    {
34
        if (self::isDirSafe($filePath)) {
35
            return true;
36
        }
37
38
        //controllo adesso che non sia un file
39
        if (FileHelper::fileExistsSafe($filePath)) {
40
            return false;
41
        }
42
43
        return mkdir($filePath, $modeMask, true) && self::isDirSafe($filePath);
44
    }
45
46
    /**
47
     * If dir passed, check if finishes with '/' otherwise append a slash to path.
48
     * If wrong or empty string passed, return '/'.
49
     * @param string $path
50
     * @return string
51
     */
52
    public static function addFinalSlash(string $path) : string
53
    {
54
        if ($path === null || $path == '') {
55
            return '/';
56
        }
57
58
        $quoted = preg_quote('/', '/');
59
        $path = preg_replace('/(?:' . $quoted . ')+$/', '', $path) . '/';
60
61
        return $path;
62
    }
63
64
    /**
65
     * for each dir passed in array, check if it finishes with '/' otherwise append a slash to path.
66
     * If not dir, leave the element untouched.
67
     * @param array $paths
68
     * @return array
69
     */
70
    public static function addFinalSlashToAllPaths(array $paths) : array
71
    {
72
        if (empty($paths)) {
73
            return [];
74
        }
75
76
        return array_map('self::addFinalSlash', $paths);
77
    }
78
79
    /**
80
     * Check if path ends with slash '/'
81
     * @param string $paths
82
     * @return bool
83
     */
84
    public static function endsWithSlash(string $paths) : bool
85
    {
86
        return self::endsWith($paths, '/');
87
    }
88
89
    /**
90
     * Check if path ends with star '*'
91
     * @param string $paths
92
     * @return bool
93
     */
94
    public static function endsWithStar(string $paths) : bool
95
    {
96
        return self::endsWith($paths, '*');
97
    }
98
99
    /**
100
     * Check if path ends with $needle
101
     * @param string $paths
102
     * @param string $needle
103
     * @return bool
104
     */
105
    public static function endsWith(string $paths, string $needle) : bool
106
    {
107
        if ($paths === null || $paths == '') {
108
            return false;
109
        }
110
        if ($needle === null || $needle == '') {
111
            return false;
112
        }
113
114
        // search forward starting from end minus needle length characters
115
        // see: http://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
116
        return (($temp = strlen($paths) - strlen($needle)) >= 0 && strpos($paths, $needle, $temp) !== false);
117
    }
118
119
    /**
120
     * Check if path starts with slash '/'
121
     * @param string $paths
122
     * @return bool
123
     */
124
    public static function startsWithSlash(string $paths) : bool
125
    {
126
        return self::startsWith($paths, '/');
127
    }
128
129
    /**
130
     * Check if path starts with slash $needle
131
     * @param string $paths
132
     * @param string $needle
133
     * @return bool
134
     */
135
    public static function startsWith(string $paths, string $needle) : bool
136
    {
137
        if ($paths === null || $paths == '') {
138
            return false;
139
        }
140
        if ($needle === null || $needle == '') {
141
            return false;
142
        }
143
144
        // search backwards starting from haystack length characters from the end
145
        // see: http://stackoverflow.com/questions/834303/startswith-and-endswith-functions-in-php
146
        return strrpos($paths, $needle, -strlen($paths)) !== false;
147
    }
148
149
    /**
150
     * Find dirs matching a pattern (recursive with subdirs).
151
     * Returns an array containing the matched dirs (full path and not files),
152
     * an empty array if no dir matched or on error.
153
     * @param string $pathPattern if is null it set to base_path()/* if exists otherwise __DIR__/*. It support glob() string pattern.
154
     * @return array of dirs
155
     */
156
    public static function findDirs(string $pathPattern)
157
    {
158
        if (($pathPattern === null || $pathPattern == '') && function_exists('base_path')) {
159
            $pathPattern = base_path() . '/*';
160
        } elseif ($pathPattern === null || $pathPattern == '') {
161
            $pathPattern = __DIR__ . '/*';
162
        } elseif (!self::endsWithStar($pathPattern)) {
163
            $pathPattern = DirHelper::addFinalSlash($pathPattern);
164
        }
165
166
        $files = glob($pathPattern, GLOB_ONLYDIR);
167
168
        foreach (glob(dirname($pathPattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
169
            $files = array_merge($files, self::findDirs($dir . '/' . basename($pathPattern)));
170
        }
171
172
        return $files;
173
    }
174
175
    /**
176
     * Dir::Delete()
177
     * get a dir path and remove all files and subdir in this dir.
178
     * if $not_remove_dir==TRUE then when finish DO NOT REMOVE THE $directory dir.
179
     * @param string $directory directory to empty
180
     * @param bool $not_remove_dir TRUE if DO NOT REMOVE THE $directory dir but only files.
181
     * @return bool true if success, otherwise false
182
     **/
183
    public static function delete($directory, bool $not_remove_dir = false) : bool
184
    {
185
        $directory = self::removeFinalSlash($directory);
186
187
        if (!self::isDirSafe($directory) || !is_readable($directory)) {
188
            return false;
189
        }
190
191
        $directoryHandle = opendir($directory);
192
        while (false !== ($contents = readdir($directoryHandle))) {
193
            if ($contents == '.' || $contents == '..') {
194
                continue;
195
            }
196
            $path = $directory . "/" . $contents;
197
198
            if (is_dir($path)) {
199
                self::delete($path, $not_remove_dir);
200
            } else {
201
                unlink($path);
202
            }
203
        }
204
        closedir($directoryHandle);
205
206
        if (!$not_remove_dir) {
207
            return true;
208
        }
209
        return rmdir($directory);
210
    }
211
212
    /**
213
     * Remove final slash ('/') char in dir if ends with slash.
214
     * @param $directory
215
     * @return string
216
     */
217
    public static function removeFinalSlash($directory) : string
218
    {
219
        if (self::endsWithSlash($directory)) {
220
            $directory = substr($directory, 0, -1);
221
        }
222
        return $directory;
223
    }
224
225
    /**
226
     * For each dir passed in array, check if not finishes with '/' otherwise remove a slash to path.
227
     * If not dir, leave the element untouched.
228
     * @param array $paths
229
     * @return array
230
     */
231
    public static function removeFinalSlashToAllPaths(array $paths) : array
232
    {
233
        if (empty($paths)) {
234
            return [];
235
        }
236
237
        return array_map('self::removeFinalSlash', $paths);
238
    }
239
240
    /**
241
     * Remove start slash ('/') char in dir if starts with slash.
242
     * @param $directory
243
     * @return string
244
     */
245
    public static function removeStartSlash($directory) : string
246
    {
247
        if (self::startsWithSlash($directory)) {
248
            $directory = substr($directory, 1);
249
        }
250
        return $directory;
251
    }
252
253
    /**
254
     * For each dir passed in array, check if not started with '/' otherwise remove a slash to path.
255
     * If not dir, leave the element untouched.
256
     * @param array $paths
257
     * @return array
258
     */
259
    public static function removeStartSlashToAllPaths(array $paths) : array
260
    {
261
        if (empty($paths)) {
262
            return [];
263
        }
264
265
        return array_map('self::removeStartSlash', $paths);
266
    }
267
268
    /**
269
     * Dir::copy()
270
     * Copy a source directory (files and all subdirectories) to destination directory.
271
     * If Destination directory doesn't exists try to create it.
272
     * @param $directorySource
273
     * @param $directoryDestination
274
     * @param array $excludedDirectory array of path to be escluded (i.e. it will not copied to destination folder)
275
     * @param \Closure|null $copied a function with two arguments  ($directorySource,$directoryDestination).
276
     * @return bool true if success, otherwise false
277
     */
278
    public static function copy(
279
        $directorySource,
280
        $directoryDestination,
281
        array $excludedDirectory = [],
282
        \Closure $copied = null
283
    ) : bool
284
    {
285
        $directorySource = self::removeFinalSlash($directorySource);
286
        if (!self::isDirSafe($directorySource) || !is_readable($directorySource)) {
287
            return false;
288
        }
289
290
        $directoryDestination = self::removeFinalSlash($directoryDestination);
291
        if (!self::checkDirExistOrCreate($directoryDestination)) {
292
            return false;
293
        }
294
        is_callable($copied) ? $copied($directorySource, $directoryDestination) : '';
295
296
        $excludedDirectory = self::removeFinalSlashToAllPaths($excludedDirectory);
297
298
        $directorySourceHandle = opendir($directorySource);
299
        while (false !== ($contents = readdir($directorySourceHandle))) {
300
            if ($contents == '.' || $contents == '..') {
301
                continue;
302
            }
303
            $path = $directorySource . "/" . $contents;
304
            if (in_array(DirHelper::removeFinalSlash($path), $excludedDirectory)) {
305
                continue;
306
            }
307
            $pathDest = $directoryDestination . "/" . $contents;
308
309
            if (is_dir($path)) {
310
                self::copy($path, $pathDest, $excludedDirectory);
311
            } else {
312
                copy($path, $pathDest);
313
                is_callable($copied) ? $copied($path, $pathDest) : '';
314
            }
315
        }
316
        closedir($directorySourceHandle);
317
        return true;
318
    }
319
320
    /**
321
     * Returns whether the given path is on the local filesystem.
322
     *
323
     * @param string $path A path string
324
     *
325
     * @return boolean Returns true if the path is local, false for a URL
326
     * @see https://github.com/laradic/support/blob/master/src/Path.php
327
     */
328
    public static function isLocal($path)
329
    {
330
        return is_string($path) && '' !== $path && false === strpos($path, '://');
331
    }
332
333
    /**
334
     * Returns whether a path is absolute Unix path.
335
     *
336
     * @param string $path A path string
337
     *
338
     * @return boolean Returns true if the path is absolute unix path, false if it is
339
     *                 relative or empty
340
     */
341
    public static function isAbsoluteUnix($path)
342
    {
343
        return '' !== $path && '/' === $path[0];
344
    }
345
346
    /**
347
     * Returns whether a path is absolute Windows Path.
348
     *
349
     * @param string $path A path string
350
     *
351
     * @return boolean Returns true if the path is absolute, false if it is
352
     *                 relative or empty
353
     * @see https://github.com/laradic/support/blob/master/src/Path.php
354
     */
355
    public static function isAbsoluteWindows($path)
356
    {
357
        if ('' === $path) {
358
            return false;
359
        }
360
        if ('\\' === $path[0]) {
361
            return true;
362
        }
363
        // Windows root
364
        return self::isAbsoluteWindowsRoot($path);
365
    }
366
367
    /**
368
     * Check if win special drive C: or Normal win drive C:/  C:\
369
     * @param $path
370
     * @return bool
371
     */
372
    protected static function isAbsoluteWindowsRoot($path):bool
373
    {
374
        if (strlen($path) > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
375
            // Special win drive C:
376
            if (2 === strlen($path)) {
377
                return true;
378
            }
379
            // Normal win drive C:/  C:\
380
            if ('/' === $path[2] || '\\' === $path[2]) {
381
                return true;
382
            }
383
        }
384
        return false;
385
    }
386
387
    /**
388
     * Returns whether a path is absolute.
389
     *
390
     * @param string $path A path string
391
     *
392
     * @return boolean Returns true if the path is absolute, false if it is
393
     *                 relative or empty
394
     */
395
    public static function isAbsolute($path)
396
    {
397
        return self::isAbsoluteUnix($path) || self::isAbsoluteWindows($path);
398
    }
399
400
    /**
401
     * Returns whether a path is relative.
402
     *
403
     * @param string $path A path string
404
     *
405
     * @return boolean Returns true if the path is relative or empty, false if
406
     *                 it is absolute
407
     * @see https://github.com/laradic/support/blob/master/src/Path.php
408
     */
409
    public static function isRelative($path)
410
    {
411
        return !self::isAbsolute($path);
412
    }
413
414
    /**
415
     * Joins a split file system path.
416
     *
417
     * @param  array|string $paths
418
     *
419
     * @return string
420
     * @see https://github.com/laradic/support/blob/master/src/Path.php
421
     */
422
    public static function join($paths) : string
0 ignored issues
show
Unused Code introduced by
The parameter $paths is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
423
    {
424
        $paths = func_get_args();
425
        if (func_num_args() === 1 && is_array($paths[ 0 ])) {
426
            $paths = $paths[ 0 ];
427
        }
428
        foreach ($paths as $key => &$argument) {
429
            if (is_array($argument)) {
430
                $argument = self::join($argument);
431
            }
432
            $argument = self::removeFinalSlash($argument);
433
            if ($key > 0) {
434
                $argument = self::removeStartSlash($argument);
435
            }
436
            #$paths[$key] = $argument;
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
437
        }
438
        return implode(DIRECTORY_SEPARATOR, $paths);
439
    }
440
441
    /**
442
     * Similar to the join() method, but also normalize()'s the result
443
     *
444
     * @param string|array ...$paths
445
     *
446
     * @return string
447
     * @see https://github.com/laradic/support/blob/master/src/Path.php
448
     */
449
    public static function njoin(...$paths) : string
450
    {
451
        return self::canonicalize(self::join($paths));
452
    }
453
454
    /**
455
     * Canonicalizes the given path.
456
     *
457
     * During normalization, all slashes are replaced by forward slashes ("/").
458
     * Furthermore, all "." and ".." segments are removed as far as possible.
459
     * ".." segments at the beginning of relative paths are not removed.
460
     *
461
     * ```php
462
     * echo DirHelper::canonicalize("\webmozart\puli\..\css\style.css");
463
     * // => /webmozart/style.css
464
     *
465
     * echo DirHelper::canonicalize("../css/./style.css");
466
     * // => ../css/style.css
467
     * ```
468
     *
469
     * This method is able to deal with both UNIX and Windows paths.
470
     *
471
     * @param string $path A path string
472
     *
473
     * @return string The canonical path
474
     * @see https://github.com/laradic/support/blob/master/src/Path.php
475
     */
476
    public static function canonicalize($path)
477
    {
478
        $path = (string)$path;
479
        if ('' === $path) {
480
            return '';
481
        }
482
        $path = str_replace('\\', '/', $path);
483
        list ($root, $path) = self::split($path);
484
        $parts = array_filter(explode('/', $path), 'strlen');
485
        $canonicalParts = [];
486
        // Collapse dot folder ., .., i f possible
487
        foreach ($parts as $part) {
488
            self::collapseDotFolder($root, $part, $canonicalParts);
489
        }
490
        // Add the root directory again
491
        return $root . implode('/', $canonicalParts);
492
    }
493
494
    /**
495
     * Collapse dot folder '.', '..', if possible
496
     * @param $root
497
     * @param $part
498
     * @param $canonicalParts
499
     */
500
    protected static function collapseDotFolder($root, $part, &$canonicalParts)
501
    {
502
        if ('.' === $part) {
503
            return;
504
        }
505
        // Collapse ".." with the previous part, if one exists
506
        // Don't collapse ".." if the previous part is also ".."
507
        if ('..' === $part && count($canonicalParts) > 0
508
            && '..' !== $canonicalParts[count($canonicalParts) - 1]
509
        ) {
510
            array_pop($canonicalParts);
511
            return;
512
        }
513
        // Only add ".." prefixes for relative paths
514
        if ('..' !== $part || '' === $root) {
515
            $canonicalParts[] = $part;
516
        }
517
    }
518
519
    /**
520
     * Splits a part into its root directory and the remainder.
521
     *
522
     * If the path has no root directory, an empty root directory will be
523
     * returned.
524
     *
525
     * If the root directory is a Windows style partition, the resulting root
526
     * will always contain a trailing slash.
527
     *
528
     * list ($root, $path) = DirHelpersplit("C:/webmozart")
529
     * // => array("C:/", "webmozart")
530
     *
531
     * list ($root, $path) = DirHelpersplit("C:")
532
     * // => array("C:/", "")
533
     *
534
     * @param string $path The canonical path to split
535
     *
536
     * @return array An array with the root directory and the remaining relative
537
     *               path
538
     * @see https://github.com/laradic/support/blob/master/src/Path.php
539
     */
540
    private static function split($path)
541
    {
542
        if ('' === $path) {
543
            return ['', ''];
544
        }
545
        $root = '';
546
        $length = strlen($path);
547
        // Remove and remember root directory
548
        if ('/' === $path[0]) {
549
            $root = '/';
550
            $path = $length > 1 ? substr($path, 1) : '';
551
        } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
552
            if (2 === $length) {
553
                // Windows special case: "C:"
554
                $root = $path . '/';
555
                $path = '';
556
            } elseif ('/' === $path[2]) {
557
                // Windows normal case: "C:/"..
558
                $root = substr($path, 0, 3);
559
                $path = $length > 3 ? substr($path, 3) : '';
560
            }
561
        }
562
        return [$root, $path];
563
    }
564
}
565