1 <?php declare(strict_types=1);
3 namespace Cli\Services;
9 * Join together the given path components.
10 * Does no resolving or cleaning.
11 * Only the $base will remain absolute if so,
12 * $parts are assumed to treated as non-absolute paths.
14 public static function join(string $base, string ...$parts): string
16 $outParts = [rtrim($base, '/\\')];
17 foreach ($parts as $part) {
18 $outParts[] = trim($part, '/\\');
21 return implode(DIRECTORY_SEPARATOR, $outParts);
25 * Resolve the full path for the given path/sub-path.
26 * If the provided path is not absolute, it will be returned
27 * be resolved to the provided $base or current working directory if
28 * no $base is provided.
30 public static function resolve(string $path, string $base = ''): string
32 if (str_starts_with($path, '/') || str_starts_with($path, '\\')) {
33 return DIRECTORY_SEPARATOR . self::clean($path);
36 $base = rtrim($base ?: getcwd(), '/');
37 $joined = $base . '/' . $path;
38 $absoluteBase = (str_starts_with($base, '/') || str_starts_with($base, '\\'));
39 return ($absoluteBase ? '/' : '') . self::clean($joined);
43 * Clean the given path so that all up/down navigations are resolved,
44 * and so its using system-correct directory separators.
45 * Credit to Sven Arduwie in PHP docs:
46 * https://www.php.net/manual/en/function.realpath.php#84012
48 private static function clean(string $path): string
50 $path = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path);
51 $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
53 foreach ($parts as $part) {
54 if ('.' == $part) continue;
56 array_pop($absolutes);
61 return implode(DIRECTORY_SEPARATOR, $absolutes);