2010-10-29 6 views
9

Rõ ràng, realpath rất bị lỗi. Trong PHP 5.3.1, nó gây ra các sự cố ngẫu nhiên. Trong 5.3.0 và ít hơn, realpath ngẫu nhiên thất bại và trả về false (cho cùng một chuỗi khóa học), cộng với nó luôn thất bại trên realpath -ing cùng một chuỗi hai lần/nhiều hơn (và tất nhiên, nó hoạt động lần đầu tiên).Thay thế đường dẫn thực của PHP()

Ngoài ra, nó rất lỗi trong các phiên bản PHP trước đó, rằng nó hoàn toàn không sử dụng được. Vâng ... nó đã được, vì nó không nhất quán.

Nhưng dù sao, tôi có những tùy chọn nào? Có thể tự viết lại nó? Điều này có được khuyến khích không?

+0

xin vui lòng truy cập [bugs.php.net] (http://bugs.php.net "Trình gỡ lỗi của PHP") và xem các lỗi bạn gặp phải đã được liệt kê chưa. Nếu không, vui lòng gửi báo cáo lỗi để chúng được khắc phục. – Gordon

+0

Chúng được ghi lại, tuy nhiên, ngay cả khi chúng không phải là bản vá không thể giúp các phiên bản PHP ("ổn định") sớm hơn ... tôi cần phải làm việc trên một cái gì đó thực sự hoạt động. – Christian

+3

chăm sóc để chia sẻ liên kết đến báo cáo lỗi? –

Trả lời

26

Nhờ mã Sven Arduwie (pointed out by Pekka) và một số sửa đổi, tôi đã xây dựng một (hy vọng) thực hiện tốt hơn:

/** 
* 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: Khác với PHP realpath, chức năng này không trả lại sai về lỗi; nó trả về một con đường mà có thể giải quyết được những điều kỳ quặc này.

Lưu ý 2: Dường như một số người không thể đọc chính xác. Truepath() không hoạt động trên tài nguyên mạng bao gồm UNC và URL. Chỉ hoạt động với hệ thống tệp cục bộ.

+0

Có vẻ tốt. Tôi đã thử nghiệm nó một chút trên Windows và nó hoạt động tốt, ngay cả trên các ký tự ổ đĩa. –

+0

Cảm ơn bạn rất nhiều vì đã thử nghiệm! Biết nó là rất ổn định là rất quan trọng cho dự án của tôi. – Christian

+0

@Christian Tôi nhận thấy rằng nếu bạn cung cấp một giao thức, chỉ có một dấu gạch chéo được trả về khi hai được mong đợi. ví dụ: 'truepath (" http://www.foobar.com/ ");' sẽ trả về 'http:/www.foobar.com' –

1

Tôi chưa bao giờ nghe nói về những vấn đề lớn như vậy với realpath() (Tôi luôn nghĩ rằng nó chỉ giao diện một số chức năng hệ điều hành cơ bản - sẽ quan tâm đến một số liên kết), nhưng User Contributed Notes đến trang hướng dẫn sử dụng có một số triển khai thay thế. Here là một giao diện có vẻ ổn.

Tất nhiên, nó không được đảm bảo các triển khai này sẽ giải quyết mọi vấn đề và vấn đề đa nền tảng, vì vậy bạn phải kiểm tra kỹ lưỡng để xem nó có phù hợp với nhu cầu của bạn hay không.

Theo như tôi có thể thấy, mặc dù không ai trong số họ trả về một đường dẫn chuẩn, chúng chỉ giải quyết các đường dẫn tương đối. Nếu bạn cần điều đó, tôi không chắc liệu bạn có thể nhận được khoảng realpath() (ngoại trừ có thể thực hiện lệnh điều khiển (phụ thuộc vào hệ thống) cung cấp cho bạn đường dẫn đầy đủ.)

+1

Giải quyết các liên kết tượng trưng là một trong những lý do chính để sử dụng realpath, thật đáng buồn ... – Christian

0

Trên Windows 7, mã hoạt động tốt. Trên Linux, có một vấn đề trong đó đường dẫn tạo ra bắt đầu với (trong trường hợp của tôi) home/xxx khi nó nên bắt đầu bằng/home/xxx ... tức là ban đầu /, chỉ ra thư mục gốc, bị thiếu. Vấn đề không phải là quá nhiều với chức năng này, nhưng với những gì getcwd trả về trong Linux.

+2

Ah, tôi đã gặp phải và khắc phục sự cố đó nhưng quên cập nhật câu trả lời. Tôi đã cập nhật nó ngay bây giờ. Cảm ơn. Nhân tiện, lần sau, hãy thêm nhận xét vào câu trả lời của tôi thay vì tạo một câu trả lời mới hoàn toàn. :) – Christian

+0

Xin lỗi, không thể xem cách nhận xét về câu trả lời của bạn. Tuy nhiên, phiên bản sửa đổi này hiện không hoạt động trên Windows hoặc Linux. Tôi thấy rằng $ unipath đang được thiết lập cho cả hai hệ điều hành ... và trong trường hợp, tôi không hiểu được logic của câu lệnh '$ unipath = strlen ($ path) == 0 || $ path {0}! = '/'; (tại sao một trong hai điều này biểu thị việc sử dụng Unix ???). Ngoài ra, chắc chắn trong stament $ path =! $ Unipath? '/'.$path: $ path; các ! không chính xác và phải được xóa ??? –

+0

Phần đầu tiên kiểm tra xem đường dẫn đó có trống không và bắt đầu bằng dấu tách hay không. Tên là sai, nó phải là "nonunixpath" hoặc một cái gì đó. Cân nhắc điều này, tuyên bố cuối cùng là chính xác; 'path =! nonunixpath? '/'.path: đường dẫn; '. – Christian

1

Tôi biết đây là một chủ đề cũ, nhưng nó thực sự hữu ích.

Tôi gặp sự cố Phar::interceptFileFuncs kỳ lạ khi tôi triển khai đường dẫn tương đối trong phpctags, realpath() thực sự thực sự lỗi trong phar.

Cảm ơn chủ đề này cung cấp cho tôi một số đèn, ở đây đi kèm với việc triển khai của tôi dựa trên việc thực hiện christian từ chủ đề này và comments này.

Hy vọng nó phù hợp với bạn.

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

Đối với những người sử dụng Zend ra khỏi đó, câu trả lời này có thể giúp bạn, như nó đã làm tôi:

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

đây là mã sửa đổi để hỗ trợ các đường dẫn UNC cũng

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