]> BookStack Code Mirror - system-cli/blob - src/Services/Paths.php
Bumped version
[system-cli] / src / Services / Paths.php
1 <?php declare(strict_types=1);
2
3 namespace Cli\Services;
4
5 class Paths
6 {
7
8     /**
9      * Get the full real path, resolving symbolic links, to
10      * the existing file/directory of the given path.
11      * @throws \Exception
12      */
13     public static function real(string $path): string
14     {
15         $real = realpath($path);
16         if ($real === false) {
17             throw new \Exception("Path {$path} could not be resolved to a location on the filesystem");
18         }
19
20         return $real;
21     }
22
23     /**
24      * Join together the given path components.
25      * Does no resolving or cleaning.
26      * Only the $base will remain absolute if so,
27      * $parts are assumed to treated as non-absolute paths.
28      */
29     public static function join(string $base, string ...$parts): string
30     {
31         $outParts = [rtrim($base, '/\\')];
32         foreach ($parts as $part) {
33             $outParts[] = trim($part, '/\\');
34         }
35
36         return implode(DIRECTORY_SEPARATOR, $outParts);
37     }
38
39     /**
40      * Resolve the full path for the given path/sub-path.
41      * If the provided path is not absolute, it will be returned
42      * be resolved to the provided $base or current working directory if
43      * no $base is provided.
44      */
45     public static function resolve(string $path, string $base = ''): string
46     {
47         if (str_starts_with($path, '/') || str_starts_with($path, '\\')) {
48             return DIRECTORY_SEPARATOR . self::clean($path);
49         }
50
51         $base = rtrim($base ?: getcwd(), '/');
52         $joined = $base . '/' . $path;
53         $absoluteBase = (str_starts_with($base, '/') || str_starts_with($base, '\\'));
54         return ($absoluteBase ? '/' : '') . self::clean($joined);
55     }
56
57     /**
58      * Clean the given path so that all up/down navigations are resolved,
59      * and so its using system-correct directory separators.
60      * Credit to Sven Arduwie in PHP docs:
61      * https://www.php.net/manual/en/function.realpath.php#84012
62      */
63     private static function clean(string $path): string
64     {
65         $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
66         $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
67         $absolutes = [];
68         foreach ($parts as $part) {
69             if ('.' == $part) continue;
70             if ('..' == $part) {
71                 array_pop($absolutes);
72             } else {
73                 $absolutes[] = $part;
74             }
75         }
76         return implode(DIRECTORY_SEPARATOR, $absolutes);
77     }
78 }