我自己也遇到过这个问题,主要是关于路径的规范化。
归一化是:
- 一个分隔符(我选择支持,但从不返回反斜杠
\\)
- 解析间接:
/../
- 删除重复的分隔符:
/home/www/uploads//file.ext
- 始终删除尾随分隔符。
我已经编写了一个函数来实现这一点。我现在无权访问该代码,但自己编写也没有那么难。
路径是否是绝对的对于这个规范化函数的实现并不重要,只要注意前导分隔符就可以了。
我不太担心操作系统的依赖性。 Windows 和 Linux PHP 都理解 /,所以为了简单起见,我总是使用它 - 但我想你使用什么分隔符并不重要。
回答您的问题:如果您始终使用/ 并假设目录没有尾随分隔符,则路径连接会非常容易。 “没有尾随分隔符”似乎是一个很好的假设,因为像 dirname 这样的函数会删除尾随分隔符。
那么这样做总是安全的:$dir . "/" . $file。
即使结果路径是/home/uploads/../uploads//my_uploads/myfile.ext,它仍然可以正常工作。
当您需要将路径存储在某处时,规范化变得有用。因为你有这个归一化函数,所以你可以做出这些假设。
另一个有用的功能是创建相对路径的功能。
- /文件/上传
- /files/uploads/my_uploads/myfile.ext
从这两个路径派生文件的相对路径是什么很有用。
真实路径
我发现realpath 的性能非常重要。如果你只调用一次它并没有那么糟糕,但是如果你在某个地方循环调用它,你会得到很大的打击。请记住,每个realpath 调用也是对文件系统的调用。另外,如果你传入一些愚蠢的东西,它只会返回false,我宁愿让它抛出一个异常。
对我来说,realpath 函数是 BAD 函数 的一个很好的例子,因为它做了两件事:1. 规范化路径,2. 检查路径是否存在。这两个功能当然有用,但必须分开。它也不区分文件和目录。对于 Windows,这通常不是问题,但对于 Linux 则可能。
而且我认为在 Windows 上使用 realpath("") 时有些古怪。我认为它会返回 \\ - 这可能是非常不可接受的。
/**
* This function is a proper replacement for realpath
* It will _only_ normalize the path and resolve indirections (.. and .)
* Normalization includes:
* - directiory separator is always /
* - there is never a trailing directory separator
* @param $path
* @return String
*/
function normalize_path($path) {
$parts = preg_split(":[\\\/]:", $path); // split on known directory separators
// resolve relative paths
for ($i = 0; $i < count($parts); $i +=1) {
if ($parts[$i] === "..") { // resolve ..
if ($i === 0) {
throw new Exception("Cannot resolve path, path seems invalid: `" . $path . "`");
}
unset($parts[$i - 1]);
unset($parts[$i]);
$parts = array_values($parts);
$i -= 2;
} else if ($parts[$i] === ".") { // resolve .
unset($parts[$i]);
$parts = array_values($parts);
$i -= 1;
}
if ($i > 0 && $parts[$i] === "") { // remove empty parts
unset($parts[$i]);
$parts = array_values($parts);
}
}
return implode("/", $parts);
}
/**
* Removes base path from longer path. The resulting path will never contain a leading directory separator
* Base path must occur in longer path
* Paths will be normalized
* @throws Exception
* @param $base_path
* @param $longer_path
* @return string normalized relative path
*/
function make_relative_path($base_path, $longer_path) {
$base_path = normalize_path($base_path);
$longer_path = normalize_path($longer_path);
if (0 !== strpos($longer_path, $base_path)) {
throw new Exception("Can not make relative path, base path does not occur at 0 in longer path: `" . $base_path . "`, `" . $longer_path . "`");
}
return substr($longer_path, strlen($base_path) + 1);
}