Test Failed
Pull Request — main (#17)
by Dimitri
16:16 queued 22s
created

directory_map()   B

Complexity

Conditions 11
Paths 25

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 18
c 1
b 0
f 0
nc 25
nop 3
dl 0
loc 31
ccs 0
cts 11
cp 0
crap 132
rs 7.3166

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
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
use BlitzPHP\Container\Services;
13
14
if (! function_exists('directory_map')) {
15
    /**
16
     * Créer une carte de répertoire
17
     *
18
     * Lit le répertoire spécifié et construit un tableau
19
     * représentation de celui-ci. Les sous-dossiers contenus dans le répertoire seront également mappés.
20
     *
21
     * @param string $sourceDir      Chemin d'accès à la source
22
     * @param int    $directoryDepth Profondeur des répertoires à parcourir
23
     *                               (0 = entièrement récursif, 1 = répertoire actuel, etc.)
24
     * @param bool   $hidden         Afficher ou non les fichiers cachés
25
     */
26
    function directory_map(string $sourceDir, int $directoryDepth = 0, bool $hidden = false): array
27
    {
28
        try {
29
            $fp = opendir($sourceDir);
30
31
            $fileData  = [];
32
            $newDepth  = $directoryDepth - 1;
33
            $sourceDir = rtrim($sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
34
35
            while (false !== ($file = readdir($fp))) {
36
                // Remove '.', '..', and hidden files [optional]
37
                if ($file === '.' || $file === '..' || ($hidden === false && $file[0] === '.')) {
38
                    continue;
39
                }
40
41
                if (is_dir($sourceDir . $file)) {
42
                    $file .= DIRECTORY_SEPARATOR;
43
                }
44
45
                if (($directoryDepth < 1 || $newDepth > 0) && is_dir($sourceDir . $file)) {
46
                    $fileData[$file] = directory_map($sourceDir . $file, $newDepth, $hidden);
47
                } else {
48
                    $fileData[] = $file;
49
                }
50
            }
51
52
            closedir($fp);
53
54
            return $fileData;
55
        } catch (Throwable) {
56
            return [];
57
        }
58
    }
59
}
60
61
if (! function_exists('directory_mirror')) {
62
    /**
63
     * Copie récursivement les fichiers et répertoires du répertoire d'origine
64
     * dans le répertoire cible, c'est-à-dire "miroir" son contenu.
65
     *
66
     * @param bool $overwrite Si les fichiers individuels sont écrasés en cas de collision
67
     */
68
    function directory_mirror(string $originDir, string $targetDir, bool $overwrite = true): bool
69
    {
70
        return Services::fs()->copyDirectory($originDir, $targetDir, $overwrite);
0 ignored issues
show
Bug introduced by
$overwrite of type boolean is incompatible with the type integer|null expected by parameter $options of BlitzPHP\Filesystem\Filesystem::copyDirectory(). ( Ignorable by Annotation )

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

70
        return Services::fs()->copyDirectory($originDir, $targetDir, /** @scrutinizer ignore-type */ $overwrite);
Loading history...
71
    }
72
}
73
74
if (! function_exists('write_file')) {
75
    /**
76
     * Write File
77
     *
78
     * Writes data to the file specified in the path.
79
     * Creates a new file if non-existent.
80
     *
81
     * @param string $path File path
82
     * @param string $data Data to write
83
     * @param string $mode fopen() mode (default: 'wb')
84
     */
85
    function write_file(string $path, string $data, string $mode = 'wb'): bool
86
    {
87
        try {
88 2
            $fp = fopen($path, $mode);
89
90 2
            flock($fp, LOCK_EX);
91
92 2
            for ($result = $written = 0, $length = strlen($data); $written < $length; $written += $result) {
93
                if (($result = fwrite($fp, substr($data, $written))) === false) {
94
                    break;
95
                }
96
            }
97
98 2
            flock($fp, LOCK_UN);
99 2
            fclose($fp);
100
101 2
            return is_int($result);
102
        } catch (Throwable) {
103
            return false;
104
        }
105
    }
106
}
107
108
if (! function_exists('delete_files')) {
109
    /**
110
     * Delete Files
111
     *
112
     * Deletes all files contained in the supplied directory path.
113
     * Files must be writable or owned by the system in order to be deleted.
114
     * If the second parameter is set to true, any directories contained
115
     * within the supplied base directory will be nuked as well.
116
     *
117
     * @param string $path   File path
118
     * @param bool   $delDir Whether to delete any directories found in the path
119
     * @param bool   $htdocs Whether to skip deleting .htaccess and index page files
120
     * @param bool   $hidden Whether to include hidden files (files beginning with a period)
121
     */
122
    function delete_files(string $path, bool $delDir = false, bool $htdocs = false, bool $hidden = false): bool
123
    {
124
        $path = realpath($path) ?: $path;
125
        $path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
126
127
        try {
128
            foreach (new RecursiveIteratorIterator(
129
                new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
130
                RecursiveIteratorIterator::CHILD_FIRST
131
            ) as $object) {
132
                $filename = $object->getFilename();
133
                if (! $hidden && $filename[0] === '.') {
134
                    continue;
135
                }
136
137
                if (! $htdocs || ! preg_match('/^(\.htaccess|index\.(html|htm|php)|web\.config)$/i', $filename)) {
138
                    $isDir = $object->isDir();
139
                    if ($isDir && $delDir) {
140
                        rmdir($object->getPathname());
141
142
                        continue;
143
                    }
144
                    if (! $isDir) {
145
                        unlink($object->getPathname());
146
                    }
147
                }
148
            }
149
150
            return true;
151
        } catch (Throwable) {
152
            return false;
153
        }
154
    }
155
}
156
157
if (! function_exists('get_filenames')) {
158
    /**
159
     * Get Filenames
160
     *
161
     * Reads the specified directory and builds an array containing the filenames.
162
     * Any sub-folders contained within the specified path are read as well.
163
     *
164
     * @param string    $sourceDir   Path to source
165
     * @param bool|null $includePath Whether to include the path as part of the filename; false for no path, null for a relative path, true for full path
166
     * @param bool      $hidden      Whether to include hidden files (files beginning with a period)
167
     * @param bool      $includeDir  Whether to include directories
168
     */
169
    function get_filenames(
170
        string $sourceDir,
171
        ?bool $includePath = false,
172
        bool $hidden = false,
173
        bool $includeDir = true
174
    ): array {
175
        $files = [];
176
177
        $sourceDir = realpath($sourceDir) ?: $sourceDir;
178
        $sourceDir = rtrim($sourceDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
179
180
        try {
181
            foreach (new RecursiveIteratorIterator(
182
                new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS),
183
                RecursiveIteratorIterator::SELF_FIRST
184
            ) as $name => $object) {
185
                $basename = pathinfo($name, PATHINFO_BASENAME);
0 ignored issues
show
Bug introduced by
It seems like $name can also be of type null and true; however, parameter $path of pathinfo() does only seem to accept string, 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

185
                $basename = pathinfo(/** @scrutinizer ignore-type */ $name, PATHINFO_BASENAME);
Loading history...
186
                if (! $hidden && $basename[0] === '.') {
187
                    continue;
188
                }
189
190
                if ($includeDir || ! $object->isDir()) {
191
                    if ($includePath === false) {
192
                        $files[] = $basename;
193
                    } elseif ($includePath === null) {
194
                        $files[] = str_replace($sourceDir, '', $name);
195
                    } else {
196
                        $files[] = $name;
197
                    }
198
                }
199
            }
200
        } catch (Throwable) {
201
            return [];
202
        }
203
204
        sort($files);
205
206
        return $files;
207
    }
208
}
209
210
if (! function_exists('get_dir_file_info')) {
211
    /**
212
     * Get Directory File Information
213
     *
214
     * Reads the specified directory and builds an array containing the filenames,
215
     * filesize, dates, and permissions
216
     *
217
     * Any sub-folders contained within the specified path are read as well.
218
     *
219
     * @param string $sourceDir    Path to source
220
     * @param bool   $topLevelOnly Look only at the top level directory specified?
221
     * @param bool   $recursion    Internal variable to determine recursion status - do not use in calls
222
     */
223
    function get_dir_file_info(string $sourceDir, bool $topLevelOnly = true, bool $recursion = false): array
224
    {
225
        static $fileData = [];
226
        $relativePath    = $sourceDir;
227
228
        try {
229
            $fp = opendir($sourceDir);
230
231
            // reset the array and make sure $source_dir has a trailing slash on the initial call
232
            if ($recursion === false) {
233
                $fileData  = [];
234
                $sourceDir = rtrim(realpath($sourceDir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
235
            }
236
237
            // Used to be foreach (scandir($source_dir, 1) as $file), but scandir() is simply not as fast
238
            while (false !== ($file = readdir($fp))) {
239
                if (is_dir($sourceDir . $file) && $file[0] !== '.' && $topLevelOnly === false) {
240
                    get_dir_file_info($sourceDir . $file . DIRECTORY_SEPARATOR, $topLevelOnly, true);
241
                } elseif ($file[0] !== '.') {
242
                    $fileData[$file]                  = get_file_info($sourceDir . $file);
243
                    $fileData[$file]['relative_path'] = $relativePath;
244
                }
245
            }
246
247
            closedir($fp);
248
249
            return $fileData;
250
        } catch (Throwable) {
251
            return [];
252
        }
253
    }
254
}
255
256
if (! function_exists('get_file_info')) {
257
    /**
258
     * Get File Info
259
     *
260
     * Given a file and path, returns the name, path, size, date modified
261
     * Second parameter allows you to explicitly declare what information you want returned
262
     * Options are: name, server_path, size, date, readable, writable, executable, fileperms
263
     * Returns false if the file cannot be found.
264
     *
265
     * @param string $file           Path to file
266
     * @param mixed  $returnedValues Array or comma separated string of information returned
267
     *
268
     * @return array|null
269
     */
270
    function get_file_info(string $file, $returnedValues = ['name', 'server_path', 'size', 'date'])
271
    {
272
        if (! is_file($file)) {
273
            return null;
274
        }
275
276
        $fileInfo = [];
277
278
        if (is_string($returnedValues)) {
279
            $returnedValues = explode(',', $returnedValues);
280
        }
281
282
        foreach ($returnedValues as $key) {
283
            switch ($key) {
284
                case 'name':
285
                    $fileInfo['name'] = basename($file);
286
                    break;
287
288
                case 'server_path':
289
                    $fileInfo['server_path'] = $file;
290
                    break;
291
292
                case 'size':
293
                    $fileInfo['size'] = filesize($file);
294
                    break;
295
296
                case 'date':
297
                    $fileInfo['date'] = filemtime($file);
298
                    break;
299
300
                case 'readable':
301
                    $fileInfo['readable'] = is_readable($file);
302
                    break;
303
304
                case 'writable':
305
                    $fileInfo['writable'] = is_really_writable($file);
306
                    break;
307
308
                case 'executable':
309
                    $fileInfo['executable'] = is_executable($file);
310
                    break;
311
312
                case 'fileperms':
313
                    $fileInfo['fileperms'] = fileperms($file);
314
                    break;
315
            }
316
        }
317
318
        return $fileInfo;
319
    }
320
}
321
322
if (! function_exists('symbolic_permissions')) {
323
    /**
324
     * Symbolic Permissions
325
     *
326
     * Takes a numeric value representing a file's permissions and returns
327
     * standard symbolic notation representing that value
328
     *
329
     * @param int $perms Permissions
330
     */
331
    function symbolic_permissions(int $perms): string
332
    {
333
        if (($perms & 0xC000) === 0xC000) {
334
            $symbolic = 's'; // Socket
335
        } elseif (($perms & 0xA000) === 0xA000) {
336
            $symbolic = 'l'; // Symbolic Link
337
        } elseif (($perms & 0x8000) === 0x8000) {
338
            $symbolic = '-'; // Regular
339
        } elseif (($perms & 0x6000) === 0x6000) {
340
            $symbolic = 'b'; // Block special
341
        } elseif (($perms & 0x4000) === 0x4000) {
342
            $symbolic = 'd'; // Directory
343
        } elseif (($perms & 0x2000) === 0x2000) {
344
            $symbolic = 'c'; // Character special
345
        } elseif (($perms & 0x1000) === 0x1000) {
346
            $symbolic = 'p'; // FIFO pipe
347
        } else {
348
            $symbolic = 'u'; // Unknown
349
        }
350
351
        // Owner
352
        $symbolic .= ((($perms & 0x0100) !== 0) ? 'r' : '-')
353
                . ((($perms & 0x0080) !== 0) ? 'w' : '-')
354
                . ((($perms & 0x0040) !== 0) ? ((($perms & 0x0800) !== 0) ? 's' : 'x') : ((($perms & 0x0800) !== 0) ? 'S' : '-'));
355
356
        // Group
357
        $symbolic .= ((($perms & 0x0020) !== 0) ? 'r' : '-')
358
                . ((($perms & 0x0010) !== 0) ? 'w' : '-')
359
                . ((($perms & 0x0008) !== 0) ? ((($perms & 0x0400) !== 0) ? 's' : 'x') : ((($perms & 0x0400) !== 0) ? 'S' : '-'));
360
361
        // World
362
        $symbolic .= ((($perms & 0x0004) !== 0) ? 'r' : '-')
363
                . ((($perms & 0x0002) !== 0) ? 'w' : '-')
364
                . ((($perms & 0x0001) !== 0) ? ((($perms & 0x0200) !== 0) ? 't' : 'x') : ((($perms & 0x0200) !== 0) ? 'T' : '-'));
365
366
        return $symbolic;
367
    }
368
}
369
370
if (! function_exists('octal_permissions')) {
371
    /**
372
     * Octal Permissions
373
     *
374
     * Takes a numeric value representing a file's permissions and returns
375
     * a three character string representing the file's octal permissions
376
     *
377
     * @param int $perms Permissions
378
     */
379
    function octal_permissions(int $perms): string
380
    {
381
        return substr(sprintf('%o', $perms), -3);
382
    }
383
}
384
385
if (! function_exists('same_file')) {
386
    /**
387
     * Checks if two files both exist and have identical hashes
388
     *
389
     * @return bool Same or not
390
     */
391
    function same_file(string $file1, string $file2): bool
392
    {
393
        return is_file($file1) && is_file($file2) && md5_file($file1) === md5_file($file2);
394
    }
395
}
396
397
if (! function_exists('set_realpath')) {
398
    /**
399
     * Set Realpath
400
     *
401
     * @param bool $checkExistence Checks to see if the path exists
402
     */
403
    function set_realpath(string $path, bool $checkExistence = false): string
404
    {
405
        // Security check to make sure the path is NOT a URL. No remote file inclusion!
406
        if (preg_match('#^(http:\/\/|https:\/\/|www\.|ftp)#i', $path) || filter_var($path, FILTER_VALIDATE_IP) === $path) {
407
            throw new InvalidArgumentException('The path you submitted must be a local server path, not a URL');
408
        }
409
410
        // Resolve the path
411
        if (realpath($path) !== false) {
412
            $path = realpath($path);
413
        } elseif ($checkExistence && ! is_dir($path) && ! is_file($path)) {
414
            throw new InvalidArgumentException('Not a valid path: ' . $path);
415
        }
416
417
        // Add a trailing slash, if this is a directory
418
        return is_dir($path) ? rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $path;
419
    }
420
}
421