Passed
Branch master (25d5dd)
by Alexey
02:30
created

FilesUtil::convertGlobToRegEx()   D

Complexity

Conditions 27
Paths 25

Size

Total Lines 81
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 42
CRAP Score 46.683

Importance

Changes 0
Metric Value
cc 27
eloc 64
nc 25
nop 1
dl 0
loc 81
ccs 42
cts 60
cp 0.7
crap 46.683
rs 4.1666
c 0
b 0
f 0

How to fix   Long Method    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 PhpZip\Util;
4
5
use PhpZip\Util\Iterator\IgnoreFilesFilterIterator;
6
use PhpZip\Util\Iterator\IgnoreFilesRecursiveFilterIterator;
7
8
/**
9
 * Files util.
10
 *
11
 * @author Ne-Lexa [email protected]
12
 * @license MIT
13
 *
14
 * @internal
15
 */
16
final class FilesUtil
17
{
18
    /**
19
     * Is empty directory.
20
     *
21
     * @param string $dir Directory
22
     *
23
     * @return bool
24
     */
25 24
    public static function isEmptyDir($dir)
26
    {
27 24
        if (!is_readable($dir)) {
28
            return false;
29
        }
30
31 24
        return \count(scandir($dir)) === 2;
0 ignored issues
show
Bug introduced by
It seems like scandir($dir) can also be of type false; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

31
        return \count(/** @scrutinizer ignore-type */ scandir($dir)) === 2;
Loading history...
32
    }
33
34
    /**
35
     * Remove recursive directory.
36
     *
37
     * @param string $dir directory path
38
     */
39 36
    public static function removeDir($dir)
40
    {
41 36
        $files = new \RecursiveIteratorIterator(
42 36
            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
43 36
            \RecursiveIteratorIterator::CHILD_FIRST
44
        );
45
46 36
        foreach ($files as $fileInfo) {
47 32
            $function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
48 32
            $function($fileInfo->getRealPath());
49
        }
50 36
        rmdir($dir);
51 36
    }
52
53
    /**
54
     * Convert glob pattern to regex pattern.
55
     *
56
     * @param string $globPattern
57
     *
58
     * @return string
59
     */
60 1
    public static function convertGlobToRegEx($globPattern)
61
    {
62
        // Remove beginning and ending * globs because they're useless
63 1
        $globPattern = trim($globPattern, '*');
64 1
        $escaping = false;
65 1
        $inCurrent = 0;
66 1
        $chars = str_split($globPattern);
67 1
        $regexPattern = '';
68
69 1
        foreach ($chars as $currentChar) {
70 1
            switch ($currentChar) {
71 1
                case '*':
72
                    $regexPattern .= ($escaping ? '\\*' : '.*');
73
                    $escaping = false;
74
                    break;
75
76 1
                case '?':
77
                    $regexPattern .= ($escaping ? '\\?' : '.');
78
                    $escaping = false;
79
                    break;
80
81 1
                case '.':
82 1
                case '(':
83 1
                case ')':
84 1
                case '+':
85 1
                case '|':
86 1
                case '^':
87 1
                case '$':
88 1
                case '@':
89 1
                case '%':
90 1
                    $regexPattern .= '\\' . $currentChar;
91 1
                    $escaping = false;
92 1
                    break;
93
94 1
                case '\\':
95
                    if ($escaping) {
96
                        $regexPattern .= '\\\\';
97
                        $escaping = false;
98
                    } else {
99
                        $escaping = true;
100
                    }
101
                    break;
102
103 1
                case '{':
104 1
                    if ($escaping) {
105
                        $regexPattern .= '\\{';
106
                    } else {
107 1
                        $regexPattern = '(';
108 1
                        $inCurrent++;
109
                    }
110 1
                    $escaping = false;
111 1
                    break;
112
113 1
                case '}':
114 1
                    if ($inCurrent > 0 && !$escaping) {
115 1
                        $regexPattern .= ')';
116 1
                        $inCurrent--;
117
                    } elseif ($escaping) {
118
                        $regexPattern = '\\}';
119
                    } else {
120
                        $regexPattern = '}';
121
                    }
122 1
                    $escaping = false;
123 1
                    break;
124
125 1
                case ',':
126 1
                    if ($inCurrent > 0 && !$escaping) {
127 1
                        $regexPattern .= '|';
128
                    } elseif ($escaping) {
129
                        $regexPattern .= '\\,';
130
                    } else {
131
                        $regexPattern = ',';
132
                    }
133 1
                    break;
134
                default:
135 1
                    $escaping = false;
136 1
                    $regexPattern .= $currentChar;
137
            }
138
        }
139
140 1
        return $regexPattern;
141
    }
142
143
    /**
144
     * Search files.
145
     *
146
     * @param string $inputDir
147
     * @param bool   $recursive
148
     * @param array  $ignoreFiles
149
     *
150
     * @return array Searched file list
151
     */
152
    public static function fileSearchWithIgnore($inputDir, $recursive = true, array $ignoreFiles = [])
153
    {
154
        if ($recursive) {
155
            $directoryIterator = new \RecursiveDirectoryIterator($inputDir);
156
157
            if (!empty($ignoreFiles)) {
158
                $directoryIterator = new IgnoreFilesRecursiveFilterIterator($directoryIterator, $ignoreFiles);
159
            }
160
            $iterator = new \RecursiveIteratorIterator($directoryIterator);
161
        } else {
162
            $directoryIterator = new \DirectoryIterator($inputDir);
163
164
            if (!empty($ignoreFiles)) {
165
                $directoryIterator = new IgnoreFilesFilterIterator($directoryIterator, $ignoreFiles);
166
            }
167
            $iterator = new \IteratorIterator($directoryIterator);
168
        }
169
170
        $fileList = [];
171
172
        foreach ($iterator as $file) {
173
            if ($file instanceof \SplFileInfo) {
174
                $fileList[] = $file->getPathname();
175
            }
176
        }
177
178
        return $fileList;
179
    }
180
181
    /**
182
     * Search files from glob pattern.
183
     *
184
     * @param string $globPattern
185
     * @param int    $flags
186
     * @param bool   $recursive
187
     *
188
     * @return array Searched file list
189
     */
190 3
    public static function globFileSearch($globPattern, $flags = 0, $recursive = true)
191
    {
192 3
        $flags = (int) $flags;
193 3
        $recursive = (bool) $recursive;
194 3
        $files = glob($globPattern, $flags);
195
196 3
        if (!$recursive) {
197 1
            return $files;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $files could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
198
        }
199
200 2
        foreach (glob(\dirname($globPattern) . '/*', \GLOB_ONLYDIR | \GLOB_NOSORT) as $dir) {
201
            // Unpacking the argument via ... is supported starting from php 5.6 only
202
            /** @noinspection SlowArrayOperationsInLoopInspection */
203 2
            $files = array_merge($files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
0 ignored issues
show
Bug introduced by
It seems like $files can also be of type false; however, parameter $array1 of array_merge() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

203
            $files = array_merge(/** @scrutinizer ignore-type */ $files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
Loading history...
204
        }
205
206 2
        return $files;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $files could also return false which is incompatible with the documented return type array. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
207
    }
208
209
    /**
210
     * Search files from regex pattern.
211
     *
212
     * @param string $folder
213
     * @param string $pattern
214
     * @param bool   $recursive
215
     *
216
     * @return array Searched file list
217
     */
218 3
    public static function regexFileSearch($folder, $pattern, $recursive = true)
219
    {
220 3
        if ($recursive) {
221 2
            $directoryIterator = new \RecursiveDirectoryIterator($folder);
222 2
            $iterator = new \RecursiveIteratorIterator($directoryIterator);
223
        } else {
224 1
            $directoryIterator = new \DirectoryIterator($folder);
225 1
            $iterator = new \IteratorIterator($directoryIterator);
226
        }
227
228 3
        $regexIterator = new \RegexIterator($iterator, $pattern, \RegexIterator::MATCH);
229 3
        $fileList = [];
230
231 3
        foreach ($regexIterator as $file) {
232 3
            if ($file instanceof \SplFileInfo) {
233 3
                $fileList[] = $file->getPathname();
234
            }
235
        }
236
237 3
        return $fileList;
238
    }
239
240
    /**
241
     * Convert bytes to human size.
242
     *
243
     * @param int         $size Size bytes
244
     * @param string|null $unit Unit support 'GB', 'MB', 'KB'
245
     *
246
     * @return string
247
     */
248
    public static function humanSize($size, $unit = null)
249
    {
250
        if (($unit === null && $size >= 1 << 30) || $unit === 'GB') {
251
            return number_format($size / (1 << 30), 2) . 'GB';
252
        }
253
254
        if (($unit === null && $size >= 1 << 20) || $unit === 'MB') {
255
            return number_format($size / (1 << 20), 2) . 'MB';
256
        }
257
258
        if (($unit === null && $size >= 1 << 10) || $unit === 'KB') {
259
            return number_format($size / (1 << 10), 2) . 'KB';
260
        }
261
262
        return number_format($size) . ' bytes';
263
    }
264
265
    /**
266
     * Normalizes zip path.
267
     *
268
     * @param string $path Zip path
269
     *
270
     * @return string
271
     */
272 6
    public static function normalizeZipPath($path)
273
    {
274 6
        return implode(
275 6
            '/',
276
            array_filter(
277 6
                explode('/', (string) $path),
278 6
                static function ($part) {
279 6
                    return $part !== '.' && $part !== '..';
280 6
                }
281
            )
282
        );
283
    }
284
285
    /**
286
     * Returns whether the file path is an absolute path.
287
     *
288
     * @param string $file A file path
289
     *
290
     * @return bool
291
     *
292
     * @see source symfony filesystem component
293
     */
294
    public static function isAbsolutePath($file)
295
    {
296
        return strspn($file, '/\\', 0, 1)
297
            || (
298
                \strlen($file) > 3 && ctype_alpha($file[0])
299
                && $file[1] === ':'
300
                && strspn($file, '/\\', 2, 1)
301
            )
302
            || parse_url($file, \PHP_URL_SCHEME) !== null;
303
    }
304
305
    /**
306
     * @param string $linkPath
307
     * @param string $target
308
     *
309
     * @return bool
310
     */
311
    public static function symlink($target, $linkPath)
312
    {
313
        if (\DIRECTORY_SEPARATOR === '\\') {
314
            $linkPath = str_replace('/', '\\', $linkPath);
315
            $target = str_replace('/', '\\', $target);
316
            $abs = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $abs is dead and can be removed.
Loading history...
317
318
            if (!self::isAbsolutePath($target)) {
319
                $abs = realpath(\dirname($linkPath) . \DIRECTORY_SEPARATOR . $target);
320
321
                if (\is_string($abs)) {
0 ignored issues
show
introduced by
The condition is_string($abs) is always true.
Loading history...
322
                    $target = $abs;
323
                }
324
            }
325
        }
326
327
        if (!symlink($target, $linkPath)) {
328
            if (\DIRECTORY_SEPARATOR === '\\' && is_file($target)) {
329
                return copy($target, $linkPath);
330
            }
331
332
            return false;
333
        }
334
335
        return true;
336
    }
337
338
    /**
339
     * @param string $file
340
     *
341
     * @return bool
342
     */
343 11
    public static function isBadCompressionFile($file)
344
    {
345
        $badCompressFileExt = [
346 11
            'dic',
347
            'dng',
348
            'f4v',
349
            'flipchart',
350
            'h264',
351
            'lrf',
352
            'mobi',
353
            'mts',
354
            'nef',
355
            'pspimage',
356
        ];
357
358 11
        $ext = strtolower(pathinfo($file, \PATHINFO_EXTENSION));
359
360 11
        if (\in_array($ext, $badCompressFileExt, true)) {
361
            return true;
362
        }
363
364 11
        $mimeType = self::getMimeTypeFromFile($file);
365
366 11
        return self::isBadCompressionMimeType($mimeType);
367
    }
368
369
    /**
370
     * @param string $mimeType
371
     *
372
     * @return bool
373
     */
374 14
    public static function isBadCompressionMimeType($mimeType)
375
    {
376 14
        static $badDeflateCompMimeTypes = [
377
            'application/epub+zip',
378
            'application/gzip',
379
            'application/vnd.debian.binary-package',
380
            'application/vnd.oasis.opendocument.graphics',
381
            'application/vnd.oasis.opendocument.presentation',
382
            'application/vnd.oasis.opendocument.text',
383
            'application/vnd.oasis.opendocument.text-master',
384
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
385
            'application/vnd.rn-realmedia',
386
            'application/x-7z-compressed',
387
            'application/x-arj',
388
            'application/x-bzip2',
389
            'application/x-hwp',
390
            'application/x-lzip',
391
            'application/x-lzma',
392
            'application/x-ms-reader',
393
            'application/x-rar',
394
            'application/x-rpm',
395
            'application/x-stuffit',
396
            'application/x-tar',
397
            'application/x-xz',
398
            'application/zip',
399
            'application/zlib',
400
            'audio/flac',
401
            'audio/mpeg',
402
            'audio/ogg',
403
            'audio/vnd.dolby.dd-raw',
404
            'audio/webm',
405
            'audio/x-ape',
406
            'audio/x-hx-aac-adts',
407
            'audio/x-m4a',
408
            'audio/x-m4a',
409
            'audio/x-wav',
410
            'image/gif',
411
            'image/heic',
412
            'image/jp2',
413
            'image/jpeg',
414
            'image/png',
415
            'image/vnd.djvu',
416
            'image/webp',
417
            'image/x-canon-cr2',
418
            'video/ogg',
419
            'video/webm',
420
            'video/x-matroska',
421
            'video/x-ms-asf',
422
            'x-epoc/x-sisx-app',
423
        ];
424
425 14
        if (\in_array($mimeType, $badDeflateCompMimeTypes, true)) {
426
            return true;
427
        }
428
429 14
        return false;
430
    }
431
432
    /**
433
     * @param string $file
434
     *
435
     * @return string
436
     *
437
     * @noinspection PhpComposerExtensionStubsInspection
438
     */
439 11
    public static function getMimeTypeFromFile($file)
440
    {
441 11
        if (\function_exists('mime_content_type')) {
442 11
            return mime_content_type($file);
443
        }
444
445
        return 'application/octet-stream';
446
    }
447
448
    /**
449
     * @param string $contents
450
     *
451
     * @return string
452
     * @noinspection PhpComposerExtensionStubsInspection
453
     */
454 5
    public static function getMimeTypeFromString($contents)
455
    {
456 5
        $contents = (string) $contents;
457 5
        $finfo = new \finfo(\FILEINFO_MIME);
458 5
        $mimeType = $finfo->buffer($contents);
459
460 5
        if ($mimeType === false) {
461
            $mimeType = 'application/octet-stream';
462
        }
463
464 5
        return explode(';', $mimeType)[0];
465
    }
466
}
467