在PHP中从绝对path获取相对path

当我input标题时,我注意到了一些类似的问题,但他们似乎不是在PHP中。 那么PHP函数的解决scheme是什么?

要指定。

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

任何好主意? 谢谢。

试试这个:

 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); } 

这会给

 $a="/home/a.php"; $b="/home/root/b/b.php"; echo getRelativePath($a,$b), PHP_EOL; // ./root/b/b.php 

 $a="/home/apache/a/a.php"; $b="/home/root/b/b.php"; echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php 

 $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 

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

由于我们已经有了几个答案,所以我决定对它们进行testing并对其进行testing。 我用这个path来testing:

$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/"; 注意:如果它是一个文件夹,你必须把一个尾部斜杠function正常工作! 所以, __DIR__将不起作用。 改用__FILE____DIR__ . '/' __DIR__ . '/'

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

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

  • 戈登的function:结果正确 ,时间为100.000次, 1,222
  • Young的function:结果是正确的 ,100.000次执行的时间为1,540
  • 由Ceagle的function:结果错误 (它有一些path,但与其他一些失败,如在testing中使用,并在上面写)
  • Loranger的function:结果错误 (它有一些path,但与其他一些失败,如在testing中使用,并在上面写)

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

Young也是很好的,用简单的目录结构(比如“a / b / c.php”)performance更好,而Gordon的performance更好,结构复杂,有许多子目录(就像这个基准testing中使用的那样)。


注意:我在下面写下$from$to返回的结果作为input,所以您可以validation其中2个是正确的,而另外2个是错误的:

  • Gordon: ../../../../../../aaa/bbb/ccc/ddd aaa/bbb/ccc/ ../../../../../../aaa/bbb/ccc/ddd – > CORRECT
  • Young: ../../../../../../aaa/bbb/ccc/ddd aaa/bbb/ccc/ ../../../../../../aaa/bbb/ccc/ddd – > CORRECT
  • Ceagle: ../../../../../../bbb/ccc/ddd bbb/ccc/ddd->错误
  • Loranger: ../../../../../aaa/bbb/ccc/ddd aaa/bbb/ccc/ ../../../../../aaa/bbb/ccc/ddd – > WRONG

相对path? 这似乎更像是一个旅行路线。 你似乎想知道从Apath到Bpath的path。如果是这种情况,可以在'/'分解$ a和$ b,然后反向循环$ aParts,将它们与$ bParts直到find“公分母”目录为止的相同索引(logging沿途的循环次数)。 然后创build一个空string,并添加'../'到$ numLoops-1,然后添加到$ b减去公分母目录。

基于戈登的function,我的解决scheme如下:

 function getRelativePath($from, $to) { $from = explode('/', $from); $to = explode('/', $to); foreach($from as $depth => $dir) { if(isset($to[$depth])) { if($dir === $to[$depth]) { unset($to[$depth]); unset($from[$depth]); } else { break; } } } //$rawresult = implode('/', $to); for($i=0;$i<count($from)-1;$i++) { array_unshift($to,'..'); } $result = implode('/', $to); return $result; } 
 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); } 

我的尝试是故意更简单,虽然可能没有什么不同的performance。 我将留下标杆作为一个好奇的读者的练习。 但是,这是相当强大的,应该是平台不可知的。

注意使用array_intersect函数的解决scheme,因为如果并行目录具有相同的名称,这些解决scheme将会中断。 例如, getRelativePath('start/A/end/', 'start/B/end/')会返回“ ../end ”,因为../endfind所有相同的名字,在这种情况下只有2。

