From 6492142634d7fc7c73a0d3cb9f00fe92bca48db9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 12 Jul 2021 13:54:01 +0200 Subject: [PATCH] Fix support for writing into UNC paths, and comparing UNC paths correctly in InstalledVersions, fixes #9993 --- src/Composer/Factory.php | 11 +++++-- .../Repository/FilesystemRepository.php | 32 ++++++++++++------- tests/Composer/Test/Mock/FactoryMock.php | 2 +- .../Repository/FilesystemRepositoryTest.php | 9 ++++-- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index f4fe0627413c..6aed454543c0 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -385,7 +385,7 @@ public function createComposer(IOInterface $io, $localConfig = null, $disablePlu $composer->setPackage($package); // load local repository - $this->addLocalRepository($io, $rm, $vendorDir, $package); + $this->addLocalRepository($io, $rm, $vendorDir, $package, $process); // initialize installation manager $im = $this->createInstallationManager($loop, $io, $dispatcher); @@ -458,9 +458,14 @@ public static function createGlobal(IOInterface $io, $disablePlugins = false) * @param Repository\RepositoryManager $rm * @param string $vendorDir */ - protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage) + protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage, ProcessExecutor $process = null) { - $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io), true, $rootPackage)); + $fs = null; + if ($process) { + $fs = new Filesystem($process); + } + + $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io), true, $rootPackage, $fs)); } /** diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index 696b00541c9b..976eb957dca5 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -31,6 +31,7 @@ class FilesystemRepository extends WritableArrayRepository protected $file; private $dumpVersions; private $rootPackage; + private $filesystem; /** * Initializes filesystem repository. @@ -39,12 +40,13 @@ class FilesystemRepository extends WritableArrayRepository * @param bool $dumpVersions * @param ?RootPackageInterface $rootPackage Must be provided if $dumpVersions is true */ - public function __construct(JsonFile $repositoryFile, $dumpVersions = false, RootPackageInterface $rootPackage = null) + public function __construct(JsonFile $repositoryFile, $dumpVersions = false, RootPackageInterface $rootPackage = null, Filesystem $filesystem = null) { parent::__construct(); $this->file = $repositoryFile; $this->dumpVersions = $dumpVersions; $this->rootPackage = $rootPackage; + $this->filesystem = $filesystem ?: new Filesystem; if ($dumpVersions && !$rootPackage) { throw new \InvalidArgumentException('Expected a root package instance if $dumpVersions is true'); } @@ -100,14 +102,24 @@ public function write($devMode, InstallationManager $installationManager) { $data = array('packages' => array(), 'dev' => $devMode, 'dev-package-names' => array()); $dumper = new ArrayDumper(); - $fs = new Filesystem(); - $repoDir = dirname($fs->normalizePath($this->file->getPath())); + + // make sure the directory is created so we can realpath it + // as realpath() does some additional normalizations with network paths that normalizePath does not + // and we need to find shortest path correctly + $repoDir = dirname($this->file->getPath()); + $this->filesystem->ensureDirectoryExists($repoDir); + + $repoDir = $this->filesystem->normalizePath(realpath($repoDir)); $installPaths = array(); foreach ($this->getCanonicalPackages() as $package) { $pkgArray = $dumper->dump($package); $path = $installationManager->getInstallPath($package); - $installPath = ('' !== $path && null !== $path) ? $fs->findShortestPath($repoDir, $fs->isAbsolutePath($path) ? $path : getcwd() . '/' . $path, true) : null; + $installPath = null; + if ('' !== $path && null !== $path) { + $normalizedPath = $this->filesystem->normalizePath($this->filesystem->isAbsolutePath($path) ? $path : getcwd() . '/' . $path); + $installPath = $this->filesystem->findShortestPath($repoDir, $normalizedPath, true); + } $installPaths[$package->getName()] = $installPath; $pkgArray['install-path'] = $installPath; @@ -130,9 +142,9 @@ public function write($devMode, InstallationManager $installationManager) if ($this->dumpVersions) { $versions = $this->generateInstalledVersions($installationManager, $installPaths, $devMode, $repoDir); - $fs->filePutContentsIfModified($repoDir.'/installed.php', 'dumpToPhpCode($versions) . ';'."\n"); + $this->filesystem->filePutContentsIfModified($repoDir.'/installed.php', 'dumpToPhpCode($versions) . ';'."\n"); $installedVersionsClass = file_get_contents(__DIR__.'/../InstalledVersions.php'); - $fs->filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass); + $this->filesystem->filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass); \Composer\InstalledVersions::reload($versions); } @@ -154,8 +166,7 @@ private function dumpToPhpCode(array $array = array(), $level = 0) $lines .= "array(),\n"; } } elseif ($key === 'install_path' && is_string($value)) { - $fs = new Filesystem(); - if ($fs->isAbsolutePath($value)) { + if ($this->filesystem->isAbsolutePath($value)) { $lines .= var_export($value, true) . ",\n"; } else { $lines .= "__DIR__ . " . var_export('/' . $value, true) . ",\n"; @@ -203,9 +214,8 @@ private function generateInstalledVersions(InstallationManager $installationMana } if ($package instanceof RootPackageInterface) { - $fs = new Filesystem(); - $to = getcwd(); - $installPath = $fs->findShortestPath($repoDir, $to, true); + $to = $this->filesystem->normalizePath(realpath(getcwd())); + $installPath = $this->filesystem->findShortestPath($repoDir, $to, true); } else { $installPath = $installPaths[$package->getName()]; } diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php index d8b6186479c1..c095bc639690 100644 --- a/tests/Composer/Test/Mock/FactoryMock.php +++ b/tests/Composer/Test/Mock/FactoryMock.php @@ -46,7 +46,7 @@ protected function loadRootPackage(RepositoryManager $rm, Config $config, Versio return new \Composer\Package\Loader\RootPackageLoader($rm, $config, $parser, new VersionGuesserMock(), $io); } - protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage) + protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage, ProcessExecutor $process = null) { } diff --git a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php index da754661be18..efec2cd9adff 100644 --- a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php +++ b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php @@ -16,6 +16,7 @@ use Composer\Repository\FilesystemRepository; use Composer\Test\TestCase; use Composer\Json\JsonFile; +use Composer\Util\Filesystem; class FilesystemRepositoryTest extends TestCase { @@ -83,13 +84,17 @@ public function testRepositoryWrite() { $json = $this->createJsonFileMock(); + $repoDir = sys_get_temp_dir().'/repo_write_test/'; + $fs = new Filesystem(); + $fs->removeDirectory($repoDir); + $repository = new FilesystemRepository($json); $im = $this->getMockBuilder('Composer\Installer\InstallationManager') ->disableOriginalConstructor() ->getMock(); $im->expects($this->exactly(2)) ->method('getInstallPath') - ->will($this->returnValue('/foo/bar/vendor/woop/woop')); + ->will($this->returnValue($repoDir.'/vendor/woop/woop')); $json ->expects($this->once()) @@ -98,7 +103,7 @@ public function testRepositoryWrite() $json ->expects($this->once()) ->method('getPath') - ->will($this->returnValue('/foo/bar/vendor/composer/installed.json')); + ->will($this->returnValue($repoDir.'/vendor/composer/installed.json')); $json ->expects($this->once()) ->method('exists')