diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 6cc35ebe2..0454efa59 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -319,14 +319,6 @@ EOT $composer->getEventDispatcher()->dispatchScript(ScriptEvents::POST_CREATE_PROJECT_CMD, $installDevPackages); chdir($oldCwd); - $vendorComposerDir = $config->get('vendor-dir').'/composer'; - if (is_dir($vendorComposerDir) && $fs->isDirEmpty($vendorComposerDir)) { - Silencer::call('rmdir', $vendorComposerDir); - $vendorDir = $config->get('vendor-dir'); - if (is_dir($vendorDir) && $fs->isDirEmpty($vendorDir)) { - Silencer::call('rmdir', $vendorDir); - } - } return 0; } @@ -338,10 +330,6 @@ EOT */ protected function installRootPackage(InputInterface $input, IOInterface $io, Config $config, string $packageName, PlatformRequirementFilterInterface $platformRequirementFilter, ?string $directory = null, ?string $packageVersion = null, ?string $stability = 'stable', bool $preferSource = false, bool $preferDist = false, bool $installDevPackages = false, ?array $repositories = null, bool $disablePlugins = false, bool $disableScripts = false, bool $noProgress = false, bool $secureHttp = true): bool { - if (!$secureHttp) { - $config->merge(['config' => ['secure-http' => false]], Config::SOURCE_COMMAND); - } - $parser = new VersionParser(); $requirements = $parser->parseNameVersionPairs([$packageName]); $name = strtolower($requirements[0]['name']); @@ -354,12 +342,22 @@ EOT $parts = explode("/", $name, 2); $directory = Platform::getCwd() . DIRECTORY_SEPARATOR . array_pop($parts); } + $directory = rtrim($directory, '/\\'); $process = new ProcessExecutor($io); $fs = new Filesystem($process); if (!$fs->isAbsolutePath($directory)) { $directory = Platform::getCwd() . DIRECTORY_SEPARATOR . $directory; } + if ('' === $directory) { + throw new \UnexpectedValueException('Got an empty target directory, something went wrong'); + } + + // set the base dir to ensure $config->all() below resolves the correct absolute paths to vendor-dir etc + $config->setBaseDir($directory); + if (!$secureHttp) { + $config->merge(['config' => ['secure-http' => false]], Config::SOURCE_COMMAND); + } $io->writeError('Creating a "' . $packageName . '" project at "' . $fs->findShortestPath(Platform::getCwd(), $directory, true) . '"'); @@ -390,6 +388,8 @@ EOT $composer = $this->createComposerInstance($input, $io, $config->all(), $disablePlugins, $disableScripts); $config = $composer->getConfig(); + // set the base dir here again on the new config instance, as otherwise in case the vendor dir is defined in an env var for example it would still override the value set above by $config->all() + $config->setBaseDir($directory); $rm = $composer->getRepositoryManager(); $repositorySet = new RepositorySet($stability); @@ -424,16 +424,21 @@ EOT throw new \InvalidArgumentException($errorMessage .'.'); } + $oldCwd = Platform::getCwd(); // handler Ctrl+C aborts gracefully @mkdir($directory, 0777, true); if (false !== ($realDir = realpath($directory))) { - $signalHandler = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], function (string $signal, SignalHandler $handler) use ($realDir) { + $signalHandler = SignalHandler::create([SignalHandler::SIGINT, SignalHandler::SIGTERM, SignalHandler::SIGHUP], function (string $signal, SignalHandler $handler) use ($realDir, $oldCwd) { + chdir($oldCwd); $this->getIO()->writeError('Received '.$signal.', aborting', true, IOInterface::DEBUG); $fs = new Filesystem(); $fs->removeDirectory($realDir); $handler->exitWithLastSignal(); }); } + if (!chdir($directory)) { + throw new \RuntimeException('Failed to chdir into the new project dir at '.$directory); + } // avoid displaying 9999999-dev as version if default-branch was selected if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEFAULT_BRANCH_ALIAS) { @@ -467,7 +472,6 @@ EOT $installedFromVcs = 'source' === $package->getInstallationSource(); $io->writeError('Created project in ' . $directory . ''); - chdir($directory); // ensure that the env var being set does not interfere with create-project // as it is probably not meant to be used here, so we do not use it if a composer.json can be found diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 6844eb1e2..f39579e06 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -98,7 +98,7 @@ class Config /** @var array */ private $config; - /** @var ?string */ + /** @var ?non-empty-string */ private $baseDir; /** @var array */ private $repositories; @@ -139,6 +139,18 @@ class Config } } + /** + * Changing this can break path resolution for relative config paths so do not call this without knowing what you are doing + * + * The $baseDir should be an absolute path and without trailing slash + * + * @param non-empty-string|null $baseDir + */ + public function setBaseDir(?string $baseDir): void + { + $this->baseDir = $baseDir; + } + public function setConfigSource(ConfigSourceInterface $source): void { $this->configSource = $source; @@ -546,7 +558,7 @@ class Config return $path; } - return $this->baseDir ? $this->baseDir . '/' . $path : $path; + return $this->baseDir !== null ? $this->baseDir . '/' . $path : $path; } /** diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php index 62e918324..52da0d604 100644 --- a/src/Composer/Repository/RepositoryFactory.php +++ b/src/Composer/Repository/RepositoryFactory.php @@ -178,7 +178,7 @@ class RepositoryFactory /** * @param int|string $index * @param array{url?: string} $repo - * @param array $existingRepos + * @param array $existingRepos */ public static function generateRepositoryName($index, array $repo, array $existingRepos): string {