2010-10-29 2 views
9

По-видимому, realpath очень глючный. В PHP 5.3.1 он вызывает случайные сбои. В 5.3.0 и менее, realpath случайным образом терпит неудачу и возвращает false (для той же строки, конечно), плюс он всегда терпит неудачу на realpath - в той же строке дважды и более (и, конечно же, она работает в первый раз).Замените реальный путь PHP()

Кроме того, он настолько ошибочен в ранних версиях PHP, что он полностью непригоден. Ну ... это уже так, так как это непротиворечиво.

Во всяком случае, какие у меня варианты? Может быть, переписать его сам? Это целесообразно?

+0

пожалуйста перейдите на страницу [bugs.php.net] (http://bugs.php.net «Ошибка Bugtracker») и посмотрите, не были ли уже обнаружены ошибки. Если нет, напишите отчет об ошибке, чтобы они были исправлены. – Gordon

+0

Они задокументированы, однако, даже если они не были патчем, не могут помочь ранее («стабильным») версиям PHP ... Мне нужно работать над чем-то, что действительно работает. – Christian

+3

Уход за сообщениями об ошибках? –

ответ

26

Благодаря коду Sven Arduwie в (pointed out by Pekka) и некоторые модификация, я построил (надеюсь) более эффективной реализации:

/** 
* This function is to replace PHP's extremely buggy realpath(). 
* @param string The original path, can be relative etc. 
* @return string The resolved path, it might not exist. 
*/ 
function truepath($path){ 
    // whether $path is unix or not 
    $unipath=strlen($path)==0 || $path{0}!='/'; 
    // attempts to detect if path is relative in which case, add cwd 
    if(strpos($path,':')===false && $unipath) 
     $path=getcwd().DIRECTORY_SEPARATOR.$path; 
    // resolve path parts (single dot, double dot and double delimiters) 
    $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); 
    $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); 
    $absolutes = array(); 
    foreach ($parts as $part) { 
     if ('.' == $part) continue; 
     if ('..' == $part) { 
      array_pop($absolutes); 
     } else { 
      $absolutes[] = $part; 
     } 
    } 
    $path=implode(DIRECTORY_SEPARATOR, $absolutes); 
    // resolve any symlinks 
    if(file_exists($path) && linkinfo($path)>0)$path=readlink($path); 
    // put initial separator that could have been lost 
    $path=!$unipath ? '/'.$path : $path; 
    return $path; 
} 

NB: в отличие от РНР realpath, эта функция не возвращает ложь об ошибке; он возвращает путь, который, насколько это возможно, разрешает эти причуды.

Примечание 2: По-видимому, некоторые люди не могут читать правильно. Truepath() не работает на сетевых ресурсах, включая UNC и URL. Он работает только для локальной файловой системы.

+0

Выглядит хорошо. Я тестировал его немного в Windows, и он отлично работает, даже по буквам дисков. –

+0

Большое спасибо за тестирование! Знать, что это очень стабильно, имеет огромное значение для моего проекта. – Christian

+0

@Christian Я заметил, что если вы предоставляете протокол, возвращается только одна косая черта, когда ожидается два. ex: 'truepath (" http://www.foobar.com/ ");' будет возвращать 'http:/www.foobar.com' –

1

Я никогда не слышал о таких серьезных проблемах с realpath() (я всегда думал, что он просто взаимодействует с некоторыми базовыми функциональными возможностями ОС - будет интересоваться некоторыми ссылками), но в User Contributed Notes на странице руководства есть ряд альтернативных реализаций. Here - это тот, который выглядит хорошо.

Конечно, это не гарантирует, что эти реализации позаботятся обо всех кросс-платформенных причудах и проблемах, поэтому вам нужно будет провести тщательное тестирование, чтобы узнать, подходит ли оно вашим потребностям.

Насколько я могу видеть, ни один из них не возвращает канонический путь, они только разрешают относительные пути. Если вам нужно, я не уверен, можно ли обойти realpath() (за исключением, возможно выполнение() консольную команду зависимые от системы, которая дает вам полный путь.)

+1

Устранение символических ссылок было одной из главных причин использования realpath, к сожалению ... – Christian

0

В Windows 7 код работает нормально. В Linux существует проблема в том, что созданный путь начинается с (в моем случае) home/xxx, когда он должен начинаться с/home/xxx ... т.е. отсутствует начальная /, указывающая корневую папку. Проблема заключается не столько в этой функции, сколько в том, что getcwd возвращает в Linux.

+2

Ах, я столкнулся и исправил эту проблему, но забыл обновить ответ. Я обновил его сейчас. Благодарю. Кстати, в следующий раз добавьте комментарий к моему ответу, а не создайте новый ответ. :) – Christian

