Kaynağa Gözat

Fixed behaviour of Filesystem::findShortestPath[Code] for paths with up-level references

Martin Hasoň 12 yıl önce
ebeveyn
işleme
0851ef1afb

+ 33 - 4
src/Composer/Util/Filesystem.php

@@ -202,8 +202,8 @@ class Filesystem
             throw new \InvalidArgumentException(sprintf('$from (%s) and $to (%s) must be absolute paths.', $from, $to));
         }
 
-        $from = lcfirst(rtrim(strtr($from, '\\', '/'), '/'));
-        $to = lcfirst(rtrim(strtr($to, '\\', '/'), '/'));
+        $from = lcfirst($this->normalizePath($from));
+        $to = lcfirst($this->normalizePath($to));
 
         if ($directories) {
             $from .= '/dummy_file';
@@ -243,8 +243,8 @@ class Filesystem
             throw new \InvalidArgumentException(sprintf('$from (%s) and $to (%s) must be absolute paths.', $from, $to));
         }
 
-        $from = lcfirst(strtr($from, '\\', '/'));
-        $to = lcfirst(strtr($to, '\\', '/'));
+        $from = lcfirst($this->normalizePath($from));
+        $to = lcfirst($this->normalizePath($to));
 
         if ($from === $to) {
             return $directories ? '__DIR__' : '__FILE__';
@@ -300,6 +300,35 @@ class Filesystem
         return filesize($path);
     }
 
+    /**
+     * Normalize a path. This replaces backslashes with slashes, removes ending
+     * slash and collapses redundant separators and up-level references.
+     *
+     * @param string $path Path to the file or directory
+     * @return string
+     */
+    public function normalizePath($path)
+    {
+        $parts = array();
+        $path = strtr($path, '\\', '/');
+        $prefix = '';
+
+        if (preg_match('|^(([a-z]:)?/)|i', $path, $match)) {
+            $prefix = $match[1];
+            $path = substr($path, strlen($prefix));
+        }
+
+        foreach (explode('/', $path) as $chunk) {
+            if ('..' === $chunk) {
+                array_pop($parts);
+            } elseif ('.' !== $chunk && '' !== $chunk) {
+                $parts[] = $chunk;
+            }
+        }
+
+        return $prefix.implode('/', $parts);
+    }
+
     protected function directorySize($directory)
     {
         $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);

+ 12 - 0
tests/Composer/Test/Util/FilesystemTest.php

@@ -38,6 +38,7 @@ class FilesystemTest extends TestCase
             array('c:/bin/run', 'd:/vendor/acme/bin/run', false, "'d:/vendor/acme/bin/run'"),
             array('c:\\bin\\run', 'd:/vendor/acme/bin/run', false, "'d:/vendor/acme/bin/run'"),
             array('/foo/bar', '/foo/bar', true, "__DIR__"),
+            array('/foo/bar/', '/foo/bar', true, "__DIR__"),
             array('/foo/bar', '/foo/baz', true, "dirname(__DIR__).'/baz'"),
             array('/foo/bin/run', '/foo/vendor/acme/bin/run', true, "dirname(dirname(__DIR__)).'/vendor/acme/bin/run'"),
             array('/foo/bin/run', '/bar/bin/run', true, "'/bar/bin/run'"),
@@ -52,6 +53,11 @@ class FilesystemTest extends TestCase
             array('/tmp/test', '/tmp', true, "dirname(__DIR__)"),
             array('/tmp', '/tmp/test', true, "__DIR__ . '/test'"),
             array('C:/Temp', 'c:\Temp\test', true, "__DIR__ . '/test'"),
+            array('/tmp/test/./', '/tmp/test/', true, '__DIR__'),
+            array('/tmp/test/../vendor', '/tmp/test', true, "dirname(__DIR__).'/test'"),
+            array('/tmp/test/.././vendor', '/tmp/test', true, "dirname(__DIR__).'/test'"),
+            array('C:/Temp', 'c:\Temp\..\..\test', true, "dirname(__DIR__).'/test'"),
+            array('C:/Temp/../..', 'd:\Temp\..\..\test', true, "'d:/test'"),
         );
     }
 
@@ -91,6 +97,12 @@ class FilesystemTest extends TestCase
             array('/tmp', '/tmp/test', "test"),
             array('C:/Temp', 'C:\Temp\test', "test"),
             array('C:/Temp', 'c:\Temp\test', "test"),
+            array('/tmp/test/./', '/tmp/test', './', true),
+            array('/tmp/test/../vendor', '/tmp/test', '../test', true),
+            array('/tmp/test/.././vendor', '/tmp/test', '../test', true),
+            array('C:/Temp', 'c:\Temp\..\..\test', "../test", true),
+            array('C:/Temp/../..', 'c:\Temp\..\..\test', "./test", true),
+            array('/tmp', '/tmp/../../test', '/test', true),
         );
     }