diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index e5f6cbff8..025cce3a0 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -429,6 +429,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface $e = null; $output = ''; + $targetDir = Filesystem::trimTrailingSlash($targetDir); try { if (is_dir($targetDir.'_compare')) { $this->filesystem->removeDirectory($targetDir.'_compare'); diff --git a/src/Composer/Downloader/PathDownloader.php b/src/Composer/Downloader/PathDownloader.php index 21d0bcb45..77598f71d 100644 --- a/src/Composer/Downloader/PathDownloader.php +++ b/src/Composer/Downloader/PathDownloader.php @@ -47,6 +47,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter */ public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true) { + $path = Filesystem::trimTrailingSlash($path); $url = $package->getDistUrl(); $realUrl = realpath($url); if (false === $realUrl || !file_exists($realUrl) || !is_dir($realUrl)) { @@ -80,6 +81,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter */ public function install(PackageInterface $package, $path, $output = true) { + $path = Filesystem::trimTrailingSlash($path); $url = $package->getDistUrl(); $realUrl = realpath($url); @@ -178,6 +180,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter */ public function remove(PackageInterface $package, $path, $output = true) { + $path = Filesystem::trimTrailingSlash($path); /** * realpath() may resolve Windows junctions to the source path, so we'll check for a junction first * to prevent a false positive when checking if the dist and install paths are the same. @@ -209,6 +212,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter */ public function getVcsReference(PackageInterface $package, $path) { + $path = Filesystem::trimTrailingSlash($path); $parser = new VersionParser; $guesser = new VersionGuesser($this->config, $this->process, $parser); $dumper = new ArrayDumper; diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 62ba9ed23..61491df4c 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -506,6 +506,23 @@ class Filesystem return $prefix.($absolute ? '/' : '').implode('/', $parts); } + /** + * Remove trailing slashes if present to avoid issues with symlinks + * + * And other possible unforeseen disasters, see https://github.com/composer/composer/pull/9422 + * + * @param string $path + * @return bool + */ + public static function trimTrailingSlash($path) + { + if (!preg_match('{^[/\\\\]+$}', $path)) { + $path = rtrim($path, '/\\'); + } + + return $path; + } + /** * Return if the given path is local *