Completed
Push — master ( 838969...074806 )
by Lorenzo
02:23
created

DirHelper::endsWithStar()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
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
        if (strlen($path) > 1 && ctype_alpha($path[ 0 ]) && ':' === $path[ 1 ]) {
365
            // Special case: "C:"
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
366
            if (2 === strlen($path)) {
367
                return true;
368
            }
369
            // Normal case: "C:/ or "C:\"
370
            if ('/' === $path[ 2 ] || '\\' === $path[ 2 ]) {
371
                return true;
372
            }
373
        }
374
        return false;
375
    }
376
377
    /**
378
     * Returns whether a path is absolute.
379
     *
380
     * @param string $path A path string
381
     *
382
     * @return boolean Returns true if the path is absolute, false if it is
383
     *                 relative or empty
384
     */
385
    public static function isAbsolute($path)
386
    {
387
        return self::isAbsoluteUnix($path) || self::isAbsoluteWindows($path);
388
    }
389
390
    /**
391
     * Returns whether a path is relative.
392
     *
393
     * @param string $path A path string
394
     *
395
     * @return boolean Returns true if the path is relative or empty, false if
396
     *                 it is absolute
397
     * @see https://github.com/laradic/support/blob/master/src/Path.php
398
     */
399
    public static function isRelative($path)
400
    {
401
        return !self::isAbsolute($path);
402
    }
403
404
    /**
405
     * Joins a split file system path.
406
     *
407
     * @param  array|string $paths
408
     *
409
     * @return string
410
     * @see https://github.com/laradic/support/blob/master/src/Path.php
411
     */
412
    public static function join(...$paths) : string
413
    {
414
        foreach ($paths as $key => &$argument) {
415
            if (is_array($argument)) {
416
                $argument = self::join($argument);
417
            }
418
            $argument = self::removeFinalSlash($argument);
419
            if ($key > 0) {
420
                $argument = self::removeStartSlash($argument);
421
            }
422
            $paths[ $key ] = $argument;
423
        }
424
        return implode(DIRECTORY_SEPARATOR, $paths);
425
    }
426
    /**
427
     * Similar to the join() method, but also normalize()'s the result
428
     *
429
     * @param string|array ...$paths
430
     *
431
     * @return string
432
     * @see https://github.com/laradic/support/blob/master/src/Path.php
433
     */
434
    public static function njoin(...$paths) : string
435
    {
436
        return self::canonicalize(self::join($paths));
437
    }
438
439
    /**
440
     * Canonicalizes the given path.
441
     *
442
     * During normalization, all slashes are replaced by forward slashes ("/").
443
     * Furthermore, all "." and ".." segments are removed as far as possible.
444
     * ".." segments at the beginning of relative paths are not removed.
445
     *
446
     * ```php
447
     * echo DirHelper::canonicalize("\webmozart\puli\..\css\style.css");
448
     * // => /webmozart/style.css
449
     *
450
     * echo DirHelper::canonicalize("../css/./style.css");
451
     * // => ../css/style.css
452
     * ```
453
     *
454
     * This method is able to deal with both UNIX and Windows paths.
455
     *
456
     * @param string $path A path string
457
     *
458
     * @return string The canonical path
459
     * @see https://github.com/laradic/support/blob/master/src/Path.php
460
     */
461
    public static function canonicalize($path)
462
    {
463
        $path = (string)$path;
464
        if ('' === $path) {
465
            return '';
466
        }
467
        $path = str_replace('\\', '/', $path);
468
        list ($root, $path) = self::split($path);
469
        $parts = array_filter(explode('/', $path), 'strlen');
470
        $canonicalParts = [ ];
471
        // Collapse "." and "..", if possible
0 ignored issues
show
Unused Code Comprehensibility introduced by
39% 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...
472
        foreach ($parts as $part) {
473
            if ('.' === $part) {
474
                continue;
475
            }
476
            // Collapse ".." with the previous part, if one exists
477
            // Don't collapse ".." if the previous part is also ".."
478
            if ('..' === $part && count($canonicalParts) > 0
479
                && '..' !== $canonicalParts[ count($canonicalParts) - 1 ]
480
            ) {
481
                array_pop($canonicalParts);
482
                continue;
483
            }
484
            // Only add ".." prefixes for relative paths
485
            if ('..' !== $part || '' === $root) {
486
                $canonicalParts[] = $part;
487
            }
488
        }
489
        // Add the root directory again
490
        return $root . implode('/', $canonicalParts);
491
    }
492
493
    /**
494
     * Splits a part into its root directory and the remainder.
495
     *
496
     * If the path has no root directory, an empty root directory will be
497
     * returned.
498
     *
499
     * If the root directory is a Windows style partition, the resulting root
500
     * will always contain a trailing slash.
501
     *
502
     * list ($root, $path) = DirHelpersplit("C:/webmozart")
503
     * // => array("C:/", "webmozart")
504
     *
505
     * list ($root, $path) = DirHelpersplit("C:")
506
     * // => array("C:/", "")
507
     *
508
     * @param string $path The canonical path to split
509
     *
510
     * @return array An array with the root directory and the remaining relative
511
     *               path
512
     * @see https://github.com/laradic/support/blob/master/src/Path.php
513
     */
514
    private static function split($path)
515
    {
516
        if ('' === $path) {
517
            return [ '', '' ];
518
        }
519
        $root = '';
520
        $length = strlen($path);
521
        // Remove and remember root directory
522
        if ('/' === $path[ 0 ]) {
523
            $root = '/';
524
            $path = $length > 1 ? substr($path, 1) : '';
525
        } elseif ($length > 1 && ctype_alpha($path[ 0 ]) && ':' === $path[ 1 ]) {
526
            if (2 === $length) {
527
                // Windows special case: "C:"
528
                $root = $path . '/';
529
                $path = '';
530
            } elseif ('/' === $path[ 2 ]) {
531
                // Windows normal case: "C:/"..
532
                $root = substr($path, 0, 3);
533
                $path = $length > 3 ? substr($path, 3) : '';
534
            }
535
        }
536
        return [ $root, $path ];
537
    }
538
}
539