Passed
Pull Request — master (#56)
by Théo
02:16
created

FileSystem::remove()   C

Complexity

Conditions 15
Paths 24

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 31
rs 5.0504
c 0
b 0
f 0
cc 15
eloc 20
nc 24
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
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\FileSystem;
16
17
use Assert\Assertion;
18
use Symfony\Component\Filesystem\Exception\IOException;
19
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
20
use Webmozart\PathUtil\Path;
21
22
/**
23
 * @author Fabien Potencier <[email protected]>
24
 * @author Bernhard Schussek <[email protected]>
25
 * @author Thomas Schulz <[email protected]>
26
 */
27
final class FileSystem extends SymfonyFilesystem
28
{
29
    /**
30
     * Canonicalizes the given path.
31
     *
32
     * During normalization, all slashes are replaced by forward slashes ("/").
33
     * Furthermore, all "." and ".." segments are removed as far as possible.
34
     * ".." segments at the beginning of relative paths are not removed.
35
     *
36
     * ```php
37
     * echo Path::canonicalize("\webmozart\puli\..\css\style.css");
38
     * // => /webmozart/css/style.css
39
     *
40
     * echo Path::canonicalize("../css/./style.css");
41
     * // => ../css/style.css
42
     * ```
43
     *
44
     * This method is able to deal with both UNIX and Windows paths.
45
     *
46
     * @param string $path A path string
47
     *
48
     * @return string The canonical path
49
     */
50
    public function canonicalize(string $path): string
51
    {
52
        $lastChar = substr($path, -1);
53
54
        $canonical = Path::canonicalize($path);
55
56
        return '/' === $lastChar ? $canonical.$lastChar : $canonical;
57
    }
58
59
    /**
60
     * Normalizes the given path.
61
     *
62
     * During normalization, all slashes are replaced by forward slashes ("/").
63
     * Contrary to {@link canonicalize()}, this method does not remove invalid
64
     * or dot path segments. Consequently, it is much more efficient and should
65
     * be used whenever the given path is known to be a valid, absolute system
66
     * path.
67
     *
68
     * This method is able to deal with both UNIX and Windows paths.
69
     *
70
     * @param string $path a path string
71
     *
72
     * @return string the normalized path
73
     */
74
    public function normalize(string $path): string
75
    {
76
        return Path::normalize($path);
77
    }
78
79
    /**
80
     * Returns the directory part of the path.
81
     *
82
     * This method is similar to PHP's dirname(), but handles various cases
83
     * where dirname() returns a weird result:
84
     *
85
     *  - dirname() does not accept backslashes on UNIX
86
     *  - dirname("C:/webmozart") returns "C:", not "C:/"
87
     *  - dirname("C:/") returns ".", not "C:/"
88
     *  - dirname("C:") returns ".", not "C:/"
89
     *  - dirname("webmozart") returns ".", not ""
90
     *  - dirname() does not canonicalize the result
91
     *
92
     * This method fixes these shortcomings and behaves like dirname()
93
     * otherwise.
94
     *
95
     * The result is a canonical path.
96
     *
97
     * @param string $path a path string
98
     *
99
     * @return string The canonical directory part. Returns the root directory
100
     *                if the root directory is passed. Returns an empty string
101
     *                if a relative path is passed that contains no slashes.
102
     *                Returns an empty string if an empty string is passed.
103
     */
104
    public function getDirectory(string $path): string
105
    {
106
        return Path::getDirectory($path);
107
    }
108
109
    /**
110
     * Returns canonical path of the user's home directory.
111
     *
112
     * Supported operating systems:
113
     *
114
     *  - UNIX
115
     *  - Windows8 and upper
116
     *
117
     * If your operation system or environment isn't supported, an exception is thrown.
118
     *
119
     * The result is a canonical path.
120
     *
121
     * @return string The canonical home directory
122
     */
123
    public function getHomeDirectory(): string
124
    {
125
        return Path::getHomeDirectory();
126
    }
127
128
    /**
129
     * Returns the root directory of a path.
130
     *
131
     * The result is a canonical path.
132
     *
133
     * @param string $path a path string
134
     *
135
     * @return string The canonical root directory. Returns an empty string if
136
     *                the given path is relative or empty.
137
     */
138
    public function getRoot(string $path): string
139
    {
140
        return Path::getRoot($path);
141
    }
142
143
    /**
144
     * Returns the file name from a file path.
145
     *
146
     * @param string $path the path string
147
     *
148
     * @return string The file name
149
     */
150
    public function getFilename(string $path): string
151
    {
152
        return Path::getFilename($path);
153
    }
154
155
    /**
156
     * Returns the file name without the extension from a file path.
157
     *
158
     * @param string      $path      the path string
159
     * @param null|string $extension if specified, only that extension is cut
160
     *                               off (may contain leading dot)
161
     *
162
     * @return string the file name without extension
163
     */
164
    public function getFilenameWithoutExtension($path, $extension = null)
165
    {
166
        return Path::getFilenameWithoutExtension($path, $extension);
167
    }
168
169
    /**
170
     * Returns the extension from a file path.
171
     *
172
     * @param string $path           the path string
173
     * @param bool   $forceLowerCase forces the extension to be lower-case
174
     *                               (requires mbstring extension for correct
175
     *                               multi-byte character handling in extension)
176
     *
177
     * @return string the extension of the file path (without leading dot)
178
     */
179
    public function getExtension(string $path, bool $forceLowerCase = false): string
180
    {
181
        return Path::getExtension($path, $forceLowerCase);
182
    }
183
184
    /**
185
     * Returns whether the path has an extension.
186
     *
187
     * @param string            $path       the path string
188
     * @param null|array|string $extensions if null or not provided, checks if
189
     *                                      an extension exists, otherwise
190
     *                                      checks for the specified extension
191
     *                                      or array of extensions (with or
192
     *                                      without leading dot)
193
     * @param bool              $ignoreCase whether to ignore case-sensitivity
194
     *                                      (requires mbstring extension for
195
     *                                      correct multi-byte character
196
     *                                      handling in the extension)
197
     *
198
     * @return bool returns `true` if the path has an (or the specified)
199
     *              extension and `false` otherwise
200
     */
201
    public function hasExtension(string $path, $extensions = null, bool $ignoreCase = false): bool
202
    {
203
        return Path::hasExtension($path, $extensions, $ignoreCase);
204
    }
205
206
    /**
207
     * Changes the extension of a path string.
208
     *
209
     * @param string $path      The path string with filename.ext to change.
210
     * @param string $extension new extension (with or without leading dot)
211
     *
212
     * @return string the path string with new file extension
213
     */
214
    public function changeExtension(string $path, string $extension): string
215
    {
216
        return Path::changeExtension($path, $extension);
217
    }
218
219
    /**
220
     * Returns whether a path is relative.
221
     *
222
     * @param string $path a path string
223
     *
224
     * @return bool returns true if the path is relative or empty, false if
225
     *              it is absolute
226
     */
227
    public function isRelativePath(string $path): bool
228
    {
229
        return !$this->isAbsolutePath($path);
230
    }
231
232
    /**
233
     * Turns a relative path into an absolute path.
234
     *
235
     * Usually, the relative path is appended to the given base path. Dot
236
     * segments ("." and "..") are removed/collapsed and all slashes turned
237
     * into forward slashes.
238
     *
239
     * ```php
240
     * echo Path::makeAbsolute("../style.css", "/webmozart/puli/css");
241
     * // => /webmozart/puli/style.css
242
     * ```
243
     *
244
     * If an absolute path is passed, that path is returned unless its root
245
     * directory is different than the one of the base path. In that case, an
246
     * exception is thrown.
247
     *
248
     * ```php
249
     * Path::makeAbsolute("/style.css", "/webmozart/puli/css");
250
     * // => /style.css
251
     *
252
     * Path::makeAbsolute("C:/style.css", "C:/webmozart/puli/css");
253
     * // => C:/style.css
254
     *
255
     * Path::makeAbsolute("C:/style.css", "/webmozart/puli/css");
256
     * // InvalidArgumentException
257
     * ```
258
     *
259
     * If the base path is not an absolute path, an exception is thrown.
260
     *
261
     * The result is a canonical path.
262
     *
263
     * @param string $path     a path to make absolute
264
     * @param string $basePath an absolute base path
265
     *
266
     * @return string an absolute path in canonical form
267
     */
268
    public function makeAbsolute(string $path, string $basePath): string
269
    {
270
        return Path::makeAbsolute($path, $basePath);
271
    }
272
273
    /**
274
     * Turns a path into a relative path.
275
     *
276
     * The relative path is created relative to the given base path:
277
     *
278
     * ```php
279
     * echo Path::makeRelative("/webmozart/style.css", "/webmozart/puli");
280
     * // => ../style.css
281
     * ```
282
     *
283
     * If a relative path is passed and the base path is absolute, the relative
284
     * path is returned unchanged:
285
     *
286
     * ```php
287
     * Path::makeRelative("style.css", "/webmozart/puli/css");
288
     * // => style.css
289
     * ```
290
     *
291
     * If both paths are relative, the relative path is created with the
292
     * assumption that both paths are relative to the same directory:
293
     *
294
     * ```php
295
     * Path::makeRelative("style.css", "webmozart/puli/css");
296
     * // => ../../../style.css
297
     * ```
298
     *
299
     * If both paths are absolute, their root directory must be the same,
300
     * otherwise an exception is thrown:
301
     *
302
     * ```php
303
     * Path::makeRelative("C:/webmozart/style.css", "/webmozart/puli");
304
     * // InvalidArgumentException
305
     * ```
306
     *
307
     * If the passed path is absolute, but the base path is not, an exception
308
     * is thrown as well:
309
     *
310
     * ```php
311
     * Path::makeRelative("/webmozart/style.css", "webmozart/puli");
312
     * // InvalidArgumentException
313
     * ```
314
     *
315
     * If the base path is not an absolute path, an exception is thrown.
316
     *
317
     * The result is a canonical path.
318
     *
319
     * @param string $path     a path to make relative
320
     * @param string $basePath a base path
321
     *
322
     * @return string a relative path in canonical form
323
     */
324
    public function makeRelative(string $path, string $basePath): string
325
    {
326
        return Path::makeRelative($path, $basePath);
327
    }
328
329
    /**
330
     * Returns whether the given path is on the local filesystem.
331
     *
332
     * @param string $path a path string
333
     *
334
     * @return bool returns true if the path is local, false for a URL
335
     */
336
    public function isLocal(string $path): bool
337
    {
338
        return Path::isLocal($path);
339
    }
340
341
    /**
342
     * Returns the longest common base path of a set of paths.
343
     *
344
     * Dot segments ("." and "..") are removed/collapsed and all slashes turned
345
     * into forward slashes.
346
     *
347
     * ```php
348
     * $basePath = Path::getLongestCommonBasePath(array(
349
     *     '/webmozart/css/style.css',
350
     *     '/webmozart/css/..'
351
     * ));
352
     * // => /webmozart
353
     * ```
354
     *
355
     * The root is returned if no common base path can be found:
356
     *
357
     * ```php
358
     * $basePath = Path::getLongestCommonBasePath(array(
359
     *     '/webmozart/css/style.css',
360
     *     '/puli/css/..'
361
     * ));
362
     * // => /
363
     * ```
364
     *
365
     * If the paths are located on different Windows partitions, `null` is
366
     * returned.
367
     *
368
     * ```php
369
     * $basePath = Path::getLongestCommonBasePath(array(
370
     *     'C:/webmozart/css/style.css',
371
     *     'D:/webmozart/css/..'
372
     * ));
373
     * // => null
374
     * ```
375
     *
376
     * @param array $paths a list of paths
377
     *
378
     * @return null|string the longest common base path in canonical form or
379
     *                     `null` if the paths are on different Windows
380
     *                     partitions
381
     */
382
    public function getLongestCommonBasePath(array $paths): ?string
383
    {
384
        return Path::getLongestCommonBasePath($paths);
385
    }
386
387
    /**
388
     * Joins two or more path strings.
389
     *
390
     * The result is a canonical path.
391
     *
392
     * @param string|string[] $paths path parts as parameters or array
393
     *
394
     * @return string the joint path
395
     */
396
    public function join($paths): string
397
    {
398
        return Path::join($paths);
399
    }
400
401
    /**
402
     * Returns whether a path is a base path of another path.
403
     *
404
     * Dot segments ("." and "..") are removed/collapsed and all slashes turned
405
     * into forward slashes.
406
     *
407
     * ```php
408
     * Path::isBasePath('/webmozart', '/webmozart/css');
409
     * // => true
410
     *
411
     * Path::isBasePath('/webmozart', '/webmozart');
412
     * // => true
413
     *
414
     * Path::isBasePath('/webmozart', '/webmozart/..');
415
     * // => false
416
     *
417
     * Path::isBasePath('/webmozart', '/puli');
418
     * // => false
419
     * ```
420
     *
421
     * @param string $basePath the base path to test
422
     * @param string $ofPath   the other path
423
     *
424
     * @return bool whether the base path is a base path of the other path
425
     */
426
    public function isBasePath(string $basePath, string $ofPath): bool
427
    {
428
        return Path::isBasePath($basePath, $ofPath);
429
    }
430
431
    public function escapePath(string $path): string
432
    {
433
        return str_replace('/', DIRECTORY_SEPARATOR, $path);
434
    }
435
436
    /**
437
     * Gets the contents of a file.
438
     *
439
     * @param string $file File path
440
     *
441
     * @throws IOException If the file cannot be read
442
     *
443
     * @return string File contents
444
     */
445
    public function getFileContents(string $file): string
446
    {
447
        Assertion::file($file);
448
        Assertion::readable($file);
449
450
        if (false === ($contents = @file_get_contents($file))) {
0 ignored issues
show
introduced by
The condition false === $contents = @file_get_contents($file) can never be true.
Loading history...
451
            throw new IOException(
452
                sprintf(
453
                    'Failed to read file "%s": %s.',
454
                    $file,
455
                    error_get_last()['message']
456
                ),
457
                0,
458
                null,
459
                $file
460
            );
461
        }
462
463
        return $contents;
464
    }
465
466
    /**
467
     * Creates a temporary directory.
468
     *
469
     * @param string $namespace the directory path in the system's temporary directory
470
     * @param string $className the name of the test class
471
     *
472
     * @return string the path to the created directory
473
     */
474
    public function makeTmpDir(string $namespace, string $className): string
475
    {
476
        if (false !== ($pos = strrpos($className, '\\'))) {
0 ignored issues
show
introduced by
The condition false !== $pos = strrpos($className, '\') can never be false.
Loading history...
477
            $shortClass = substr($className, $pos + 1);
478
        } else {
479
            $shortClass = $className;
480
        }
481
482
        // Usage of realpath() is important if the temporary directory is a
483
        // symlink to another directory (e.g. /var => /private/var on some Macs)
484
        // We want to know the real path to avoid comparison failures with
485
        // code that uses real paths only
486
        $systemTempDir = str_replace('\\', '/', realpath(sys_get_temp_dir()));
487
        $basePath = $systemTempDir.'/'.$namespace.'/'.$shortClass;
488
489
        while (false === @mkdir($tmpDir = $this->escapePath($basePath.random_int(10000, 99999)), 0777, true)) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of mkdir($tmpDir = $this->e...00, 99999)), 511, true) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Unused Code introduced by
The call to KevinGH\Box\FileSystem\mkdir() has too many arguments starting with true. ( Ignorable by Annotation )

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

489
        while (false === @/** @scrutinizer ignore-call */ mkdir($tmpDir = $this->escapePath($basePath.random_int(10000, 99999)), 0777, true)) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
490
            // Run until we are able to create a directory
491
        }
492
493
        return $tmpDir;
494
    }
