專案中需動態生成引用路徑 (如 include、require、資源連結),已知絕對路徑 A、B,求 A 相對於 B 的相對路徑。
<?php
/**
* 計算 $path1 相對於 $path2 的相對路徑
* @param string $path1 目標檔案絕對路徑
* @param string $path2 基準檔案絕對路徑
* @return string 相對路徑 (如 ../../lib/img/cache.php)
*/
function getRelativePath($path1, $path2) {
// 正規化:統一分隔符、移除尾部斜線
$path1 = rtrim(str_replace('\\', '/', $path1), '/');
$path2 = rtrim(str_replace('\\', '/', $path2), '/');
$arr1 = explode('/', $path1);
$arr2 = explode('/', $path2);
// 找共同前綴深度
$depth = 0;
$min = min(count($arr1), count($arr2));
for ($i = 0; $i < $min; $i++) {
if ($arr1[$i] !== $arr2[$i]) break;
$depth++;
}
// path2 需向上層數 = 總層數 - 共同層數 - 1 (檔案層)
$up = count($arr2) - $depth - 1;
// path1 保留共同層之後的片段
$down = array_slice($arr1, $depth);
// 組合:../ 重複 $up 次 + 下層片段
return implode('/', array_merge(array_fill(0, $up, '..'), $down));
}
// 測試
$path1 = '/home/web/lib/img/cache.php';
$path2 = '/home/web/api/img/show.php';
echo getRelativePath($path1, $path2); // ../../lib/img/cache.php
| 情況 | 處理 |
|---|---|
| 同一目錄 | 返回檔名 (如 cache.php) |
| path1 是 path2 祖先目錄 | 返回 ../.. 等 |
| 無共同前綴 (不同磁碟/掛載點) | 返回絕對路徑或拋出異常 |
| Windows 路徑 | 統一轉 / 處理,結果用 DIRECTORY_SEPARATOR 還原 |
use Symfony\Component\String\LazyString;
$relative = (new LazyString($path1))->getRelativePath($path2);
// 或使用 Symfony\Filesystem\Path::getRelativePath()
getRelativePath 以路徑分段比對共同前綴、計算向上層數、拼接下層片段,邏輯清晰、無外部依賴。專案中建議封裝為工具類靜態方法,配合單元測試覆蓋邊界情況,確保路徑生成可靠。