What is the best way to resolve a relative path (like realpath) for non-existing files?

后端 未结 4 1983
Happy的楠姐
Happy的楠姐 2020-12-30 01:43

I\'m trying to enforce a root directory in a filesystem abstraction. The problem I\'m encountering is the following:

The API lets you read and write files, not only

相关标签:
4条回答
  • 2020-12-30 02:10
    /**
     * Remove '.' and '..' path parts and make path absolute without
     * resolving symlinks.
     *
     * Examples:
     *
     *   resolvePath("test/./me/../now/", false);
     *   => test/now
     *   
     *   resolvePath("test///.///me///../now/", true);
     *   => /home/example/test/now
     *   
     *   resolvePath("test/./me/../now/", "/www/example.com");
     *   => /www/example.com/test/now
     *   
     *   resolvePath("/test/./me/../now/", "/www/example.com");
     *   => /test/now
     *
     * @access public
     * @param string $path
     * @param mixed $basePath resolve paths realtively to this path. Params:
     *                        STRING: prefix with this path;
     *                        TRUE: use current dir;
     *                        FALSE: keep relative (default)
     * @return string resolved path
     */
    function resolvePath($path, $basePath=false) {
        // Make absolute path
        if (substr($path, 0, 1) !== DIRECTORY_SEPARATOR) {
            if ($basePath === true) {
                // Get PWD first to avoid getcwd() resolving symlinks if in symlinked folder
                $path=(getenv('PWD') ?: getcwd()).DIRECTORY_SEPARATOR.$path;
            } elseif (strlen($basePath)) {
                $path=$basePath.DIRECTORY_SEPARATOR.$path;
            }
        }
    
        // Resolve '.' and '..'
        $components=array();
        foreach(explode(DIRECTORY_SEPARATOR, rtrim($path, DIRECTORY_SEPARATOR)) as $name) {
            if ($name === '..') {
                array_pop($components);
            } elseif ($name !== '.' && !(count($components) && $name === '')) {
                // … && !(count($components) && $name === '') - we want to keep initial '/' for abs paths
                $components[]=$name;
            }
        }
    
        return implode(DIRECTORY_SEPARATOR, $components);
    }
    
    0 讨论(0)
  • 2020-12-30 02:20

    ./ current location

    ../ one level up

    function normalize_path($str){
        $N = 0;
        $A =explode("/",preg_replace("/\/\.\//",'/',$str));  // remove current_location
        $B=[];
        for($i = sizeof($A)-1;$i>=0;--$i){
            if(trim($A[$i]) ===".."){
                $N++;
            }else{
                if($N>0){
                    $N--;
                }
                else{
                    $B[] = $A[$i];
                }
            }
        }
        return implode("/",array_reverse($B));
    }
    

    so:

    "a/b/c/../../d" -> "a/d"
     "a/./b" -> "a/b"
    
    0 讨论(0)
  • 2020-12-30 02:24

    To quote Jame Zawinski:

    Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

    protected function getAbsoluteFilename($filename) {
      $path = [];
      foreach(explode('/', $filename) as $part) {
        // ignore parts that have no value
        if (empty($part) || $part === '.') continue;
    
        if ($part !== '..') {
          // cool, we found a new part
          array_push($path, $part);
        }
        else if (count($path) > 0) {
          // going back up? sure
          array_pop($path);
        } else {
          // now, here we don't like
          throw new \Exception('Climbing above the root is not permitted.');
        }
      }
    
      // prepend my root directory
      array_unshift($path, $this->getPath());
    
      return join('/', $path);
    }
    
    0 讨论(0)
  • 2020-12-30 02:28

    I've resolved how to do this, this is my solution:

    /**
     * Normalize path
     *
     * @param   string  $path
     * @param   string  $separator
     * @return  string  normalized path
     */
    public function normalizePath($path, $separator = '\\/')
    {
        // Remove any kind of funky unicode whitespace
        $normalized = preg_replace('#\p{C}+|^\./#u', '', $path);
    
        // Path remove self referring paths ("/./").
        $normalized = preg_replace('#/\.(?=/)|^\./|\./$#', '', $normalized);
    
        // Regex for resolving relative paths
        $regex = '#\/*[^/\.]+/\.\.#Uu';
    
        while (preg_match($regex, $normalized)) {
            $normalized = preg_replace($regex, '', $normalized);
        }
    
        if (preg_match('#/\.{2}|\.{2}/#', $normalized)) {
            throw new LogicException('Path is outside of the defined root, path: [' . $path . '], resolved: [' . $normalized . ']');
        }
    
        return trim($normalized, $separator);
    }
    
    0 讨论(0)
提交回复
热议问题