Passed
Push — master ( 22acbe...a6e730 )
by Théo
02:08
created

FileSystem::isLocal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

490
            /** @scrutinizer ignore-call */ 
491
            $result = $this->mkdir($tmpDir, 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...
Bug introduced by
Are you sure the assignment to $result is correct as $this->mkdir($tmpDir, 511, true) targeting Symfony\Component\Filesystem\Filesystem::mkdir() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

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