Getting relative path from absolute path in PHP
Solution 1:
Try this one:
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);
}
This will give
$a="/home/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ./root/b/b.php
and
$a="/home/apache/a/a.php";
$b="/home/root/b/b.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../root/b/b.php
and
$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
and
$a="/home/apache/htdocs/b/en/b.php";
$b="/home/root/a/a.php";
echo getRelativePath($a,$b), PHP_EOL; // ../../../../root/a/a.php
Solution 2:
Since we've had several answers, I decided to test them all and benchmark them. I used this paths to test:
$from = "/var/www/sites/web/mainroot/webapp/folder/sub/subf/subfo/subfol/subfold/lastfolder/";
NOTE: if it is a folder, you have to put a trailing slash for functions to work correctly! So, __DIR__
will not work. Use __FILE__
instead or __DIR__ . '/'
$to = "/var/www/sites/web/mainroot/webapp/folder/aaa/bbb/ccc/ddd";
RESULTS: (decimal separator is comma, thousand separator is dot)
- Function by Gordon: result CORRECT, time for 100.000 execs 1,222 seconds
- Function by Young: result CORRECT, time for 100.000 execs 1,540 seconds
- Function by Ceagle: result WRONG (it works with some paths but fails with some others, like the ones used in the tests and written above)
- Function by Loranger: result WRONG (it works with some paths but fails with some others, like the ones used in the tests and written above)
So, I suggest that you use Gordon's implementation! (the one marked as answer)
Young's one is good too and performs better with simple directory structures (like "a/b/c.php"), while Gordon's one performs better with complex structures, with lots of subdirectories (like the ones used in this benchmark).
NOTE: I write here below the results returned with $from
and $to
as inputs, so you can verify that 2 of them are OK, while other 2 are wrong:
- Gordon:
../../../../../../aaa/bbb/ccc/ddd
--> CORRECT - Young:
../../../../../../aaa/bbb/ccc/ddd
--> CORRECT - Ceagle:
../../../../../../bbb/ccc/ddd
--> WRONG - Loranger:
../../../../../aaa/bbb/ccc/ddd
--> WRONG