Passed
Push — master ( 97db60...ca068f )
by Alexey
02:56
created

FilesUtil::regexFileSearch()   A

Complexity

Conditions 5
Paths 12

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 8
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 12
rs 9.6111
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
class FilesUtil
15
{
16
17
    /**
18
     * Is empty directory
19
     *
20
     * @param string $dir Directory
21
     * @return bool
22
     */
23
    public static function isEmptyDir($dir)
24
    {
25
        if (!is_readable($dir)) {
26
            return false;
27
        }
28
        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

28
        return count(/** @scrutinizer ignore-type */ scandir($dir)) === 2;
Loading history...
29
    }
30
31
    /**
32
     * Remove recursive directory.
33
     *
34
     * @param string $dir Directory path.
35
     */
36
    public static function removeDir($dir)
37
    {
38
        $files = new \RecursiveIteratorIterator(
39
            new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
40
            \RecursiveIteratorIterator::CHILD_FIRST
41
        );
42
        foreach ($files as $fileInfo) {
43
            $function = ($fileInfo->isDir() ? 'rmdir' : 'unlink');
44
            $function($fileInfo->getRealPath());
45
        }
46
        rmdir($dir);
47
    }
48
49
50
    /**
51
     * Convert glob pattern to regex pattern.
52
     *
53
     * @param string $globPattern
54
     * @return string
55
     */
56
    public static function convertGlobToRegEx($globPattern)
57
    {
58
        // Remove beginning and ending * globs because they're useless
59
        $globPattern = trim($globPattern, '*');
60
        $escaping = false;
61
        $inCurrent = 0;
62
        $chars = str_split($globPattern);
63
        $regexPattern = '';
64
        foreach ($chars as $currentChar) {
65
            switch ($currentChar) {
66
                case '*':
67
                    $regexPattern .= ($escaping ? "\\*" : '.*');
68
                    $escaping = false;
69
                    break;
70
                case '?':
71
                    $regexPattern .= ($escaping ? "\\?" : '.');
72
                    $escaping = false;
73
                    break;
74
                case '.':
75
                case '(':
76
                case ')':
77
                case '+':
78
                case '|':
79
                case '^':
80
                case '$':
81
                case '@':
82
                case '%':
83
                    $regexPattern .= '\\' . $currentChar;
84
                    $escaping = false;
85
                    break;
86
                case '\\':
87
                    if ($escaping) {
88
                        $regexPattern .= "\\\\";
89
                        $escaping = false;
90
                    } else {
91
                        $escaping = true;
92
                    }
93
                    break;
94
                case '{':
95
                    if ($escaping) {
96
                        $regexPattern .= "\\{";
97
                    } else {
98
                        $regexPattern = '(';
99
                        $inCurrent++;
100
                    }
101
                    $escaping = false;
102
                    break;
103
                case '}':
104
                    if ($inCurrent > 0 && !$escaping) {
105
                        $regexPattern .= ')';
106
                        $inCurrent--;
107
                    } elseif ($escaping) {
108
                        $regexPattern = "\\}";
109
                    } else {
110
                        $regexPattern = "}";
111
                    }
112
                    $escaping = false;
113
                    break;
114
                case ',':
115
                    if ($inCurrent > 0 && !$escaping) {
116
                        $regexPattern .= '|';
117
                    } elseif ($escaping) {
118
                        $regexPattern .= "\\,";
119
                    } else {
120
                        $regexPattern = ",";
121
                    }
122
                    break;
123
                default:
124
                    $escaping = false;
125
                    $regexPattern .= $currentChar;
126
            }
127
        }
128
        return $regexPattern;
129
    }
130
131
    /**
132
     * Search files.
133
     *
134
     * @param string $inputDir
135
     * @param bool $recursive
136
     * @param array $ignoreFiles
137
     * @return array Searched file list
138
     */
139
    public static function fileSearchWithIgnore($inputDir, $recursive = true, array $ignoreFiles = [])
140
    {
141
        $directoryIterator = $recursive ?
142
            new \RecursiveDirectoryIterator($inputDir) :
143
            new \DirectoryIterator($inputDir);
144
145
        if (!empty($ignoreFiles)) {
146
            $directoryIterator = $recursive ?
147
                new IgnoreFilesRecursiveFilterIterator($directoryIterator, $ignoreFiles) :
0 ignored issues
show
Bug introduced by
It seems like $directoryIterator can also be of type DirectoryIterator; however, parameter $iterator of PhpZip\Util\Iterator\Ign...Iterator::__construct() does only seem to accept RecursiveIterator, 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

147
                new IgnoreFilesRecursiveFilterIterator(/** @scrutinizer ignore-type */ $directoryIterator, $ignoreFiles) :
Loading history...
148
                new IgnoreFilesFilterIterator($directoryIterator, $ignoreFiles);
149
        }
150
151
        $iterator = $recursive ?
152
            new \RecursiveIteratorIterator($directoryIterator) :
153
            new \IteratorIterator($directoryIterator);
154
155
        $fileList = [];
156
        foreach ($iterator as $file) {
157
            if ($file instanceof \SplFileInfo) {
158
                $fileList[] = $file->getPathname();
159
            }
160
        }
161
        return $fileList;
162
    }
163
164
    /**
165
     * Search files from glob pattern.
166
     *
167
     * @param string $globPattern
168
     * @param int $flags
169
     * @param bool $recursive
170
     * @return array Searched file list
171
     */
172
    public static function globFileSearch($globPattern, $flags = 0, $recursive = true)
173
    {
174
        $flags = (int)$flags;
175
        $recursive = (bool)$recursive;
176
        $files = glob($globPattern, $flags);
177
        if (!$recursive) {
178
            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...
179
        }
180
        foreach (glob(dirname($globPattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
181
            $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

181
            $files = array_merge(/** @scrutinizer ignore-type */ $files, self::globFileSearch($dir . '/' . basename($globPattern), $flags, $recursive));
Loading history...
182
        }
183
        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...
184
    }
185
186
    /**
187
     * Search files from regex pattern.
188
     *
189
     * @param string $folder
190
     * @param string $pattern
191
     * @param bool $recursive
192
     * @return array Searched file list
193
     */
194
    public static function regexFileSearch($folder, $pattern, $recursive = true)
195
    {
196
        $directoryIterator = $recursive ? new \RecursiveDirectoryIterator($folder) : new \DirectoryIterator($folder);
197
        $iterator = $recursive ? new \RecursiveIteratorIterator($directoryIterator) : new \IteratorIterator($directoryIterator);
198
        $regexIterator = new \RegexIterator($iterator, $pattern, \RegexIterator::MATCH);
199
        $fileList = [];
200
        foreach ($regexIterator as $file) {
201
            if ($file instanceof \SplFileInfo) {
202
                $fileList[] = $file->getPathname();
203
            }
204
        }
205
        return $fileList;
206
    }
207
208
    /**
209
     * Convert bytes to human size.
210
     *
211
     * @param int $size Size bytes
212
     * @param string|null $unit Unit support 'GB', 'MB', 'KB'
213
     * @return string
214
     */
215
    public static function humanSize($size, $unit = null)
216
    {
217
        if (($unit === null && $size >= 1 << 30) || $unit === "GB") {
218
            return number_format($size / (1 << 30), 2) . "GB";
219
        }
220
        if (($unit === null && $size >= 1 << 20) || $unit === "MB") {
221
            return number_format($size / (1 << 20), 2) . "MB";
222
        }
223
        if (($unit === null && $size >= 1 << 10) || $unit === "KB") {
224
            return number_format($size / (1 << 10), 2) . "KB";
225
        }
226
        return number_format($size) . " bytes";
227
    }
228
229
    /**
230
     * Normalizes zip path.
231
     *
232
     * @param string $path Zip path
233
     * @return string
234
     */
235
    public static function normalizeZipPath($path)
236
    {
237
        return implode(
238
            '/',
239
            array_filter(
240
                explode('/', (string)$path),
241
                static function ($part) {
242
                    return $part !== '.' && $part !== '..';
243
                }
244
            )
245
        );
246
    }
247
}
248