在建構資源路徑、產生相對連結、遷移檔案系統時,常需計算「路徑 B 相對於路徑 A 的相對路徑」。PHP 內建 realpath() 只能取得絕對路徑,無相對路徑計算函式。本文提供通用工具函式實作與邏輯解析。
/ 分割為陣列。.. (上層目錄)。.. 序列與路徑 B 剩餘段。<?php
/**
* 計算 $targetPath 相對於 $basePath 的相對路徑
*
* @param string $basePath 基準路徑 (通常為目錄,以 / 結尾)
* @param string $targetPath 目標路徑 (檔案或目錄)
* @return string 相對路徑 (如: ../../folder/file.php)
*/
function getRelativePath(string $basePath, string $targetPath): string {
// 1. 正規化:移除尾部斜線、統一分隔符
$basePath = rtrim(str_replace('\\', '/', $basePath), '/');
$targetPath = rtrim(str_replace('\\', '/', $targetPath), '/');
// 2. 若為檔案路徑,basePath 取目錄部分
if (!is_dir($basePath) && strpos($basePath, '.') !== false) {
$basePath = dirname($basePath);
}
// 3. 分割路徑段
$baseParts = explode('/', $basePath);
$targetParts = explode('/', $targetPath);
// 4. 找出共同前綴長度
$commonLength = 0;
$minLength = min(count($baseParts), count($targetParts));
for ($i = 0; $i < $minLength; $i++) {
if ($baseParts[$i] === $targetParts[$i]) {
$commonLength++;
} else {
break;
}
}
// 5. 構建相對路徑
$upLevels = count($baseParts) - $commonLength;
$relativeParts = array_merge(
array_fill(0, $upLevels, '..'), // 上層目錄序列
array_slice($targetParts, $commonLength) // 目標剩餘段
);
// 6. 特殊情況:同一目錄
if (empty($relativeParts)) {
return '.';
}
return implode('/', $relativeParts);
}
// 測試範例
$a = '/a/b/c/d/e.php';
$b = '/a/b/12/34/c.php';
echo getRelativePath($a, $b); // 輸出: ../../12/34/c.php
// 更多測試
var_dump(getRelativePath('/var/www/html', '/var/www/html/index.php')); // index.php
var_dump(getRelativePath('/var/www/html/index.php', '/var/www/html')); // ..
var_dump(getRelativePath('/a/b/c', '/x/y/z')); // ../../../x/y/z
var_dump(getRelativePath('/a/b/c/', '/a/b/c/d/e.php')); // d/e.php
?>
<?php
function getRelativePathAdvanced($base, $target, $isUrl = false) {
$sep = $isUrl ? '/' : DIRECTORY_SEPARATOR;
$base = rtrim(str_replace(['\\', '/'], $sep, $base), $sep);
$target = rtrim(str_replace(['\\', '/'], $sep, $target), $sep);
// URL 不檢查 is_dir,直接按字串處理
if (!$isUrl && !is_dir($base) && pathinfo($base, PATHINFO_EXTENSION)) {
$base = dirname($base);
}
$baseParts = explode($sep, $base);
$targetParts = explode($sep, $target);
$common = 0;
foreach ($baseParts as $i => $part) {
if (isset($targetParts[$i]) && $targetParts[$i] === $part) {
$common++;
} else break;
}
$up = count($baseParts) - $common;
$rel = array_merge(array_fill(0, $up, '..'), array_slice($targetParts, $common));
return $rel ? implode($sep, $rel) : '.';
}
?>
若基準路徑為目錄,可利用 realpath() + 字串處理:
<?php
function getRelativePathRealpath($baseDir, $targetPath) {
$baseDir = rtrim(realpath($baseDir), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$targetPath = realpath($targetPath);
if (strpos($targetPath, $baseDir) === 0) {
return substr($targetPath, strlen($baseDir));
}
// 不在同一樹下,退回通用演算法
return getRelativePath($baseDir, $targetPath);
}
?>
<base href="..."> 計算資源相對路徑。include、extends 路徑解析。