Looks like a lot of people are looking for an 'unrealpath' or 'relative-path' function, returning the relative path from a directory to another one (in most cases ./). Unfortunately it's kinda difficult to program such a function which really works correctly, with any type of path.
Most of the posted functions have some little bugs. I compared the following functions:
- unrealpath() by Zach Bernal zbernal at ucsc dot edu (http://ch2.php.net/manual/en/function.realpath.php#78567)
- UnRealPath() by alban dot lopez+php dot net at gmail dot com (http://ch2.php.net/manual/en/function.realpath.php#77968)
- rel_path() by Santosh Patnaik (http://ch2.php.net/manual/en/function.realpath.php#77203)
The file I used for testing is located at /var/www/test/test.php.
unrealpath(../test/test.php) -> ../test/test.php
UnRealPath(../test/test.php) -> ./var/www/test/test.php
rel_path(../test/test.php) -> ./../../../../../test/test.php
should actually return: ./test.php
UnRealPath(./) -> ./var/www/test/
rel_path(./): -> ./../../../../.
should actually return: ./
Exapmple on windows: the file I used for testing is located at C:/www/test/test.php.
unrealpath(C:/www/test/../) -> C:/www/test/../
UnRealPath(C:/www/test/../) -> .
rel_path(C:/www/test/../) -> ./../../../../C:/www/test/../
should actually return: ../
So here's my try, I use 3 functions to get the relative path between two directories:
- clean_path() -> replace / or \ with the correct directory separator etc.
- absolute_path() -> get absolute path of the given path (actually the same as realpath(), but using clean_path() to f.e. add a directory separator at the end if the path isn't a file)
- relative_path() -> get relative path from one to another path
<?php
define('DIR_DELIM',DIRECTORY_SEPARATOR); // in case you'd like to change it. Easier to define a constant instead of including a parameter in each function for the directory separator
function clean_path($path) {
$path = str_replace(array('/','\\'),DIR_DELIM,$path);
$path = preg_replace('/[' . preg_quote(DIR_DELIM,DIR_DELIM) . ']{2,}/',DIR_DELIM,$path);
return substr($path,-1) != DIR_DELIM ? $path . DIR_DELIM : $path;
}
function absolute_path($path) {
$filename = '';
if (is_file($path)) {
$filename = basename($path);
$path = dirname($path);
}
return clean_path(realpath($path)) . $filename;
}
function relative_path($path,$source = null) {
$filename = '';
if (is_file($path)) {
$filename = basename($path);
$path = dirname($path);
}
$path = absolute_path($path);
$source = absolute_path(is_null($source) ? '.' : $source);
if ($path == $source)
return '.' . DIR_DELIM . $filename;
$path_dirs = explode(DIR_DELIM,substr($path,0,-1));
$source_dirs = explode(DIR_DELIM,substr($source,0,-1));
for ($i = 0; $i < min(count($path_dirs),count($source_dirs)); $i++)
if ($path_dirs[$i] != $source_dirs[$i])
break;
if ($i == 0)
return false;
$relative_path = str_repeat('..' . DIR_DELIM,count($source_dirs) - $i);
$same_path = implode(DIR_DELIM,array_splice($path_dirs,0,$i));
if ($same_path != '' && strpos($path,$same_path) === 0)
$path = substr($path,strlen($same_path) + 1);
return $relative_path . ($path == DIR_DELIM ? '' : $path) . $filename;
}
?>
Of course my functions might have some bugs too, but I tested it with a lot of different paths and it seems to work with all of them, I think ;). I tried my best.
Please note that both directories ($path and $source) need to exist, otherwise the function won't return a correct path. If you ignore $source (in most cases I suppose) then the actual directory ./ is taken as $source.
Testing:
relative_path(/var/www/) -> ../
relative_path(/var/www/../test/../../) -> ../../../
relative_path(../) -> ../
relative_path(/) -> ../../../
relative_path(../test/test.php) -> ./test.php
I did a lot of testings on windows too (path like C:/www/ etc.) and it worked with all the paths I used.
I know my english isn't too good, but I hope you understood what I wanted to say ...
Have fun
프로그래밍/PHP
댓글