替换 PHP 的 realpath()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4049856/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 11:49:12  来源:igfitidea点击:

Replace PHP's realpath()

phppathrealpath

提问by Christian

Apparently, realpathis very buggy. In PHP 5.3.1, it causes random crashes. In 5.3.0 and less, realpathrandomly fails and returns false (for the same string of course), plus it always fails on realpath-ing the same string twice/more (and of course, it works the first time).

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

Also, it is so buggy in earlier PHP versions, that it is completely unusable. Well...it already is, since it's not consistent.

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

Anyhow, what options do I have? Maybe rewrite it by myself? Is this advisable?

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

回答by Christian

Thanks to Sven Arduwie's code (pointed out by Pekka) and some modification, I've built a (hopefully) better implementation:

感谢 Sven Arduwie 的代码(由 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:Unlike PHP's realpath, this function does not return false on error; it returns a path which is as far as it could to resolving these quirks.

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

Note 2:Apparently some people can't read properly. Truepath() does not work on network resources including UNC and URLs. It works for the local file system only.

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

回答by Pavel Perna

here is the modified code that supports UNC paths as well

这是支持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;
}

回答by axiom82

For those Zend users out there, THIS answer may help you, as it did me:

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

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

回答by Mark Wu

I know this is an old thread, but it is really helpful.

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

I meet a weird Phar::interceptFileFuncsissue when I implemented relative path in phpctags, the realpath()is really really buggy inside phar.

我遇到一个奇怪的Phar :: interceptFileFuncs问题,当我在执行相对路径phpctags时,realpath()是真的马车里面药业。

Thanks this thread give me some lights, here comes with my implementation based on christian's implemenation from this thread and this comments.

感谢这个线程给我一些启发,这里是我基于christian's implementation from this thread 和 this comments 的实现

Hope it works for you.

希望对你有效。

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

回答by Pekka

I have never heard of such massive problems with realpath()(I always thought that it just interfaces some underlying OS functionality - would be interested in some links), but the User Contributed Notesto the manual page have a number of alternative implementations. Hereis one that looks okay.

我从来没有听说过这么大的问题realpath()(我一直认为它只是接口一些底层的操作系统功能 - 会对某些链接感兴趣),但是手册页的用户贡献注释有许多替代实现。是一个看起来不错的。

Of course, it's not guaranteed these implementations take care of all cross-platform quirks and issues, so you'd have to do thorough testing to see whether it suits your needs.

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

As far as I can see though, none of them returns a canonicalized path, they only resolve relative paths. If you need that, I'm not sure whether you can get around realpath()(except perhaps executing a (system-dependent) console command that gives you the full path.)

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

回答by Andrew Fry

On Windows 7, the code works fine. On Linux, there is a problem in that the path generated starts with (in my case) home/xxx when it should start with /home/xxx ... ie the initial /, indicating the root folder, is missing. The problem is not so much with this function, but with what getcwd returns in Linux.

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