在構建資源引用、模板引擎、檔案管理器時,常需根據「基準路徑 A」計算「目標路徑 B」相對於 A 的相對路徑(如 ../../target/file.txt)。
<?php
/**
* 獲取 $target 相對於 $base 的相對路徑
* @param string $base 基準路徑(目錄或文件)
* @param string $target 目標路徑(目錄或文件)
* @return string 相對路徑,如 ../foo/bar.txt
*/
function getRelativePath($base, $target) {
// 統一處理為目錄路徑
$baseDir = rtrim(dirname($base), '/');
$targetDir = rtrim(dirname($target), '/');
// 分割路徑段
$baseParts = $baseDir ? explode('/', $baseDir) : [];
$targetParts = $targetDir ? explode('/', $targetDir) : [];
// 找到共同前綴長度
$common = 0;
$minLen = min(count($baseParts), count($targetParts));
while ($common < $minLen && $baseParts[$common] === $targetParts[$common]) {
$common++;
}
// 構建相對路徑:向上返回 + 向下進入
$up = str_repeat('../', count($baseParts) - $common);
$down = implode('/', array_slice($targetParts, $common));
$relative = $up . $down;
return $relative ?: '.'; // 同目錄返回 .
}
// 測試
echo getRelativePath('/var/www/a/b/c.php', '/var/www/a/d/e.txt'); // ../d/e.txt
echo getRelativePath('/home/user/project/src/Controller', '/home/user/project/public/assets'); // ../../public/assets
echo getRelativePath('C:/xampp/htdocs/app', 'C:/xampp/htdocs/app'); // .
?>
Base: /var/www/a/b/ Target: /var/www/a/d/e.txt Common: /var/www/a/ (3 segments) Up: ../ (b -> a) Down: d/e.txt Result: ../d/e.txt
<?php
function getRelativePathUniversal($base, $target, $isUrl = false) {
$sep = $isUrl ? '/' : DIRECTORY_SEPARATOR;
// 正規化分隔符
$base = str_replace(['\\', '/'], $sep, $base);
$target = str_replace(['\\', '/'], $sep, $target);
// 去除尾部分隔符
$base = rtrim($base, $sep);
$target = rtrim($target, $sep);
// 取目錄部分
$baseDir = $isUrl ? dirname($base) : dirname($base);
$targetDir = $isUrl ? dirname($target) : dirname($target);
// 同邏輯...
// (省略,核心同上)
return $relative;
}
?>
realpath() 先解析符號鏈接、相對路徑,再計算。Symfony\Component\Filesystem\Path::getRelativePath($base, $target) (PHP 8.1+ 核心已內建 path_relative() 提案中)。Illuminate\Support\Str::relativePath($target, $base)。dirname() 取目錄,否則會多算一層。\ 需統一轉換。