【问题标题】:Replace PHP's realpath()替换 PHP 的 realpath()
【发布时间】:2011-05-02 06:44:33
【问题描述】:

显然,realpath 非常有问题。在 PHP 5.3.1 中,它会导致随机崩溃。 在 5.3.0 及更低版本中,realpath 随机失败并返回 false(当然对于相同的字符串),而且它总是在 realpath 上失败 - 两次/多次使用相同的字符串(当然,它第一次工作)。

此外,在早期的 PHP 版本中,它有很多错误,以至于完全无法使用。嗯......它已经是,因为它不一致。

无论如何,我有什么选择?也许自己重写?这是可取的吗?

【问题讨论】:

  • 请转至bugs.php.net,看看您遇到的错误是否已经列出。如果没有,请提交错误报告以修复它们。
  • 它们已被记录在案,但是,即使它们不是补丁也无助于早期(“稳定”)PHP 版本......我需要做一些真正有效的工作。
  • 愿意分享错误报告的链接吗?
  • Google: "site:bugs.php.net realpath" #39367 #14049 看起来很有趣。

标签: php path realpath


【解决方案1】:

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

注意: 与 PHP 的 realpath 不同,此函数在错误时不会返回 false;它返回一条尽可能解决这些怪癖的路径。

注意 2: 显然有些人无法正常阅读。 Truepath() 不适用于包括 UNC 和 URL 在内的网络资源。 它仅适用于本地文件系统。

【讨论】:

  • -1 是的,当然,少一些错误。只看一眼,我就可以看出它对于 UNC 路径是错误的,而且它可能也无法正确处理许多其他边缘情况。
  • @NikiC 该功能正在 5 台不同的服务器上使用,每台 realpath() 都因各种原因而失败。你说的“少车”是什么意思?
  • 危险,请勿使用。谁在乎 UNC 路径?这个函数有严重的问题,我不是在谈论可怕的代码格式。它是doesn't correctly resolve symbolic links,这是一个major security problem!返回部分也可能缺少起始斜线,这是另一个主要的安全问题。它在许多其他方面也不符合 POSIX。 PHP 的 realpath() 不是很好,但这要差很多
  • @Carpetsmoker - 嗯?你是什​​么意思它不解析符号链接? readlink() 在你看来是做什么的?此外,我从未说过也不希望它解决 UNC 路径。至于 POSIX……嗯?
  • 该函数有一个错误:如果路径是相对的并且正在添加 cwd,则 unipath 需要再次变为 false - 否则结果中将缺少前导 /: // 尝试检测路径是否为相对在这种情况下,添加 cwd if(strpos($path,':')===false && $unipath) { $path=getcwd().DIRECTORY_SEPARATOR.$path; $单路径=假; }
【解决方案2】:

这是修改后的代码,也支持 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;
}

【讨论】:

    【解决方案3】:

    我知道这是一个旧线程,但它确实很有帮助。

    当我在phpctags 中实现相对路径时,我遇到了一个奇怪的Phar::interceptFileFuncs 问题,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;
    }
    

    【讨论】:

      【解决方案4】:

      对于那些 Zend 用户,这个答案可能会帮助你,就像它对我一样:

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

      【讨论】:

        【解决方案5】:

        我从来没有听说过realpath() 有这么大的问题(我一直认为它只是接口一些底层操作系统功能 - 会对一些链接感兴趣),但是手册页的User Contributed Notes 有许多替代方案实施。 Here 看起来不错。

        当然,不能保证这些实现会处理所有跨平台的怪癖和问题,因此您必须进行彻底的测试以查看它是否适合您的需求。

        据我所知,它们都没有返回规范化路径,它们只解析相对路径。如果你需要,我不确定你是否可以绕过realpath()(除了可能执行一个(系统相关的)控制台命令,它可以为你提供完整的路径。)

        【讨论】:

        • 解析符号链接是使用 realpath 的主要原因之一,可悲的是......
        【解决方案6】:

        在 Windows 7 上,代码运行良好。在 Linux 上,存在一个问题,即生成的路径以(在我的情况下)home/xxx 开头,而它应该以 /home/xxx 开头......即缺少初始 /,表示根文件夹。 问题不在于这个函数,而在于 Linux 中 getcwd 返回的内容。

        【讨论】:

        • 啊,我确实遇到并解决了这个问题,但忘了更新答案。我现在已经更新了。谢谢。顺便说一句,下次请在我的答案中添加评论,而不是完全创建新答案。 :)
        • 对不起,看不到如何评论您的答案。但是,这个修订版现在不能在 Windows 或 Linux 上运行。我发现 $unipath 正在为两个操作系统设置......以防万一,我不理解语句 '$unipath=strlen($path)==0 || 的逻辑$path{0}!='/'; (为什么其中任何一个都意味着使用 Unix ???)。另外,肯定在 $path=!$unipath 中? '/'.$path : $path;这 !不正确,应该删除???
        • 第一部分检查该路径是否为空并以分隔符开头。名字不对,应该是“nonunixpath”什么的。考虑到这一点,最后的说法是正确的; path = !nonunixpath ? '/'.path : path;.
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多