2013-12-11 9 views
12

Ich versuche, ein Stammverzeichnis in einer Dateisystemabstraktion zu erzwingen. Das Problem, auf das ich stoße, ist folgendes:Was ist der beste Weg, um einen relativen Pfad (wie Realpath) für nicht existierende Dateien aufzulösen?

Die API ermöglicht das Lesen und Schreiben von Dateien, nicht nur für lokale, sondern auch für entfernte Speicher. Es gibt also alle Arten von Normalisierung unter der Haube. Im Moment nicht unterstützt relative Pfade, so etwas wie dies nicht möglich ist:

$filesystem->write('path/to/some/../relative/file.txt', 'file contents'); 

ich sicher in der Lage sein wollen, den Weg zu lösen, so dass die Ausgabe wäre ist: path/to/relative/file.txt. Wie in einem GitHub-Problem, das für diesen Bug/Enhancement erstellt wurde (https://github.com/FrenkyNet/Flysystem/issues/36#issuecomment-30319406), muss es mehr tun, als nur Segmente aufzuteilen und sie entsprechend zu entfernen.

Auch, da das Paket Remote-Dateisysteme und nicht vorhandene Dateien behandelt, ist realpath nicht in Frage.

Wie sollte man also mit diesen Pfaden umgehen?

+0

Wie wäre es 'realpath (dirname ($ path))'? –

+2

realpath benötigt einen Pfad, um auf dem lokalen Dateisystem zu existieren, was für Schreibvorgänge nicht der Fall ist und auf entfernten Dateisystemen überhaupt nicht verwendbar ist. –

+2

Ich sehe nicht, wie Sie den absoluten Pfad eines nicht existierenden relativen Pfades bestimmen können. Du brauchst mindestens den Unterpfad, der die Punkte enthält –

Antwort

6

ich beschlossen habe, wie dies zu tun, das ist meine Lösung:

/** 
* 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

"Pfad entfernen sich selbst verweisende Pfade (" /./ ")" funktioniert nicht ordnungsgemäß mit Pfaden, die mit ".." z. 'a/b/..'. Außerdem funktioniert die "Regex zum Auflösen relativer Pfade" nicht richtig mit Verzeichnissen mit einem Punktpräfix, z. 'a/.b /../ c'. – bernie

+1

Ich habe diese erste Implementation verbessert, nachdem ich dies gepostet habe, was auch mit diesen Situationen zu tun hat. Der Code kann hier gefunden werden: https://github.com/thephleague/flysystem/blob/master/src/Util.php#L80 –

+0

Wenn Sie die Antwort bearbeiten, kann ich meine -1 entfernen. Übrigens, gibt es einen Grund, warum Sie Regexes verwenden, anstatt auf Dir-Separatoren zu splitten und die Pfad-Parts zu loopen, einen Stapel von Path-Parts zu behalten, den letzten Parts-Part zu puffern, wenn Sie '..' treffen? – bernie

7

zu zitieren Jame Zawinski:

Einige Leute, wenn sie mit einem Problem konfrontiert, denken: „Ich weiß, ich verwende reguläre Ausdrücke. " Jetzt haben sie zwei Probleme.

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); 
} 
-1
/** 
* 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); 
} 
+1

Einige Erklärungen neben Ihrem Code wären sehr hilfreich. – Graham

+0

Was für eine Erklärung meinst du? Beispiele? Oder betone einfach die erste Kommentarzeile "Entfernen". und '..' Pfad Teile und machen Pfad absolut, ohne Symlinks zu lösen. "? –

+0

Schauen Sie sich SO an und Sie werden viele Beispiele dafür sehen. Dies ist nicht nur eine Code-Fabrik, sondern ein Ort, an dem Menschen lernen. Es gibt einen Grund, warum das System Ihre Antwort für längerfristige Benutzer markiert hat und darum gebeten hat, dass die Community kommt und Ihnen dabei hilft, wie die Dinge hier funktionieren. – Graham

Verwandte Themen