495
496
    /**
497
     * Removes files or directories.
498
     *
499
     * @param iterable|string $files A filename, an array of files, or a \Traversable instance to remove
500
     *
501
     * @throws IOException When removal fails
502
     */
503
    public function remove($files): void
504
    {
505
        if ($files instanceof \Traversable) {
506
            $files = iterator_to_array($files, false);
507
        } elseif (!is_array($files)) {
508
            $files = [$files];
509
        }
510
        $files = array_reverse($files);
511
        foreach ($files as $file) {
512
            if (defined('PHP_WINDOWS_VERSION_BUILD')) {
513
                //TODO: https://github.com/humbug/php-scoper/pull/19/files#r118838268
514
                exec(sprintf('rd /s /q %s', escapeshellarg($file)));
515
            } elseif (is_link($file)) {
516
                // See https://bugs.php.net/52176
517
                if (!@(unlink($file) || '\\' !== DIRECTORY_SEPARATOR || rmdir($file)) && file_exists($file)) {
518
                    $error = error_get_last();
519
520
                    throw new IOException(sprintf('Failed to remove symlink "%s": %s.', $file, $error['message']));
521
                }
522
            } elseif (is_dir($file)) {
523
                $this->remove(new \FilesystemIterator($file, \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::SKIP_DOTS));
524
525
                if (!@rmdir($file) && file_exists($file)) {
526
                    $error = error_get_last();
527
528
                    throw new IOException(sprintf('Failed to remove directory "%s": %s.', $file, $error['message']));
529
                }
530
            } elseif (!@unlink($file) && file_exists($file)) {
531
                $error = error_get_last();
532
533
                throw new IOException(sprintf('Failed to remove file "%s": %s.', $file, $error['message']));
534
            }
535
        }
536
    }
537
}
538