这段代码取自Symfony URL生成器https://github.com/symfony/Routing/blob/master/Generator/UrlGenerator.php

  /** * Returns the target path as relative reference from the base path. * * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash. * Both paths must be absolute and not contain relative parts. * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. * Furthermore, they can be used to reduce the link size in documents. * * Example target paths, given a base path of "/a/b/c/d": * - "/a/b/c/d" -> "" * - "/a/b/c/" -> "./" * - "/a/b/" -> "../" * - "/a/b/c/other" -> "other" * - "/a/x/y" -> "../../x/y" * * @param string $basePath The base path * @param string $targetPath The target path * * @return string The relative target path */ function getRelativePath($basePath, $targetPath) { if ($basePath === $targetPath) { return ''; } $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath); array_pop($sourceDirs); $targetFile = array_pop($targetDirs); foreach ($sourceDirs as $i => $dir) { if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { unset($sourceDirs[$i], $targetDirs[$i]); } else { break; } } $targetDirs[] = $targetFile; $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs); // A reference to the same base directory or an empty subdirectory must be prefixed with "./". // This also applies to a segment with a colon character (eg, "file:colon") that cannot be used // as the first segment of a relative-path reference, as it would be mistaken for a scheme name // (see http://tools.ietf.org/html/rfc3986#section-4.2). return '' === $path || '/' === $path[0] || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) ? "./$path" : $path; } 

有些理由戈登没有为我工作….这是我的解决scheme

 function getRelativePath($from, $to) { $patha = explode('/', $from); $pathb = explode('/', $to); $start_point = count(array_intersect($patha,$pathb)); while($start_point--) { array_shift($patha); array_shift($pathb); } $output = ""; if(($back_count = count($patha))) { while($back_count--) { $output .= "../"; } } else { $output .= './'; } return $output . implode('/', $pathb); } 

我使用这些数组操作来得到相同的结果:

 function getRelativePath($path, $from = __FILE__ ) { $path = explode(DIRECTORY_SEPARATOR, $path); $from = explode(DIRECTORY_SEPARATOR, dirname($from.'.')); $common = array_intersect_assoc($path, $from); $base = array('.'); if ( $pre_fill = count( array_diff_assoc($from, $common) ) ) { $base = array_fill(0, $pre_fill, '..'); } $path = array_merge( $base, array_diff_assoc($path, $common) ); return implode(DIRECTORY_SEPARATOR, $path); } 

第二个参数是path相对于的文件。 这是可选的,所以你可以得到相对path,而不pipe你当前的网页。 为了和@Young或@Gordon例子一起使用它,因为你想知道从$ a到$ b的相对path,你必须使用

 getRelativePath($b, $a); 

简单的一行代码常见的情况:

 str_replace(getcwd() . DIRECTORY_SEPARATOR, '', $filepath) 

要么:

 substr($filepath, strlen(getcwd())+1) 

要检查path是否绝对,请尝试:

 $filepath[0] == DIRECTORY_SEPARATOR 

这是对我有用的东西。 由于某种未知的原因,对这个问题的最有争议的答案没有按预期那样工作

 public function getRelativePath($absolutePathFrom, $absolutePathDestination) { $absolutePathFrom = is_dir($absolutePathFrom) ? rtrim($absolutePathFrom, "\/")."/" : $absolutePathFrom; $absolutePathDestination = is_dir($absolutePathDestination) ? rtrim($absolutePathDestination, "\/")."/" : $absolutePathDestination; $absolutePathFrom = explode("/", str_replace("\\", "/", $absolutePathFrom)); $absolutePathDestination = explode("/", str_replace("\\", "/", $absolutePathDestination)); $relativePath = ""; $path = array(); $_key = 0; foreach($absolutePathFrom as $key => $value) { if (strtolower($value) != strtolower($absolutePathDestination[$key])) { $_key = $key + 1; for ($i = $key; $i < count($absolutePathDestination); $i++) { $path[] = $absolutePathDestination[$i]; } break; } } for ($i = 0; $i <= (count($absolutePathFrom) - $_key - 1); $i++) { $relativePath .= "../"; } return $relativePath.implode("/", $path); } 

如果$a = "C:\xampp\htdocs\projects\SMS\App\www\App\index.php"
$b = "C:\xampp\htdocs\projects\SMS\App/www/App/bin/bootstrap/css/bootstrap.min.css"

然后$c ,这是$b$b的相对path

$c = getRelativePath($a, $b) = "bin/bootstrap/css/bootstrap.min.css"