1 <?php declare(strict_types=1);
3 namespace Cli\Services;
9 * Get the full real path, resolving symbolic links, to
10 * the existing file/directory of the given path.
13 public static function real(string $path): string
15 $real = realpath($path);
16 if ($real === false) {
17 throw new \Exception("Path {$path} could not be resolved to a location on the filesystem");
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.
29 public static function join(string $base, string ...$parts): string
31 $outParts = [rtrim($base, '/\\')];
32 foreach ($parts as $part) {
33 $outParts[] = trim($part, '/\\');
36 return implode(DIRECTORY_SEPARATOR, $outParts);
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.
45 public static function resolve(string $path, string $base = ''): string
47 if (str_starts_with($path, '/') || str_starts_with($path, '\\')) {
48 return DIRECTORY_SEPARATOR . self::clean($path);
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);
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
63 private static function clean(string $path): string
65 $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
66 $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
68 foreach ($parts as $part) {
69 if ('.' == $part) continue;
71 array_pop($absolutes);
76 return implode(DIRECTORY_SEPARATOR, $absolutes);