脚本.php
$filename = realpath(sprintf("%s/%s", getcwd(), $argv[1]));
var_dump($filename);
让我们尝试一些事情
[/foo/bar/bof] $ php script.php ../foo.txt
string(16) "/foo/bar/foo.txt"
[/foo/bar/bof] $ php script.php ../nonexistent.txt
bool(false)
该死!
realpath
返回 false,因为文件不存在。
我想看到的
../nonexistent.txt
是
string(24) "/foo/bar/nonexistent.txt"
如何获取 PHP 中任何相对路径的规范化路径?
注意:我看到了一些有关解析符号链接路径的问题。这些问题的答案不适用于我的问题。
这是我能想到的最好的了
function canonicalize_path($path, $cwd=null) {
// don't prefix absolute paths
if (substr($path, 0, 1) === "/") {
$filename = $path;
}
// prefix relative path with $root
else {
$root = is_null($cwd) ? getcwd() : $cwd;
$filename = sprintf("%s/%s", $root, $path);
}
// get realpath of dirname
$dirname = dirname($filename);
$canonical = realpath($dirname);
// trigger error if $dirname is nonexistent
if ($canonical === false) {
trigger_error(sprintf("Directory `%s' does not exist", $dirname), E_USER_ERROR);
}
// prevent double slash "//" below
if ($canonical === "/") $canonical = null;
// return canonicalized path
return sprintf("%s/%s", $canonical, basename($filename));
}
要求路径中所有目录都存在。路径的
basename
是唯一可以不存在的部分。
如果目录名不存在,将会抛出错误。
我创建了这个:
$path_canonicalize = function($str, $started = false) use(&$path_canonicalize)
{
$str = str_replace('/', DIRECTORY_SEPARATOR, $str).DIRECTORY_SEPARATOR;
if (!$started)
$str = preg_replace("/".preg_quote(DIRECTORY_SEPARATOR, "'".DIRECTORY_SEPARATOR."'")."{2,}/", DIRECTORY_SEPARATOR, $str);
$pos = strpos($str, '..'.DIRECTORY_SEPARATOR);
if ($pos !== false)
{
$part = trim(substr($str, 0, $pos), DIRECTORY_SEPARATOR);
$str = $path_canonicalize(trim(substr($part, 0, strrpos($part, DIRECTORY_SEPARATOR)), DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.trim(substr($str, $pos+3), DIRECTORY_SEPARATOR), true);
}
return rtrim($str, DIRECTORY_SEPARATOR);
};
/*
Try those cases to check the consistency:
$str = __DIR__.'/template//////../header//..';
$str = __DIR__.'/template///..///../header//..';
$str = __DIR__.'/template/../header/..';
$str = __DIR__.'/template/../header/../';
$str = __DIR__.'/template/../header/..//';
$str = __DIR__.'/template/../header/..///';
$str = __DIR__.'/template/../header/..///..';
$str = __DIR__.'/template/../header/..///../';
$str = __DIR__.'/template\\..\\header\\..';
*/
$str = __DIR__.'/template/../header/..///..//';
echo 'original: '.$str.PHP_EOL;
echo 'normalized: '.$path_canonicalize($str).PHP_EOL;
一些担忧:
./
或 /.
function getCanonicalPath($path, $dirSep = "/") {
$isAbsolute = str_starts_with($path, $dirSep);
$pathParts = explode($dirSep, $path);
$resultingParts = [];
for ($i = 0; $i < count($pathParts); $i++) {
$pathPart = $pathParts[$i];
switch(trim($pathPart)){
case "":
case ".":
// do nothing
break;
case "..":
array_pop($resultingParts);
break;
default:
$resultingParts[] = $pathPart;
break;
}
}
$ret = implode($dirSep, $resultingParts) . $dirSep;
if ($isAbsolute) {
$ret = "/" . $ret;
}
return $ret;
}