+0

Извините, не можете посмотреть, как прокомментировать ваш ответ. Однако эта пересмотренная версия теперь не работает ни в Windows, ни в Linux. Я нахожу, что $ unipath устанавливается для обеих ОС ... и в случае, если я не понимаю логику выражения '$ unipath = strlen ($ path) == 0 || $ Путь {0} = '/'; (почему любой из них означает использование Unix ???). Также, конечно, в stament $ path =! $ Unipath? '/' .$path: $ path; ! неверно и должно быть удалено ??? –

+0

Первая часть проверяет, пуст ли пуст и начинается с разделителя или нет. Имя неверно, оно должно быть «nonunixpath» или что-то еще. Принимая это во внимание, окончательное утверждение верно; 'path =! nonunixpath? '/'. path: path; '. – Christian

1

Я знаю, что это старая нить, но это действительно полезно.

Я встречаюсь с странным Phar::interceptFileFuncs вопросом, когда я применил относительный путь в phpctags, realpath() действительно действительно глючит внутри phar.

Благодаря этой теме дайте мне немного света, здесь идет моя реализация, основанная на реализации christian из этой темы и этой comments.

Надеюсь, это сработает для вас.

function relativePath($from, $to) 
{ 
    $fromPath = absolutePath($from); 
    $toPath = absolutePath($to); 

    $fromPathParts = explode(DIRECTORY_SEPARATOR, rtrim($fromPath, DIRECTORY_SEPARATOR)); 
    $toPathParts = explode(DIRECTORY_SEPARATOR, rtrim($toPath, DIRECTORY_SEPARATOR)); 
    while(count($fromPathParts) && count($toPathParts) && ($fromPathParts[0] == $toPathParts[0])) 
    { 
     array_shift($fromPathParts); 
     array_shift($toPathParts); 
    } 
    return str_pad("", count($fromPathParts)*3, '..'.DIRECTORY_SEPARATOR).implode(DIRECTORY_SEPARATOR, $toPathParts); 
} 

function absolutePath($path) 
{ 
    $isEmptyPath = (strlen($path) == 0); 
    $isRelativePath = ($path{0} != '/'); 
    $isWindowsPath = !(strpos($path, ':') === false); 

    if (($isEmptyPath || $isRelativePath) && !$isWindowsPath) 
     $path= getcwd().DIRECTORY_SEPARATOR.$path; 

    // resolve path parts (single dot, double dot and double delimiters) 
    $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); 
    $pathParts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); 
    $absolutePathParts = array(); 
    foreach ($pathParts as $part) { 
     if ($part == '.') 
      continue; 

     if ($part == '..') { 
      array_pop($absolutePathParts); 
     } else { 
      $absolutePathParts[] = $part; 
     } 
    } 
    $path = implode(DIRECTORY_SEPARATOR, $absolutePathParts); 

    // resolve any symlinks 
    if (file_exists($path) && linkinfo($path)>0) 
     $path = readlink($path); 

    // put initial separator that could have been lost 
    $path= (!$isWindowsPath ? '/'.$path : $path); 

    return $path; 
} 
2

Для тех пользователей, Zend там, этот ответ может помочь вам, как это было со мной:

$path = APPLICATION_PATH . "/../directory"; 
$realpath = new Zend_Filter_RealPath(new Zend_Config(array('exists' => false))); 
$realpath = $realpath->filter($path); 
4

здесь модифицированный код, который поддерживает пути UNC, а

static public function truepath($path) 
{ 
    // whether $path is unix or not 
    $unipath = strlen($path)==0 || $path{0}!='/'; 
    $unc = substr($path,0,2)=='\\\\'?true:false; 
    // attempts to detect if path is relative in which case, add cwd 
    if(strpos($path,':') === false && $unipath && !$unc){ 
     $path=getcwd().DIRECTORY_SEPARATOR.$path; 
     if($path{0}=='/'){ 
      $unipath = false; 
     } 
    } 

    // resolve path parts (single dot, double dot and double delimiters) 
    $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); 
    $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen'); 
    $absolutes = array(); 
    foreach ($parts as $part) { 
     if ('.' == $part){ 
      continue; 
     } 
     if ('..' == $part) { 
      array_pop($absolutes); 
     } else { 
      $absolutes[] = $part; 
     } 
    } 
    $path = implode(DIRECTORY_SEPARATOR, $absolutes); 
    // resolve any symlinks 
    if(function_exists('readlink') && file_exists($path) && linkinfo($path)>0){ 
     $path = readlink($path); 
    } 
    // put initial separator that could have been lost 
    $path = !$unipath ? '/'.$path : $path; 
    $path = $unc ? '\\\\'.$path : $path; 
    return $path; 
} 
Смежные вопросы