본문 바로가기
프로그래밍/PHP

[PHP] 폴더 경로 변경 함수

by 베리베리 2008. 8. 19.
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

댓글