DirHelper::split()   C
last analyzed

Complexity

Conditions 10
Paths 8

Size

Total Lines 24
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 5.2164
c 0
b 0
f 0
cc 10
eloc 16
nc 8
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 == '') {
108
            return false;
109
        }
110
        if ($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 == '') {
138
            return false;
139
        }
140
        if ($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 (self::isDotDir($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 string $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::isReadable($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
            self::copyInternal(
301
                $contents,
302
                $directorySource,
303
                $directoryDestination,
304
                $excludedDirectory,
305
                $copied);
306
        }
307
        closedir($directorySourceHandle);
308
        return true;
309
    }
310
311
    /**
312
     * @param $contents
313
     * @param $directorySource
314
     * @param $directoryDestination
315
     * @param $excludedDirectory
316
     * @param $copied
317
     */
318
    private static function copyInternal(
319
        $contents,
320
        $directorySource,
321
        $directoryDestination,
322
        array $excludedDirectory = [],
323
        \Closure $copied = null
324
    ) {
325
        if (self::isDotDir($contents)) {
326
            return;
327
        }
328
        $path = $directorySource . "/" . $contents;
329
        if (in_array(DirHelper::removeFinalSlash($path), $excludedDirectory)) {
330
            return;
331
        }
332
        $pathDest = $directoryDestination . "/" . $contents;
333
334
        if (is_dir($path)) {
335
            self::copy($path, $pathDest, $excludedDirectory);
336
        } else {
337
            copy($path, $pathDest);
338
            is_callable($copied) ? $copied($path, $pathDest) : '';
339
        }
340
    }
341
342
    /**
343
     * Returns whether the given path is on the local filesystem.
344
     *
345
     * @param string $path A path string
346
     *
347
     * @return boolean Returns true if the path is local, false for a URL
348
     * @see https://github.com/laradic/support/blob/master/src/Path.php
349
     */
350
    public static function isLocal($path)
351
    {
352
        return is_string($path) && '' !== $path && false === strpos($path, '://');
353
    }
354
355
    /**
356
     * Returns whether a path is absolute Unix path.
357
     *
358
     * @param string $path A path string
359
     *
360
     * @return boolean Returns true if the path is absolute unix path, false if it is
361
     *                 relative or empty
362
     */
363
    public static function isAbsoluteUnix($path)
364
    {
365
        return '' !== $path && '/' === $path[0];
366
    }
367
368
    /**
369
     * Returns whether a path is absolute Windows Path.
370
     *
371
     * @param string $path A path string
372
     *
373
     * @return boolean Returns true if the path is absolute, false if it is
374
     *                 relative or empty
375
     * @see https://github.com/laradic/support/blob/master/src/Path.php
376
     */
377
    public static function isAbsoluteWindows($path)
378
    {
379
        if ('' === $path) {
380
            return false;
381
        }
382
        if ('\\' === $path[0]) {
383
            return true;
384
        }
385
        // Windows root
386
        return self::isAbsoluteWindowsRoot($path);
387
    }
388
389
    /**
390
     * Check if win special drive C: or Normal win drive C:/  C:\
391
     * @param string $path
392
     * @return bool
393
     */
394
    protected static function isAbsoluteWindowsRoot($path):bool
395
    {
396
        if (strlen($path) > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
397
            // Special win drive C:
398
            if (2 === strlen($path)) {
399
                return true;
400
            }
401
            // Normal win drive C:/  C:\
402
            if ('/' === $path[2] || '\\' === $path[2]) {
403
                return true;
404
            }
405
        }
406
        return false;
407
    }
408
409
    /**
410
     * Returns whether a path is absolute.
411
     *
412
     * @param string $path A path string
413
     *
414
     * @return boolean Returns true if the path is absolute, false if it is
415
     *                 relative or empty
416
     */
417
    public static function isAbsolute($path)
418
    {
419
        return self::isAbsoluteUnix($path) || self::isAbsoluteWindows($path);
420
    }
421
422
    /**
423
     * Returns whether a path is relative.
424
     *
425
     * @param string $path A path string
426
     *
427
     * @return boolean Returns true if the path is relative or empty, false if
428
     *                 it is absolute
429
     * @see https://github.com/laradic/support/blob/master/src/Path.php
430
     */
431
    public static function isRelative($path)
432
    {
433
        return !self::isAbsolute($path);
434
    }
435
436
    /**
437
     * Joins a split file system path.
438
     *
439
     * @param  array|string
440
     *
441
     * @return string
442
     * @see https://github.com/laradic/support/blob/master/src/Path.php
443
     */
444
    public static function join() : string
445
    {
446
        $paths = func_get_args();
447
        if (func_num_args() === 1 && is_array($paths[0])) {
448
            $paths = $paths[0];
449
        }
450
        foreach ($paths as $key => &$argument) {
451
            if (is_array($argument)) {
452
                $argument = self::join($argument);
453
            }
454
            $argument = self::removeFinalSlash($argument);
455
            if ($key > 0) {
456
                $argument = self::removeStartSlash($argument);
457
            }
458
        }
459
        return implode(DIRECTORY_SEPARATOR, $paths);
460
    }
461
462
    /**
463
     * Similar to the join() method, but also normalize()'s the result
464
     *
465
     * @param string|array
466
     *
467
     * @return string
468
     * @see https://github.com/laradic/support/blob/master/src/Path.php
469
     */
470
    public static function njoin() : string
471
    {
472
        return self::canonicalize(self::join(func_get_args()));
473
    }
474
475
    /**
476
     * Canonicalizes the given path.
477
     *
478
     * During normalization, all slashes are replaced by forward slashes ("/").
479
     * Furthermore, all "." and ".." segments are removed as far as possible.
480
     * ".." segments at the beginning of relative paths are not removed.
481
     *
482
     * ```php
483
     * echo DirHelper::canonicalize("\webmozart\puli\..\css\style.css");
484
     * // => /webmozart/style.css
485
     *
486
     * echo DirHelper::canonicalize("../css/./style.css");
487
     * // => ../css/style.css
488
     * ```
489
     *
490
     * This method is able to deal with both UNIX and Windows paths.
491
     *
492
     * @param string $path A path string
493
     *
494
     * @return string The canonical path
495
     * @see https://github.com/laradic/support/blob/master/src/Path.php
496
     */
497
    public static function canonicalize($path)
498
    {
499
        $path = (string)$path;
500
        if ('' === $path) {
501
            return '';
502
        }
503
        $path = str_replace('\\', '/', $path);
504
        list ($root, $path) = self::split($path);
505
        $parts = array_filter(explode('/', $path), 'strlen');
506
        $canonicalParts = [];
507
        // Collapse dot folder ., .., i f possible
508
        foreach ($parts as $part) {
509
            self::collapseDotFolder($root, $part, $canonicalParts);
510
        }
511
        // Add the root directory again
512
        return $root . implode('/', $canonicalParts);
513
    }
514
515
    /**
516
     * Collapse dot folder '.', '..', if possible
517
     * @param string $root
518
     * @param $part
519
     * @param $canonicalParts
520
     */
521
    protected static function collapseDotFolder($root, $part, &$canonicalParts)
522
    {
523
        if ('.' === $part) {
524
            return;
525
        }
526
        // Collapse ".." with the previous part, if one exists
527
        // Don't collapse ".." if the previous part is also ".."
528
        if ('..' === $part && count($canonicalParts) > 0
529
            && '..' !== $canonicalParts[count($canonicalParts) - 1]
530
        ) {
531
            array_pop($canonicalParts);
532
            return;
533
        }
534
        // Only add ".." prefixes for relative paths
535
        if ('..' !== $part || '' === $root) {
536
            $canonicalParts[] = $part;
537
        }
538
    }
539
540
    /**
541
     * Splits a part into its root directory and the remainder.
542
     *
543
     * If the path has no root directory, an empty root directory will be
544
     * returned.
545
     *
546
     * If the root directory is a Windows style partition, the resulting root
547
     * will always contain a trailing slash.
548
     *
549
     * list ($root, $path) = DirHelpersplit("C:/webmozart")
550
     * // => array("C:/", "webmozart")
551
     *
552
     * list ($root, $path) = DirHelpersplit("C:")
553
     * // => array("C:/", "")
554
     *
555
     * @param string $path The canonical path to split
556
     *
557
     * @return string[] An array with the root directory and the remaining relative
558
     *               path
559
     * @see https://github.com/laradic/support/blob/master/src/Path.php
560
     */
561
    private static function split($path)
562
    {
563
        if ('' === $path) {
564
            return ['', ''];
565
        }
566
        $root = '';
567
        $length = strlen($path);
568
        // Remove and remember root directory
569
        if ('/' === $path[0]) {
570
            $root = '/';
571
            $path = $length > 1 ? substr($path, 1) : '';
572
        } elseif ($length > 1 && ctype_alpha($path[0]) && ':' === $path[1]) {
573
            if (2 === $length) {
574
                // Windows special case: "C:"
575
                $root = $path . '/';
576
                $path = '';
577
            } elseif ('/' === $path[2]) {
578
                // Windows normal case: "C:/"..
579
                $root = substr($path, 0, 3);
580
                $path = $length > 3 ? substr($path, 3) : '';
581
            }
582
        }
583
        return [$root, $path];
584
    }
585
586
    /**
587
     * Check if a directory is empty in efficent way.
588
     * Check hidden files too.
589
     * @param string $path
590
     * @return bool
591
     */
592
    public static function isDirEmpty(string $path) : bool
593
    {
594
        //che if no such dir, not a dir, not readable
595
        if (!self::isReadable($path)) {
596
            return false;
597
        }
598
599
        $result = true;
600
        $handle = opendir($path);
601
        while (false !== ($entry = readdir($handle))) {
602
            if (!self::isDotDir($entry)) {
603
                $result = false;
604
                break;
605
            }
606
        }
607
        closedir($handle);
608
        return $result;
609
    }
610
611
    /**
612
     * Check if an antry is linux dot dir (i.e.: . or .. )
613
     * @param $entry
614
     * @return bool
615
     */
616
    public static function isDotDir($entry):bool
617
    {
618
        return $entry == "." || $entry == "..";
619
    }
620
621
    /**
622
     * Check if $path is a dir and is readable.
623
     * Return false is you pass a file.
624
     * @param string $path
625
     * @return bool
626
     */
627
    public static function isReadable(string $path):bool
628
    {
629
        return self::isDirSafe($path) && is_readable($path);
630
    }
631
}
632