function Filesystem::makePathRelative
Given an existing path, convert it to a path relative to a given starting path.
File
-
vendor/
symfony/ filesystem/ Filesystem.php, line 436
Class
- Filesystem
- Provides basic utility to manipulate the file system.
Namespace
Symfony\Component\FilesystemCode
public function makePathRelative(string $endPath, string $startPath) : string {
if (!$this->isAbsolutePath($startPath)) {
throw new InvalidArgumentException(\sprintf('The start path "%s" is not absolute.', $startPath));
}
if (!$this->isAbsolutePath($endPath)) {
throw new InvalidArgumentException(\sprintf('The end path "%s" is not absolute.', $endPath));
}
// Normalize separators on Windows
if ('\\' === \DIRECTORY_SEPARATOR) {
$endPath = str_replace('\\', '/', $endPath);
$startPath = str_replace('\\', '/', $startPath);
}
$splitDriveLetter = fn($path) => \strlen($path) > 2 && ':' === $path[1] && '/' === $path[2] && ctype_alpha($path[0]) ? [
substr($path, 2),
strtoupper($path[0]),
] : [
$path,
null,
];
$splitPath = function ($path) {
$result = [];
foreach (explode('/', trim($path, '/')) as $segment) {
if ('..' === $segment) {
array_pop($result);
}
elseif ('.' !== $segment && '' !== $segment) {
$result[] = $segment;
}
}
return $result;
};
[
$endPath,
$endDriveLetter,
] = $splitDriveLetter($endPath);
[
$startPath,
$startDriveLetter,
] = $splitDriveLetter($startPath);
$startPathArr = $splitPath($startPath);
$endPathArr = $splitPath($endPath);
if ($endDriveLetter && $startDriveLetter && $endDriveLetter != $startDriveLetter) {
// End path is on another drive, so no relative path exists
return $endDriveLetter . ':/' . ($endPathArr ? implode('/', $endPathArr) . '/' : '');
}
// Find for which directory the common path stops
$index = 0;
while (isset($startPathArr[$index]) && isset($endPathArr[$index]) && $startPathArr[$index] === $endPathArr[$index]) {
++$index;
}
// Determine how deep the start path is relative to the common path (ie, "web/bundles" = 2 levels)
if (1 === \count($startPathArr) && '' === $startPathArr[0]) {
$depth = 0;
}
else {
$depth = \count($startPathArr) - $index;
}
// Repeated "../" for each level need to reach the common path
$traverser = str_repeat('../', $depth);
$endPathRemainder = implode('/', \array_slice($endPathArr, $index));
// Construct $endPath from traversing to the common path, then to the remaining $endPath
$relativePath = $traverser . ('' !== $endPathRemainder ? $endPathRemainder . '/' : '');
return '' === $relativePath ? './' : $relativePath;
}