From 146aa4938f25e10831f3d5fe35388739390ba383 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 7 Oct 2019 18:50:18 +0200 Subject: [PATCH 1/8] Command::execute() should always return an integer. --- src/Composer/Command/AboutCommand.php | 2 + .../Command/BaseDependencyCommand.php | 2 +- src/Composer/Command/ClearCacheCommand.php | 2 + src/Composer/Command/ConfigCommand.php | 71 ++++++++++++++----- src/Composer/Command/DependsCommand.php | 2 +- src/Composer/Command/DumpAutoloadCommand.php | 2 + src/Composer/Command/InitCommand.php | 2 + src/Composer/Command/LicensesCommand.php | 2 + src/Composer/Command/ProhibitsCommand.php | 2 +- src/Composer/Command/SearchCommand.php | 2 + src/Composer/Command/SelfUpdateCommand.php | 2 + src/Composer/Command/StatusCommand.php | 2 +- src/Composer/Command/SuggestsCommand.php | 4 +- 13 files changed, 73 insertions(+), 24 deletions(-) diff --git a/src/Composer/Command/AboutCommand.php b/src/Composer/Command/AboutCommand.php index d1472ba17..bf1fa0f9c 100644 --- a/src/Composer/Command/AboutCommand.php +++ b/src/Composer/Command/AboutCommand.php @@ -42,5 +42,7 @@ EOT See https://getcomposer.org/ for more information. EOT ); + + return 0; } } diff --git a/src/Composer/Command/BaseDependencyCommand.php b/src/Composer/Command/BaseDependencyCommand.php index 4c8766ba3..78fe0551e 100644 --- a/src/Composer/Command/BaseDependencyCommand.php +++ b/src/Composer/Command/BaseDependencyCommand.php @@ -62,7 +62,7 @@ class BaseDependencyCommand extends BaseCommand * @param InputInterface $input * @param OutputInterface $output * @param bool $inverted Whether to invert matching process (why-not vs why behaviour) - * @return int|null Exit code of the operation. + * @return int Exit code of the operation. */ protected function doExecute(InputInterface $input, OutputInterface $output, $inverted = false) { diff --git a/src/Composer/Command/ClearCacheCommand.php b/src/Composer/Command/ClearCacheCommand.php index ec51c56d3..08ec04701 100644 --- a/src/Composer/Command/ClearCacheCommand.php +++ b/src/Composer/Command/ClearCacheCommand.php @@ -70,5 +70,7 @@ EOT } $io->writeError('All caches cleared.'); + + return 0; } } diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 71ec9d816..b3efa06f5 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -463,13 +463,19 @@ EOT $this->getIO()->writeError('You are now running Composer with SSL/TLS protection enabled.'); } - return $this->configSource->removeConfigSetting($settingKey); + $this->configSource->removeConfigSetting($settingKey); + + return 0; } if (isset($uniqueConfigValues[$settingKey])) { - return $this->handleSingleValue($settingKey, $uniqueConfigValues[$settingKey], $values, 'addConfigSetting'); + $this->handleSingleValue($settingKey, $uniqueConfigValues[$settingKey], $values, 'addConfigSetting'); + + return 0; } if (isset($multiConfigValues[$settingKey])) { - return $this->handleMultiValue($settingKey, $multiConfigValues[$settingKey], $values, 'addConfigSetting'); + $this->handleMultiValue($settingKey, $multiConfigValues[$settingKey], $values, 'addConfigSetting'); + + return 0; } // handle properties @@ -530,38 +536,51 @@ EOT throw new \InvalidArgumentException('The '.$settingKey.' property can not be set in the global config.json file. Use `composer global config` to apply changes to the global composer.json'); } if ($input->getOption('unset') && (isset($uniqueProps[$settingKey]) || isset($multiProps[$settingKey]))) { - return $this->configSource->removeProperty($settingKey); + $this->configSource->removeProperty($settingKey); + + return 0; } if (isset($uniqueProps[$settingKey])) { - return $this->handleSingleValue($settingKey, $uniqueProps[$settingKey], $values, 'addProperty'); + $this->handleSingleValue($settingKey, $uniqueProps[$settingKey], $values, 'addProperty'); + + return 0; } if (isset($multiProps[$settingKey])) { - return $this->handleMultiValue($settingKey, $multiProps[$settingKey], $values, 'addProperty'); + $this->handleMultiValue($settingKey, $multiProps[$settingKey], $values, 'addProperty'); + + return 0; } // handle repositories if (preg_match('/^repos?(?:itories)?\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { - return $this->configSource->removeRepository($matches[1]); + $this->configSource->removeRepository($matches[1]); + + return 0; } if (2 === count($values)) { - return $this->configSource->addRepository($matches[1], array( + $this->configSource->addRepository($matches[1], array( 'type' => $values[0], 'url' => $values[1], )); + + return 0; } if (1 === count($values)) { $value = strtolower($values[0]); if (true === $booleanValidator($value)) { if (false === $booleanNormalizer($value)) { - return $this->configSource->addRepository($matches[1], false); + $this->configSource->addRepository($matches[1], false); + + return 0; } } else { $value = JsonFile::parseJson($values[0]); + $this->configSource->addRepository($matches[1], $value); - return $this->configSource->addRepository($matches[1], $value); + return 0; } } @@ -571,22 +590,32 @@ EOT // handle extra if (preg_match('/^extra\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { - return $this->configSource->removeProperty($settingKey); + $this->configSource->removeProperty($settingKey); + + return 0; } - return $this->configSource->addProperty($settingKey, $values[0]); + $this->configSource->addProperty($settingKey, $values[0]); + + return 0; } // handle platform if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { - return $this->configSource->removeConfigSetting($settingKey); + $this->configSource->removeConfigSetting($settingKey); + + return 0; } - return $this->configSource->addConfigSetting($settingKey, $values[0]); + $this->configSource->addConfigSetting($settingKey, $values[0]); + + return 0; } if ($settingKey === 'platform' && $input->getOption('unset')) { - return $this->configSource->removeConfigSetting($settingKey); + $this->configSource->removeConfigSetting($settingKey); + + return 0; } // handle auth @@ -595,7 +624,7 @@ EOT $this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); - return; + return 0; } if ($matches[1] === 'bitbucket-oauth') { @@ -618,16 +647,20 @@ EOT $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('username' => $values[0], 'password' => $values[1])); } - return; + return 0; } // handle script if (preg_match('/^scripts\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { - return $this->configSource->removeProperty($settingKey); + $this->configSource->removeProperty($settingKey); + + return 0; } - return $this->configSource->addProperty($settingKey, count($values) > 1 ? $values : $values[0]); + $this->configSource->addProperty($settingKey, count($values) > 1 ? $values : $values[0]); + + return 0; } throw new \InvalidArgumentException('Setting '.$settingKey.' does not exist or is not supported by this command'); diff --git a/src/Composer/Command/DependsCommand.php b/src/Composer/Command/DependsCommand.php index d6adec083..c350fde9b 100644 --- a/src/Composer/Command/DependsCommand.php +++ b/src/Composer/Command/DependsCommand.php @@ -48,7 +48,7 @@ EOT * * @param InputInterface $input * @param OutputInterface $output - * @return int|null + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index 3add15166..dbda29d63 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -84,5 +84,7 @@ EOT } else { $this->getIO()->overwriteError('Generated autoload files containing '. $numberOfClasses .' classes'); } + + return 0; } } diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 6cd722ad5..279e1291e 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -152,6 +152,8 @@ EOT if ($input->isInteractive() && $this->hasDependencies($options) && $io->askConfirmation($question, true)) { $this->installDependencies($output); } + + return 0; } /** diff --git a/src/Composer/Command/LicensesCommand.php b/src/Composer/Command/LicensesCommand.php index b3c30d63b..7537945e9 100644 --- a/src/Composer/Command/LicensesCommand.php +++ b/src/Composer/Command/LicensesCommand.php @@ -110,6 +110,8 @@ EOT default: throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format)); } + + return 0; } /** diff --git a/src/Composer/Command/ProhibitsCommand.php b/src/Composer/Command/ProhibitsCommand.php index 9e5575c74..1e18e5e23 100644 --- a/src/Composer/Command/ProhibitsCommand.php +++ b/src/Composer/Command/ProhibitsCommand.php @@ -48,7 +48,7 @@ EOT * * @param InputInterface $input * @param OutputInterface $output - * @return int|null + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Composer/Command/SearchCommand.php b/src/Composer/Command/SearchCommand.php index 54aa4dcea..0e8aa60e4 100644 --- a/src/Composer/Command/SearchCommand.php +++ b/src/Composer/Command/SearchCommand.php @@ -79,5 +79,7 @@ EOT foreach ($results as $result) { $io->write($result['name'] . (isset($result['description']) ? ' '. $result['description'] : '')); } + + return 0; } } diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 78b27460e..0dba48e28 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -254,6 +254,8 @@ TAGSPUBKEY } else { $io->writeError('A backup of the current version could not be written to '.$backupFile.', no rollback possible'); } + + return 0; } protected function fetchKeys(IOInterface $io, Config $config) diff --git a/src/Composer/Command/StatusCommand.php b/src/Composer/Command/StatusCommand.php index cd153fc58..116d5b99e 100644 --- a/src/Composer/Command/StatusCommand.php +++ b/src/Composer/Command/StatusCommand.php @@ -61,7 +61,7 @@ EOT /** * @param InputInterface $input * @param OutputInterface $output - * @return int|null + * @return int */ protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index a200f8f69..add539ba2 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -118,7 +118,7 @@ EOT $io->write(sprintf('%s', $suggestion)); } - return; + return 0; } // Grouped by package @@ -148,5 +148,7 @@ EOT $io->write(''); } } + + return 0; } } From 20eb9e66cf4b156a8459484e9cc2aa579fcf9052 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Tue, 15 Oct 2019 08:25:00 +0200 Subject: [PATCH 2/8] Consider replaces when checking package dependents --- src/Composer/Repository/BaseRepository.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/BaseRepository.php b/src/Composer/Repository/BaseRepository.php index 2b30b63cd..295b2b919 100644 --- a/src/Composer/Repository/BaseRepository.php +++ b/src/Composer/Repository/BaseRepository.php @@ -67,6 +67,24 @@ abstract class BaseRepository implements RepositoryInterface // Replacements are considered valid reasons for a package to be installed during forward resolution if (!$invert) { $links += $package->getReplaces(); + + foreach ($package->getReplaces() as $link) { + foreach ($needles as $needle) { + if ($link->getSource() === $needle) { + if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) { + // already displayed this node's dependencies, cutting short + if (in_array($link->getTarget(), $packagesInTree)) { + $results[$link->getTarget()] = array($package, $link, false); + continue; + } + $packagesInTree[] = $link->getTarget(); + $dependents = $recurse ? $this->getDependents($link->getTarget(), null, false, true, $packagesInTree) : array(); + $results[] = array($package, $link, $dependents); + $needles[] = $link->getTarget(); + } + } + } + } } // Require-dev is only relevant for the root package @@ -81,12 +99,12 @@ abstract class BaseRepository implements RepositoryInterface if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) { // already displayed this node's dependencies, cutting short if (in_array($link->getSource(), $packagesInTree)) { - $results[$link->getSource()] = array($package, $link, false); + $results[] = array($package, $link, false); continue; } $packagesInTree[] = $link->getSource(); $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree) : array(); - $results[$link->getSource()] = array($package, $link, $dependents); + $results[] = array($package, $link, $dependents); } } } From 80317eb289fd6993e57af981d7922cd99563146f Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Tue, 15 Oct 2019 10:56:43 +0200 Subject: [PATCH 3/8] Remove invalid array keys --- src/Composer/Repository/BaseRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/BaseRepository.php b/src/Composer/Repository/BaseRepository.php index 295b2b919..5ee866543 100644 --- a/src/Composer/Repository/BaseRepository.php +++ b/src/Composer/Repository/BaseRepository.php @@ -74,7 +74,7 @@ abstract class BaseRepository implements RepositoryInterface if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) { // already displayed this node's dependencies, cutting short if (in_array($link->getTarget(), $packagesInTree)) { - $results[$link->getTarget()] = array($package, $link, false); + $results[] = array($package, $link, false); continue; } $packagesInTree[] = $link->getTarget(); From 5f202efa0bcf8f51f13ab073a09b42ebfb6c8f26 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Wed, 16 Oct 2019 13:36:28 +0200 Subject: [PATCH 4/8] Added comment why source link check is necessary --- src/Composer/Repository/BaseRepository.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Repository/BaseRepository.php b/src/Composer/Repository/BaseRepository.php index 5ee866543..d668f43cb 100644 --- a/src/Composer/Repository/BaseRepository.php +++ b/src/Composer/Repository/BaseRepository.php @@ -68,6 +68,9 @@ abstract class BaseRepository implements RepositoryInterface if (!$invert) { $links += $package->getReplaces(); + // On forward search, check if any replaced package was required and add the replaced + // packages to the list of needles. Contrary to the cross-reference link check below, + // replaced packages are the target of links. foreach ($package->getReplaces() as $link) { foreach ($needles as $needle) { if ($link->getSource() === $needle) { From c2f1a6b643b09fc5a095e4c75f437370165cf1e5 Mon Sep 17 00:00:00 2001 From: Mikhail Fesenko Date: Thu, 17 Oct 2019 13:17:13 +0300 Subject: [PATCH 5/8] Added clear cache for windows, fix tests --- src/Composer/Util/Filesystem.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index c025a6b8c..7cdad1df4 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -682,12 +682,14 @@ class Filesystem if (!Platform::isWindows()) { return false; } + + // Important to clear all caches first + clearstatcache(true, $junction); + if (!is_dir($junction) || is_link($junction)) { return false; } - // Important to clear all caches first - clearstatcache(true, $junction); $stat = lstat($junction); // S_ISDIR test (S_IFDIR is 0x4000, S_IFMT is 0xF000 bitmask) From 8d9b822413c87eed8b7f7bb67285ba9c9c482d19 Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Tue, 8 Oct 2019 18:12:56 +0100 Subject: [PATCH 6/8] Add messages to junction tests to see failures --- tests/Composer/Test/Util/FilesystemTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Composer/Test/Util/FilesystemTest.php b/tests/Composer/Test/Util/FilesystemTest.php index 9f684dbfb..f3fb30278 100644 --- a/tests/Composer/Test/Util/FilesystemTest.php +++ b/tests/Composer/Test/Util/FilesystemTest.php @@ -300,16 +300,16 @@ class FilesystemTest extends TestCase // Create and detect junction $fs->junction($target, $junction); - $this->assertTrue($fs->isJunction($junction)); - $this->assertFalse($fs->isJunction($target)); - $this->assertTrue($fs->isJunction($target . '/../../junction')); - $this->assertFalse($fs->isJunction($junction . '/../real')); - $this->assertTrue($fs->isJunction($junction . '/../junction')); + $this->assertTrue($fs->isJunction($junction), $junction . ': is a junction'); + $this->assertFalse($fs->isJunction($target), $target . ': is not a junction'); + $this->assertTrue($fs->isJunction($target . '/../../junction'), $target . '/../../junction: is a junction'); + $this->assertFalse($fs->isJunction($junction . '/../real'), $junction . '/../real: is not a junction'); + $this->assertTrue($fs->isJunction($junction . '/../junction'), $junction . '/../junction: is a junction'); // Remove junction - $this->assertTrue(is_dir($junction)); - $this->assertTrue($fs->removeJunction($junction)); - $this->assertFalse(is_dir($junction)); + $this->assertTrue(is_dir($junction), $junction . ' is a directory'); + $this->assertTrue($fs->removeJunction($junction), $junction . ' has been removed'); + $this->assertFalse(is_dir($junction), $junction . ' is not a directory'); } public function testCopy() From 73b269fade421ed799d3ef321b70f228896dc779 Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Mon, 14 Oct 2019 12:49:59 +0100 Subject: [PATCH 7/8] HgDriver: don't run command in non-existing directory --- src/Composer/Repository/Vcs/HgDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/HgDriver.php b/src/Composer/Repository/Vcs/HgDriver.php index 3812e7a66..04a363442 100644 --- a/src/Composer/Repository/Vcs/HgDriver.php +++ b/src/Composer/Repository/Vcs/HgDriver.php @@ -71,7 +71,7 @@ class HgDriver extends VcsDriver return sprintf('hg clone --noupdate %s %s', ProcessExecutor::escape($url), ProcessExecutor::escape($repoDir)); }; - $hgUtils->runCommand($command, $this->url, $this->repoDir); + $hgUtils->runCommand($command, $this->url, null); } } From 9c9ca875378b16c67c09d9b08d0c2111378d1d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Anne?= Date: Mon, 21 Oct 2019 12:50:14 +0200 Subject: [PATCH 8/8] Fix composer outdated command on PHP 7.4; fixes #8346 --- src/Composer/Command/OutdatedCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/OutdatedCommand.php b/src/Composer/Command/OutdatedCommand.php index ae26a7487..599087246 100644 --- a/src/Composer/Command/OutdatedCommand.php +++ b/src/Composer/Command/OutdatedCommand.php @@ -59,7 +59,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { $args = array( - 'show', + 'command' => 'show', '--latest' => true, ); if (!$input->getOption('all')) {