从PHP中的绝对路径获取相对路径

You*_*ung 40 php relative-path

当我输入标题时,我注意到有关此问题的一些类似问题,但它们似乎不是在PHP中.那么PHP功能的解决方案是什么?

待指定.

$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
$relpath = getRelativePath($a,$b); //needed function,should return '../../root/b/b.php'
Run Code Online (Sandbox Code Playgroud)

有什么好主意吗?谢谢.

Gor*_*don 64

试试这个:

function getRelativePath($from, $to)
{
    // some compatibility fixes for Windows paths
    $from = is_dir($from) ? rtrim($from, '\/') . '/' : $from;
    $to   = is_dir($to)   ? rtrim($to, '\/') . '/'   : $to;
    $from = str_replace('\\', '/', $from);
    $to   = str_replace('\\', '/', $to);

    $from     = explode('/', $from);
    $to       = explode('/', $to);
    $relPath  = $to;

    foreach($from as $depth => $dir) {
        // find first non-matching dir
        if($dir === $to[$depth]) {
            // ignore this directory
            array_shift($relPath);
        } else {
            // get number of remaining dirs to $from
            $remaining = count($from) - $depth;
            if($remaining > 1) {
                // add traversals up to first matching dir
                $padLength = (count($relPath) + $remaining - 1) * -1;
                $relPath = array_pad($relPath, $padLength, '..');
                break;
            } else {
                $relPath[0] = './' . $relPath[0];
            }
        }
    }
    return implode('/', $relPath);
}
Run Code Online (Sandbox Code Playgroud)

这会给

$a="/home/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL;  // ./root/b/b.php
Run Code Online (Sandbox Code Playgroud)

$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php
Run Code Online (Sandbox Code Playgroud)

$a="/home/root/a/a.php";
$b="/home/apache/htdocs/b/en/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../apache/htdocs/b/en/b.php
Run Code Online (Sandbox Code Playgroud)

$a="/home/apache/htdocs/b/en/b.php";
$b="/home/root/a/a.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../../../root/a/a.php
Run Code Online (Sandbox Code Playgroud)


luc*_*rio 15

由于我们有几个答案,我决定测试它们并对它们进行基准测试.我用这条路来测试:

$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/";注意:如果它是一个文件夹,您必须为函数设置一个尾部斜杠才能正常工作!所以,__DIR__不行.__FILE__改为使用或__DIR__ . '/'

$to = "/var/www/sites/web/mainroot/webapp/folder/aaa/bbb/ccc/ddd";

结果:(小数点分隔符为逗号,千位分隔符为点)

  • 戈登的功能:结果正确,时间为100.000高手1,222
  • 杨的功能:结果正确,时间为100.000高手1,540
  • Ceagle的功能:结果错误(它适用于某些路径,但与其他路径一起失败,如测试中使用的和上面写的)
  • Loranger的功能:结果错误(它适用于某些路径,但与其他路径一起失败,如测试中使用的和上面写的)

所以,我建议你使用Gordon的实现!(标记为答案的那个)

Young的一个也很好用简单的目录结构(比如"a/b/c.php")表现得更好,而Gordon的表现更好,复杂的结构,有很多子目录(比如这个基准测试中使用的子目录).


注意:我在下面写下返回的结果$from$to作为输入,因此您可以验证其中2个是正常的,而其他2个是错误的:

  • 戈登:../../../../../../aaa/bbb/ccc/ddd- >正确
  • 年轻:../../../../../../aaa/bbb/ccc/ddd- >正确
  • Ceagle:../../../../../../bbb/ccc/ddd- >错了
  • Loranger:../../../../../aaa/bbb/ccc/ddd- >错了


web*_*ave 8

相对路径?这似乎更像是一条旅行路径.你似乎想知道从路径A到路径B的路径.如果是这种情况,你可以爆炸 $ a和$ b '/'然后反向循环$ aParts,将它们与同一索引的$ bPart进行比较直到找到"common denominator"目录(记录沿途的循环次数).然后创建一个空字符串并添加'../'$ numLoops-1次然后添加到$ b减去公分母目录.


clo*_*eek 5

const DS = DIRECTORY_SEPARATOR; // for convenience

function getRelativePath($from, $to) {
    $dir = explode(DS, is_file($from) ? dirname($from) : rtrim($from, DS));
    $file = explode(DS, $to);

    while ($dir && $file && ($dir[0] == $file[0])) {
        array_shift($dir);
        array_shift($file);
    }
    return str_repeat('..'.DS, count($dir)) . implode(DS, $file);
}
Run Code Online (Sandbox Code Playgroud)

我的尝试故意简化了,尽管性能可能没有什么不同。我会将基准测试留给好奇的读者练习。但是,这是相当可靠的,并且应该与平台无关。

当心使用array_intersect函数的解决方案,因为如果并行目录具有相同的名称,这些函数将被破坏。例如,getRelativePath('start/A/end/', 'start/B/end/')将返回“ ../end”,因为会array_intersect找到所有相同的名称,在本例中为2时,应该只有1个。