diff --git a/Tests/PathTest.php b/Tests/PathTest.php index d71f9398..af48e45e 100644 --- a/Tests/PathTest.php +++ b/Tests/PathTest.php @@ -6,6 +6,7 @@ namespace Joomla\Filesystem\Tests; +use Joomla\Filesystem\Exception\FilesystemException; use Joomla\Filesystem\File; use Joomla\Filesystem\Path; @@ -362,4 +363,87 @@ public function testFind() Path::find(__DIR__, 'PathTest.php') ); } + + /** + * Test resolve method + * + * @param string $path test path + * @param string $expectedResult expected path + * + * @return void + * + * @since 1.4.0 + * + * @dataProvider getResolveData + */ + public function testResolve($path, $expectedResult) + { + $this->assertEquals(str_replace("_DS_", DIRECTORY_SEPARATOR, $expectedResult), Path::resolve($path)); + } + + /** + * Test resolve method + + * @param string $path test path + * + * @expectedException Joomla\Filesystem\Exception\FilesystemException + * @expectedExceptionMessage Path is outside of the defined root + * + * @return void + * + * @since 1.4.0 + * + * @dataProvider getResolveExceptionData + */ + public function testResolveThrowsExceptionIfRootIsLeft($path) + { + Path::resolve($path); + } + + /** + * Data provider for testResolve() method. + * + * @return array + * + * @since 1.0 + */ + public function getResolveData() + { + return array( + array("/", "_DS_"), + array("a", "a"), + array("/test/", "_DS_test"), + array("C:/", "C:"), + array("/var/www/joomla", "_DS_var_DS_www_DS_joomla"), + array("C:/iis/www/joomla", "C:_DS_iis_DS_www_DS_joomla"), + array("var/www/joomla", "var_DS_www_DS_joomla"), + array("./var/www/joomla", "var_DS_www_DS_joomla"), + array("/var/www/foo/../joomla", "_DS_var_DS_www_DS_joomla"), + array("C:/var/www/foo/../joomla", "C:_DS_var_DS_www_DS_joomla"), + array("/var/www/../foo/../joomla", "_DS_var_DS_joomla"), + array("C:/var/www/..foo../joomla", "C:_DS_var_DS_www_DS_..foo.._DS_joomla"), + array("c:/var/www/..foo../joomla", "c:_DS_var_DS_www_DS_..foo.._DS_joomla"), + array("/var/www///joomla", "_DS_var_DS_www_DS_joomla"), + array("/var///www///joomla", "_DS_var_DS_www_DS_joomla"), + array("C:/var///www///joomla", "C:_DS_var_DS_www_DS_joomla"), + array("/var/\/../www///joomla", "_DS_www_DS_joomla"), + array("C:/var///www///joomla", "C:_DS_var_DS_www_DS_joomla"), + array("/var\\www///joomla", "_DS_var_DS_www_DS_joomla") + ); + } + + /** + * Data provider for testResolve() method. + * + * @return array + * + * @since 1.0 + */ + public function getResolveExceptionData() + { + return array( + array("../var/www/joomla"), + array("/var/../../../www/joomla") + ); + } } diff --git a/src/Path.php b/src/Path.php index 402a96b9..c9e538a9 100644 --- a/src/Path.php +++ b/src/Path.php @@ -340,4 +340,49 @@ public static function find($paths, $file) // Could not find the file in the set of paths return false; } + + /** + * Resolves /./, /../ and multiple / in a string and returns the resulting absolute path, inspired by Flysystem + * Removes trailing slashes + * + * @param string $path A path to resolve + * + * @return string The resolved path + * + * @since __DEPLOY_VERSION__ + */ + public static function resolve($path) + { + $path = static::clean($path); + + // Save start character for absolute path + $startCharacter = ($path[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : ''; + + $parts = array(); + + foreach (explode(DIRECTORY_SEPARATOR, $path) as $part) + { + switch ($part) + { + case '': + case '.': + break; + + case '..': + if (empty($parts)) + { + throw new FilesystemException('Path is outside of the defined root'); + } + + array_pop($parts); + break; + + default: + $parts[] = $part; + break; + } + } + + return $startCharacter . implode(DIRECTORY_SEPARATOR, $parts); + } }