From b60e44c1dc774e37d19e4458f4ed52aad31a1088 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 2 Aug 2019 20:41:50 +0200 Subject: [PATCH 01/63] Update deps --- composer.lock | 38 +++++++++++++++++++------------------- doc/articles/scripts.md | 3 +++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 8b7b3cb57..ca55827ee 100644 --- a/composer.lock +++ b/composer.lock @@ -8,25 +8,25 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.1.4", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d" + "reference": "f26a67e397be0e5c00d7c52ec7b5010098e15ce5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/558f321c52faeb4828c03e7dc0cfe39a09e09a2d", - "reference": "558f321c52faeb4828c03e7dc0cfe39a09e09a2d", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/f26a67e397be0e5c00d7c52ec7b5010098e15ce5", + "reference": "f26a67e397be0e5c00d7c52ec7b5010098e15ce5", "shasum": "" }, "require": { "ext-openssl": "*", "ext-pcre": "*", - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", "psr/log": "^1.0", "symfony/process": "^2.5 || ^3.0 || ^4.0" }, @@ -60,7 +60,7 @@ "ssl", "tls" ], - "time": "2019-01-28T09:30:10+00:00" + "time": "2019-08-02T09:05:43+00:00" }, { "name": "composer/semver", @@ -126,16 +126,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.5.1", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d" + "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d", - "reference": "a1aa51cf3ab838b83b0867b14e56fc20fbd55b3d", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5", + "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5", "shasum": "" }, "require": { @@ -182,7 +182,7 @@ "spdx", "validator" ], - "time": "2019-03-26T10:23:26+00:00" + "time": "2019-07-29T10:31:59+00:00" }, { "name": "composer/xdebug-handler", @@ -924,16 +924,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", "shasum": "" }, "require": { @@ -954,8 +954,8 @@ } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -983,7 +983,7 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2019-06-13T12:50:23+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index 807423274..38f6861b6 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -339,6 +339,9 @@ One limitation of this is that you can not call multiple commands in a row like `@php install && @php foo`. You must split them up in a JSON array of commands. +You can also call a shell/bash script, which will have the path to +the PHP executable available in it as a `PHP_BINARY` env var. + ## Custom descriptions. You can set custom script descriptions with the following in your `composer.json`: From dc964d6515f7e4d609b86ff5b2ba917e2550e783 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 2 Aug 2019 20:53:12 +0200 Subject: [PATCH 02/63] Update changelog --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e6075148..859f8112e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +### [1.9.0] 2019-08-02 + + * Added a `--no-cache` flag available on all commands to run with the cache disabled + * Added PHP_BINARY as env var pointing to the PHP process when executing Composer scripts as shell scripts + * Added a `use-github-api` config option which can set the `no-api` flag on all GitHub VCS repositories declared + * Added a static helper you can preprend to a script to avoid process timeouts, `"Composer\\Config::disableProcessTimeout"` + * Added Event::getOriginatingEvent to retrieve an event's original event when a script handler forwards to another one + * Added support for autoloading directly from a phar file + * Fixed loading order of plugins to always initialize them in order of dependencies + * Fixed various network-mount related issues + * Fixed --ignore-platform-reqs not ignoring conflict rules against platform packages + ### [1.8.6] 2019-06-11 * Fixed handling of backslash-escapes handling in composer.json when using the require command @@ -751,6 +763,7 @@ * Initial release +[1.9.0]: https://github.com/composer/composer/compare/1.8.6...1.9.0 [1.8.6]: https://github.com/composer/composer/compare/1.8.5...1.8.6 [1.8.5]: https://github.com/composer/composer/compare/1.8.4...1.8.5 [1.8.4]: https://github.com/composer/composer/compare/1.8.3...1.8.4 From bfba228b5a232bcb9e4bb7941f0a0aaa37bab117 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 13 Aug 2019 17:28:15 +0200 Subject: [PATCH 03/63] Add note about breaking change, fixes #8274 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 859f8112e..15526d860 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ### [1.9.0] 2019-08-02 + * Breaking: artifact repositories with URLs containing port numbers and requiring authentication now require you to configure http-basic auth for the `host:port` pair explicitly * Added a `--no-cache` flag available on all commands to run with the cache disabled * Added PHP_BINARY as env var pointing to the PHP process when executing Composer scripts as shell scripts * Added a `use-github-api` config option which can set the `no-api` flag on all GitHub VCS repositories declared From f9fccbab1e5e8b849b3790a9eb0c763835443917 Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Wed, 21 Aug 2019 10:07:36 +0100 Subject: [PATCH 04/63] GitHub: don't display access token in debug log --- src/Composer/Util/RemoteFilesystem.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 32767c161..2e3f4627b 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -301,7 +301,7 @@ class RemoteFilesystem $actualContextOptions = stream_context_get_options($ctx); $usingProxy = !empty($actualContextOptions['http']['proxy']) ? ' using proxy ' . $actualContextOptions['http']['proxy'] : ''; - $this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $origFileUrl . $usingProxy, true, IOInterface::DEBUG); + $this->io->writeError((substr($origFileUrl, 0, 4) === 'http' ? 'Downloading ' : 'Reading ') . $this->stripCredentialsFromUrl($origFileUrl) . $usingProxy, true, IOInterface::DEBUG); unset($origFileUrl, $actualContextOptions); // Check for secure HTTP, but allow insecure Packagist calls to $hashed providers as file integrity is verified with sha256 @@ -873,7 +873,7 @@ class RemoteFilesystem $this->redirects++; $this->io->writeError('', true, IOInterface::DEBUG); - $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $targetUrl), true, IOInterface::DEBUG); + $this->io->writeError(sprintf('Following redirect (%u) %s', $this->redirects, $this->stripCredentialsFromUrl($targetUrl)), true, IOInterface::DEBUG); $additionalOptions['redirects'] = $this->redirects; @@ -1123,4 +1123,15 @@ class RemoteFilesystem return $hostPort; } + + private function stripCredentialsFromUrl($url) + { + // GitHub repository rename result in redirect locations containing the access_token as GET parameter + // e.g. https://api.github.com/repositories/9999999999?access_token=github_token + if (preg_match('{^(https?://([a-z0-9-]+\.)*github\.com/.*)\?access_token=[a-z0-9]+}', $url, $matches)) { + return $matches[1]; + } + + return $url; + } } From 0500e64f8826e73fc3a744c1b01d61f9547d1e1d Mon Sep 17 00:00:00 2001 From: David Szkiba Date: Sun, 25 Aug 2019 17:00:25 +0200 Subject: [PATCH 05/63] Respect COMPOSER_NO_INTERATION for search prompt. As described in GH-8289, if no `composer.json` file is found in the current directory, the user is prompted if she wants to use another `composer.json` file from a parent directory even if the `COMPOSER_NO_INTERACTION` environment variable is set. This is fixed here by just moving the check of the environment variable up in the code so that it is evaluated before the user is prompted. --- src/Composer/Console/Application.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index d7d113492..171f4fc5b 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -113,6 +113,10 @@ class Application extends BaseApplication { $this->disablePluginsByDefault = $input->hasParameterOption('--no-plugins'); + if (getenv('COMPOSER_NO_INTERACTION')) { + $input->setInteractive(false); + } + $io = $this->io = new ConsoleIO($input, $output, new HelperSet(array( new QuestionHelper(), ))); @@ -208,10 +212,6 @@ class Application extends BaseApplication $io->writeError(sprintf('Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); } - if (getenv('COMPOSER_NO_INTERACTION')) { - $input->setInteractive(false); - } - if (!Platform::isWindows() && function_exists('exec') && !getenv('COMPOSER_ALLOW_SUPERUSER')) { if (function_exists('posix_getuid') && posix_getuid() === 0) { if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { From 550c01b471c0deecf95647b7b48e7712623208da Mon Sep 17 00:00:00 2001 From: rbairwell Date: Tue, 27 Aug 2019 20:53:57 +0100 Subject: [PATCH 06/63] Fixes a problem with path based repositories on PHP7.4 where an attempt is made to access null as an array --- src/Composer/Repository/PathRepository.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index 61ebc8d8c..20dcbcd6f 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -155,7 +155,11 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn if (!isset($package['version'])) { $versionData = $this->versionGuesser->guessVersion($package, $path); - $package['version'] = $versionData['pretty_version'] ?: 'dev-master'; + if (is_array($versionData)) { + $package['version'] = $versionData['pretty_version'] ?: 'dev-master'; + } else { + $package['version'] = 'dev-master'; + } } $output = ''; From fd70d9cdc31881ae7441560a205fb5e49f647ee9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 29 Aug 2019 12:22:38 +0200 Subject: [PATCH 07/63] Stripe access tokens in a more generic way --- src/Composer/Util/RemoteFilesystem.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 2e3f4627b..a44d8f86f 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -1128,10 +1128,6 @@ class RemoteFilesystem { // GitHub repository rename result in redirect locations containing the access_token as GET parameter // e.g. https://api.github.com/repositories/9999999999?access_token=github_token - if (preg_match('{^(https?://([a-z0-9-]+\.)*github\.com/.*)\?access_token=[a-z0-9]+}', $url, $matches)) { - return $matches[1]; - } - - return $url; + return preg_replace('{([&?]access_token=)[^&]+}', '$1***', $url); } } From 6fee17f16cefbf7b08dad4bfef69919922a1f9aa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 29 Aug 2019 15:09:26 +0200 Subject: [PATCH 08/63] Fix tests, refs #8292 --- tests/Composer/Test/ApplicationTest.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/Composer/Test/ApplicationTest.php b/tests/Composer/Test/ApplicationTest.php index 8739a5004..48615af9c 100644 --- a/tests/Composer/Test/ApplicationTest.php +++ b/tests/Composer/Test/ApplicationTest.php @@ -25,7 +25,13 @@ class ApplicationTest extends TestCase $inputMock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(); $outputMock = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); + putenv('COMPOSER_NO_INTERACTION=1'); + $index = 0; + $inputMock->expects($this->at($index++)) + ->method('setInteractive') + ->with($this->equalTo(false)); + $inputMock->expects($this->at($index++)) ->method('hasParameterOption') ->with($this->equalTo('--no-plugins')) @@ -83,7 +89,13 @@ class ApplicationTest extends TestCase $inputMock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(); $outputMock = $this->getMockBuilder('Symfony\Component\Console\Output\OutputInterface')->getMock(); + putenv('COMPOSER_NO_INTERACTION=1'); + $index = 0; + $inputMock->expects($this->at($index++)) + ->method('setInteractive') + ->with($this->equalTo(false)); + $inputMock->expects($this->at($index++)) ->method('hasParameterOption') ->with($this->equalTo('--no-plugins')) From 7d99a56332895b863ad672457abe07b052a46d9a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 29 Aug 2019 15:16:34 +0200 Subject: [PATCH 09/63] Combine conditionals in one --- src/Composer/Repository/PathRepository.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index 20dcbcd6f..6e218f159 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -155,8 +155,8 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn if (!isset($package['version'])) { $versionData = $this->versionGuesser->guessVersion($package, $path); - if (is_array($versionData)) { - $package['version'] = $versionData['pretty_version'] ?: 'dev-master'; + if (is_array($versionData) && $versionData['pretty_version']) { + $package['version'] = $versionData['pretty_version']; } else { $package['version'] = 'dev-master'; } From a403ee9b0e4dbd971334a63dd8a7925d45c68153 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 29 Aug 2019 15:23:45 +0200 Subject: [PATCH 10/63] Fix tests for reals --- tests/Composer/Test/ApplicationTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Composer/Test/ApplicationTest.php b/tests/Composer/Test/ApplicationTest.php index 48615af9c..97921950f 100644 --- a/tests/Composer/Test/ApplicationTest.php +++ b/tests/Composer/Test/ApplicationTest.php @@ -28,15 +28,15 @@ class ApplicationTest extends TestCase putenv('COMPOSER_NO_INTERACTION=1'); $index = 0; - $inputMock->expects($this->at($index++)) - ->method('setInteractive') - ->with($this->equalTo(false)); - $inputMock->expects($this->at($index++)) ->method('hasParameterOption') ->with($this->equalTo('--no-plugins')) ->will($this->returnValue(true)); + $inputMock->expects($this->at($index++)) + ->method('setInteractive') + ->with($this->equalTo(false)); + $inputMock->expects($this->at($index++)) ->method('hasParameterOption') ->with($this->equalTo('--no-cache')) @@ -92,15 +92,15 @@ class ApplicationTest extends TestCase putenv('COMPOSER_NO_INTERACTION=1'); $index = 0; - $inputMock->expects($this->at($index++)) - ->method('setInteractive') - ->with($this->equalTo(false)); - $inputMock->expects($this->at($index++)) ->method('hasParameterOption') ->with($this->equalTo('--no-plugins')) ->will($this->returnValue(true)); + $inputMock->expects($this->at($index++)) + ->method('setInteractive') + ->with($this->equalTo(false)); + $inputMock->expects($this->at($index++)) ->method('hasParameterOption') ->with($this->equalTo('--no-cache')) From f0d565bb6d354331cadf12712b4f6e8ebbaf51a6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 30 Aug 2019 17:52:08 +0200 Subject: [PATCH 11/63] Avoid transforming the origins when prompting for auth, fixes #8300 --- src/Composer/Util/Git.php | 2 +- src/Composer/Util/RemoteFilesystem.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 74e5c286f..20457d708 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -193,7 +193,7 @@ class Git } } - $this->io->writeError(' Authentication required (' . parse_url($url, PHP_URL_HOST) . '):'); + $this->io->writeError(' Authentication required (' . $match[2] . '):'); $auth = array( 'username' => $this->io->ask(' Username: ', $defaultUsername), 'password' => $this->io->askAndHideAnswer(' Password: '), diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index a44d8f86f..b3f51aae5 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -745,7 +745,7 @@ class RemoteFilesystem throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); } - $this->io->writeError(' Authentication required ('.parse_url($this->fileUrl, PHP_URL_HOST).'):'); + $this->io->writeError(' Authentication required ('.$this->originUrl.'):'); $username = $this->io->ask(' Username: '); $password = $this->io->askAndHideAnswer(' Password: '); $this->io->setAuthentication($this->originUrl, $username, $password); From b09945c3ba7ac28019a1780f4e044a44ad55325f Mon Sep 17 00:00:00 2001 From: Grey Baker Date: Mon, 2 Sep 2019 22:26:35 +0100 Subject: [PATCH 12/63] Allow for leading slash in path part of GitHub URLs --- src/Composer/Repository/Vcs/GitHubDriver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 529112811..2fc4ccffe 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -47,7 +47,7 @@ class GitHubDriver extends VcsDriver */ public function initialize() { - preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $this->url, $match); + preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/(.+?)(?:\.git|/)?$#', $this->url, $match); $this->owner = $match[3]; $this->repository = $match[4]; $this->originUrl = !empty($match[1]) ? $match[1] : $match[2]; @@ -267,7 +267,7 @@ class GitHubDriver extends VcsDriver */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { - if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $url, $matches)) { + if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/(.+?)(?:\.git|/)?$#', $url, $matches)) { return false; } From e3b6c67a449e9375385f596c2ab5cadcafdf1afc Mon Sep 17 00:00:00 2001 From: Nikita Konstantinov Date: Wed, 4 Sep 2019 21:48:40 +0300 Subject: [PATCH 13/63] Provide `--fixed` option for the `require` command, fixes #8303 --- src/Composer/Command/InitCommand.php | 11 ++++++----- src/Composer/Command/RequireCommand.php | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 6cd722ad5..f4d6d99d3 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -400,7 +400,7 @@ EOT return $this->repos; } - protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true) + final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false) { if ($requires) { $requires = $this->normalizeRequirements($requires); @@ -410,7 +410,7 @@ EOT foreach ($requires as $requirement) { if (!isset($requirement['version'])) { // determine the best version automatically - list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability); + list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, null, null, $fixed); $requirement['version'] = $version; // replace package name from packagist.org @@ -423,7 +423,7 @@ EOT )); } else { // check that the specified version/constraint exists before we proceed - list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev'); + list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $phpVersion, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed); // replace package name from packagist.org $requirement['name'] = $name; @@ -700,10 +700,11 @@ EOT * @param string $preferredStability * @param string|null $requiredVersion * @param string $minimumStability + * @param bool $fixed * @throws \InvalidArgumentException * @return array name version */ - private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null) + private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) { // find the latest version allowed in this pool $versionSelector = new VersionSelector($this->getPool($input, $minimumStability)); @@ -777,7 +778,7 @@ EOT return array( $package->getPrettyName(), - $versionSelector->findRecommendedRequireVersion($package), + $fixed ? $package->getPrettyVersion() : $versionSelector->findRecommendedRequireVersion($package), ); } diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 8f91b6675..c922875f8 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -49,6 +49,7 @@ class RequireCommand extends InitCommand new InputOption('dev', null, InputOption::VALUE_NONE, 'Add requirement to require-dev.'), new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), + new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'Do not show package suggestions.'), new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), @@ -120,6 +121,25 @@ EOT return 1; } + if ($input->getOption('fixed') === true) { + $config = $this->json->read(); + + $packageType = empty($config['type']) ? 'library' : $config['type']; + + /** + * @see https://github.com/composer/composer/pull/8313#issuecomment-532637955 + */ + if ($packageType !== 'project') { + $io->writeError('"--fixed" option is allowed for "project" package types only to prevent possible misuses.'); + + if (empty($config['type'])) { + $io->writeError('If your package is not library, you should explicitly specify "type" parameter in composer.json.'); + } + + return 1; + } + } + $composer = $this->getComposer(true, $input->getOption('no-plugins')); $repos = $composer->getRepositoryManager()->getRepositories(); @@ -137,7 +157,7 @@ EOT } $phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion(); - $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update')); + $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed')); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; From ec96e5de64a9e6da3c837eef5ac23346c2cdbc1e Mon Sep 17 00:00:00 2001 From: zakonnic Date: Fri, 20 Sep 2019 12:58:09 +0300 Subject: [PATCH 14/63] Fix incorrect strip of non-php blocks in the file --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 1ecf96bfe..c3a3a07d8 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -173,7 +173,7 @@ class ClassMapGenerator } } // strip non-php blocks in the file - $contents = preg_replace('{\?>.+<\?}s', '?>.+?<\?}s', '?>'); if (false !== $pos && false === strpos(substr($contents, $pos), ' Date: Fri, 20 Sep 2019 16:59:53 +0300 Subject: [PATCH 15/63] Optimize template for striping non-php blocks --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index c3a3a07d8..8467c742e 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -173,7 +173,7 @@ class ClassMapGenerator } } // strip non-php blocks in the file - $contents = preg_replace('{\?>.+?<\?}s', '?>(?:[^<]++|<(?!\?))*+<\?}s', '?>'); if (false !== $pos && false === strpos(substr($contents, $pos), ' Date: Fri, 20 Sep 2019 17:08:00 +0300 Subject: [PATCH 16/63] Add test for double gap in php-file --- tests/Composer/Test/Autoload/Fixtures/classmap/LargeGap.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Autoload/Fixtures/classmap/LargeGap.php b/tests/Composer/Test/Autoload/Fixtures/classmap/LargeGap.php index 1ad22ecf8..7fe3bee86 100644 --- a/tests/Composer/Test/Autoload/Fixtures/classmap/LargeGap.php +++ b/tests/Composer/Test/Autoload/Fixtures/classmap/LargeGap.php @@ -1385,6 +1385,10 @@ namespace Foo; + public function a1381() { var_dump(var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null), var_dump(null)); } + Date: Sun, 6 Oct 2019 21:27:33 +0200 Subject: [PATCH 17/63] Make gitlab/bitbucket driver checks case insensitive --- src/Composer/Repository/Vcs/BitbucketDriver.php | 2 +- src/Composer/Repository/Vcs/GitBitbucketDriver.php | 2 +- src/Composer/Repository/Vcs/GitHubDriver.php | 4 ++-- src/Composer/Repository/Vcs/GitLabDriver.php | 2 ++ src/Composer/Repository/Vcs/HgBitbucketDriver.php | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Composer/Repository/Vcs/BitbucketDriver.php b/src/Composer/Repository/Vcs/BitbucketDriver.php index 730edec3c..c360bad10 100644 --- a/src/Composer/Repository/Vcs/BitbucketDriver.php +++ b/src/Composer/Repository/Vcs/BitbucketDriver.php @@ -46,7 +46,7 @@ abstract class BitbucketDriver extends VcsDriver */ public function initialize() { - preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+?)(\.git|/?)$#', $this->url, $match); + preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+?)(\.git|/?)$#i', $this->url, $match); $this->owner = $match[1]; $this->repository = $match[2]; $this->originUrl = 'bitbucket.org'; diff --git a/src/Composer/Repository/Vcs/GitBitbucketDriver.php b/src/Composer/Repository/Vcs/GitBitbucketDriver.php index c8a5c9905..82d934b5b 100644 --- a/src/Composer/Repository/Vcs/GitBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/GitBitbucketDriver.php @@ -53,7 +53,7 @@ class GitBitbucketDriver extends BitbucketDriver */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { - if (!preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#', $url)) { + if (!preg_match('#^https?://bitbucket\.org/([^/]+)/(.+?)\.git$#i', $url)) { return false; } diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 2fc4ccffe..e44dd875b 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -50,7 +50,7 @@ class GitHubDriver extends VcsDriver preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/(.+?)(?:\.git|/)?$#', $this->url, $match); $this->owner = $match[3]; $this->repository = $match[4]; - $this->originUrl = !empty($match[1]) ? $match[1] : $match[2]; + $this->originUrl = strtolower(!empty($match[1]) ? $match[1] : $match[2]); if ($this->originUrl === 'www.github.com') { $this->originUrl = 'github.com'; } @@ -272,7 +272,7 @@ class GitHubDriver extends VcsDriver } $originUrl = !empty($matches[2]) ? $matches[2] : $matches[3]; - if (!in_array(preg_replace('{^www\.}i', '', $originUrl), $config->get('github-domains'))) { + if (!in_array(strtolower(preg_replace('{^www\.}i', '', $originUrl)), $config->get('github-domains'))) { return false; } diff --git a/src/Composer/Repository/Vcs/GitLabDriver.php b/src/Composer/Repository/Vcs/GitLabDriver.php index 0d8925bc8..e346b0306 100644 --- a/src/Composer/Repository/Vcs/GitLabDriver.php +++ b/src/Composer/Repository/Vcs/GitLabDriver.php @@ -494,6 +494,8 @@ class GitLabDriver extends VcsDriver */ private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts, $portNumber) { + $guessedDomain = strtolower($guessedDomain); + if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array($guessedDomain.':'.$portNumber, $configuredDomains))) { if ($portNumber) { return $guessedDomain.':'.$portNumber; diff --git a/src/Composer/Repository/Vcs/HgBitbucketDriver.php b/src/Composer/Repository/Vcs/HgBitbucketDriver.php index 8324f22ac..1cf630da9 100644 --- a/src/Composer/Repository/Vcs/HgBitbucketDriver.php +++ b/src/Composer/Repository/Vcs/HgBitbucketDriver.php @@ -53,7 +53,7 @@ class HgBitbucketDriver extends BitbucketDriver */ public static function supports(IOInterface $io, Config $config, $url, $deep = false) { - if (!preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#', $url)) { + if (!preg_match('#^https?://bitbucket\.org/([^/]+)/([^/]+)/?$#i', $url)) { return false; } From 5b4fad9056b01bd17bf241fa794e5dcb4150b7ea Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 6 Oct 2019 21:39:10 +0200 Subject: [PATCH 18/63] Update target version for master branch --- composer.json | 2 +- src/Composer/Composer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 0878f5b65..da7e2e8cd 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.10-dev" } }, "autoload": { diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 0d5faceb2..99f4756b0 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -53,7 +53,7 @@ class Composer const VERSION = '@package_version@'; const BRANCH_ALIAS_VERSION = '@package_branch_alias_version@'; const RELEASE_DATE = '@release_date@'; - const SOURCE_VERSION = '1.9-dev+source'; + const SOURCE_VERSION = '1.10-dev+source'; public static function getVersion() { From f753c15664c24d95bb64bd99ddc32cb01d1e32b8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 6 Oct 2019 22:13:14 +0200 Subject: [PATCH 19/63] Update deps --- composer.lock | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/composer.lock b/composer.lock index ca55827ee..7fd5777b7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0367be765bb2ea718da11dbb9b3ed793", + "content-hash": "617f7e5e1a6d8429c246415ec685bd90", "packages": [ { "name": "composer/ca-bundle", - "version": "1.2.3", + "version": "1.2.4", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "f26a67e397be0e5c00d7c52ec7b5010098e15ce5" + "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/f26a67e397be0e5c00d7c52ec7b5010098e15ce5", - "reference": "f26a67e397be0e5c00d7c52ec7b5010098e15ce5", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/10bb96592168a0f8e8f6dcde3532d9fa50b0b527", + "reference": "10bb96592168a0f8e8f6dcde3532d9fa50b0b527", "shasum": "" }, "require": { @@ -60,7 +60,7 @@ "ssl", "tls" ], - "time": "2019-08-02T09:05:43+00:00" + "time": "2019-08-30T08:44:50+00:00" }, { "name": "composer/semver", @@ -653,16 +653,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "82ebae02209c21113908c229e9883c419720738a" + "reference": "550ebaac289296ce228a706d0867afc34687e3f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/82ebae02209c21113908c229e9883c419720738a", - "reference": "82ebae02209c21113908c229e9883c419720738a", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4", + "reference": "550ebaac289296ce228a706d0867afc34687e3f4", "shasum": "" }, "require": { @@ -674,7 +674,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -690,13 +690,13 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -707,20 +707,20 @@ "polyfill", "portable" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609" + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fe5e94c604826c35a32fa832f35bd036b6799609", - "reference": "fe5e94c604826c35a32fa832f35bd036b6799609", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17", + "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17", "shasum": "" }, "require": { @@ -732,7 +732,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11-dev" + "dev-master": "1.12-dev" } }, "autoload": { @@ -766,7 +766,7 @@ "portable", "shim" ], - "time": "2019-02-06T07:57:58+00:00" + "time": "2019-08-06T08:03:45+00:00" }, { "name": "symfony/process", @@ -924,22 +924,22 @@ }, { "name": "phpspec/prophecy", - "version": "1.8.1", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", + "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, @@ -983,7 +983,7 @@ "spy", "stub" ], - "time": "2019-06-13T12:50:23+00:00" + "time": "2019-10-03T11:07:50+00:00" }, { "name": "phpunit/php-code-coverage", From 0d1d35c346ca2639c8c47bdcc277272c3cf8177e Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Fri, 4 Oct 2019 16:38:22 +0100 Subject: [PATCH 20/63] Debug: display used authentication for http calls --- src/Composer/Util/RemoteFilesystem.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index b3f51aae5..6e4b1b557 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -47,6 +47,7 @@ class RemoteFilesystem private $degradedMode = false; private $redirects; private $maxRedirects = 20; + private $displayedOriginAuthentications = array(); /** * Constructor. @@ -814,24 +815,35 @@ class RemoteFilesystem } if ($this->io->hasAuthentication($originUrl)) { + $authenticationDisplayMessage = null; $auth = $this->io->getAuthentication($originUrl); if ('github.com' === $originUrl && 'x-oauth-basic' === $auth['password']) { $options['github-token'] = $auth['username']; + $authenticationDisplayMessage = 'Using GitHub token authentication'; } elseif ($this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)) { if ($auth['password'] === 'oauth2') { $headers[] = 'Authorization: Bearer '.$auth['username']; + $authenticationDisplayMessage = 'Using GitLab OAuth token authentication'; } elseif ($auth['password'] === 'private-token') { $headers[] = 'PRIVATE-TOKEN: '.$auth['username']; + $authenticationDisplayMessage = 'Using GitLab private token authentication'; } } elseif ('bitbucket.org' === $originUrl && $this->fileUrl !== Bitbucket::OAUTH2_ACCESS_TOKEN_URL && 'x-token-auth' === $auth['username'] ) { if (!$this->isPublicBitBucketDownload($this->fileUrl)) { $headers[] = 'Authorization: Bearer ' . $auth['password']; + $authenticationDisplayMessage = 'Using Bitbucket OAuth token authentication'; } } else { $authStr = base64_encode($auth['username'] . ':' . $auth['password']); $headers[] = 'Authorization: Basic '.$authStr; + $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . $auth['username'] . '"'; + } + + if ($authenticationDisplayMessage && !in_array($originUrl, $this->displayedOriginAuthentications, true)) { + $this->io->writeError($authenticationDisplayMessage, true, IOInterface::DEBUG); + $this->displayedOriginAuthentications[] = $originUrl; } } From 146aa4938f25e10831f3d5fe35388739390ba383 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Mon, 7 Oct 2019 18:50:18 +0200 Subject: [PATCH 21/63] 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 18895064ad12bbe165221029f166eff368e4e833 Mon Sep 17 00:00:00 2001 From: Mike van Rooyen Date: Tue, 8 Oct 2019 15:46:35 +0100 Subject: [PATCH 22/63] Check that if the getUrlMatches method returns an empty value which means the path is incorrect --- src/Composer/Repository/PathRepository.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index 6e218f159..d5d21b609 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -125,7 +125,13 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn { parent::initialize(); - foreach ($this->getUrlMatches() as $url) { + $urlMatches = $this->getUrlMatches(); + + if (empty($urlMatches)) { + throw new \RuntimeException('The `url` supplied for the path repository does not exist'); + } + + foreach ($urlMatches as $url) { $path = realpath($url) . DIRECTORY_SEPARATOR; $composerFilePath = $path.'composer.json'; From e62478ab896115d5014e0f5eba6e571c723b789e Mon Sep 17 00:00:00 2001 From: Mike van Rooyen Date: Tue, 8 Oct 2019 15:48:04 +0100 Subject: [PATCH 23/63] Test to check there is a RuntimeException thrown when a path repository doesn't exist --- .../Test/Repository/PathRepositoryTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php index a9594257c..fde704066 100644 --- a/tests/Composer/Test/Repository/PathRepositoryTest.php +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -19,6 +19,24 @@ use Composer\Test\TestCase; class PathRepositoryTest extends TestCase { + + /** + * @expectedException RuntimeException + */ + public function testLoadPackageFromFileSystemWithIncorrectPath() + { + $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') + ->getMock(); + + $config = new \Composer\Config(); + $versionGuesser = null; + + $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', 'missing')); + $repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config); + $repository->getPackages(); + + } + public function testLoadPackageFromFileSystemWithVersion() { $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') From e910e06f6333d409e5379f7baa3a80e6cf5680b8 Mon Sep 17 00:00:00 2001 From: Mike van Rooyen Date: Tue, 8 Oct 2019 16:02:03 +0100 Subject: [PATCH 24/63] Add details of the path to aid debugging --- src/Composer/Repository/PathRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index d5d21b609..2f26af2f7 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -128,7 +128,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn $urlMatches = $this->getUrlMatches(); if (empty($urlMatches)) { - throw new \RuntimeException('The `url` supplied for the path repository does not exist'); + throw new \RuntimeException('The `url` supplied for the path (' . $this->url . ') repository does not exist'); } foreach ($urlMatches as $url) { From daedb4a74fd253b3b30a8c9e5eab50085f8279f9 Mon Sep 17 00:00:00 2001 From: Mike van Rooyen Date: Tue, 8 Oct 2019 16:08:11 +0100 Subject: [PATCH 25/63] Remove extra line in method following CS-Fixer --- tests/Composer/Test/Repository/PathRepositoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php index fde704066..c02c02838 100644 --- a/tests/Composer/Test/Repository/PathRepositoryTest.php +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -34,7 +34,6 @@ class PathRepositoryTest extends TestCase $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', 'missing')); $repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config); $repository->getPackages(); - } public function testLoadPackageFromFileSystemWithVersion() From 6b56ddae2a2c2eedafc378a3bcae0c5cb06ee843 Mon Sep 17 00:00:00 2001 From: Mike van Rooyen Date: Tue, 8 Oct 2019 20:49:44 +0100 Subject: [PATCH 26/63] Remove unused variable --- tests/Composer/Test/Repository/PathRepositoryTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php index c02c02838..159d8cc97 100644 --- a/tests/Composer/Test/Repository/PathRepositoryTest.php +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -29,7 +29,6 @@ class PathRepositoryTest extends TestCase ->getMock(); $config = new \Composer\Config(); - $versionGuesser = null; $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', 'missing')); $repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config); From 14b3f09cf8ac049d0cfa91c6dbd834d0caf84f79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=ADas=20Donoso?= Date: Tue, 15 Oct 2019 00:19:57 -0300 Subject: [PATCH 27/63] added package homepage information to the command 'show' --- src/Composer/Command/ShowCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index e9061743f..7599765b2 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -579,6 +579,7 @@ EOT { $io = $this->getIO(); $io->write('name : ' . $package->getPrettyName()); + $io->write('homepage : ' . $package->getHomepage()); $io->write('descrip. : ' . $package->getDescription()); $io->write('keywords : ' . implode(', ', $package->getKeywords() ?: array())); $this->printVersions($package, $versions, $installedRepo); From 07ec3bda3e7ac8eeff9dd698c0269f20b1fa8b99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Math=C3=ADas=20Donoso?= Date: Tue, 15 Oct 2019 00:38:24 -0300 Subject: [PATCH 28/63] changed homepage information position --- src/Composer/Command/ShowCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 7599765b2..4152dc9d6 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -579,7 +579,6 @@ EOT { $io = $this->getIO(); $io->write('name : ' . $package->getPrettyName()); - $io->write('homepage : ' . $package->getHomepage()); $io->write('descrip. : ' . $package->getDescription()); $io->write('keywords : ' . implode(', ', $package->getKeywords() ?: array())); $this->printVersions($package, $versions, $installedRepo); @@ -591,6 +590,7 @@ EOT } $io->write('type : ' . $package->getType()); $this->printLicenses($package); + $io->write('homepage : ' . $package->getHomepage()); $io->write('source : ' . sprintf('[%s] %s %s', $package->getSourceType(), $package->getSourceUrl(), $package->getSourceReference())); $io->write('dist : ' . sprintf('[%s] %s %s', $package->getDistType(), $package->getDistUrl(), $package->getDistReference())); if ($installedRepo->hasPackage($package)) { From 20eb9e66cf4b156a8459484e9cc2aa579fcf9052 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Tue, 15 Oct 2019 08:25:00 +0200 Subject: [PATCH 29/63] 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 30/63] 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 4c8e41d9a922005360bad3889ac3fdcc350c2195 Mon Sep 17 00:00:00 2001 From: Tyson Andre Date: Tue, 15 Oct 2019 21:21:25 -0400 Subject: [PATCH 31/63] Fix misc phpdoc and strpos arg order nits https://www.php.net/strpos has the signature `strpos ( string $haystack , mixed $needle [, int $offset = 0 ] ) : int` (The needle is usually the constant) `strpos('/', $suggestion)` would only be `false` for `''` and `'/'` So the existing check would just not suggest **anything** that was already installed (from pecl, built-in, or composer). The intent seems to be to not suggest non-vendored php packages that were already installed. (b20cc22ebb1d9f8ed467f0ea353391be7442ad3d) --- src/Composer/Command/SuggestsCommand.php | 2 +- src/Composer/Package/BasePackage.php | 2 +- src/Composer/Util/Zip.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index a200f8f69..13f742023 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -90,7 +90,7 @@ EOT continue; } foreach ($package['suggest'] as $suggestion => $reason) { - if (false === strpos('/', $suggestion) && null !== $platform->findPackage($suggestion, '*')) { + if (null !== $platform->findPackage($suggestion, '*')) { continue; } if (!isset($installed[$suggestion])) { diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index f2f5be707..9630e7ef0 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -239,7 +239,7 @@ abstract class BasePackage implements PackageInterface * Build a regexp from a package name, expanding * globs as required * * @param string $whiteListedPattern - * @param bool $wrap Wrap the cleaned string by the given string + * @param string $wrap Wrap the cleaned string by the given string * @return string */ public static function packageNameToRegexp($whiteListedPattern, $wrap = '{^%s$}i') diff --git a/src/Composer/Util/Zip.php b/src/Composer/Util/Zip.php index 8c79d106c..ab10d5bbf 100644 --- a/src/Composer/Util/Zip.php +++ b/src/Composer/Util/Zip.php @@ -21,7 +21,6 @@ class Zip * Gets content of the root composer.json inside a ZIP archive. * * @param string $pathToZip - * @param string $filename * * @return string|null */ From 5f202efa0bcf8f51f13ab073a09b42ebfb6c8f26 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Wed, 16 Oct 2019 13:36:28 +0200 Subject: [PATCH 32/63] 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 7c5e5e3ede952486010defdb1a938202788847b6 Mon Sep 17 00:00:00 2001 From: Arnout Boks Date: Sat, 19 Oct 2019 21:46:29 +0200 Subject: [PATCH 33/63] Add option to disable the lock file When the `lock` option is set to false, composer will not write a `composer.lock` file to disk. This signals that the package is meant to be developed with unlocked and always updated dependencies. At the moment, both `install` and `update` are allowed to install the dependencies for such a package. If #6822 is implemented, only `update` should be used for packages without a lockfile. https://github.com/composer/composer/issues/8354 --- doc/06-config.md | 5 +++++ res/composer-schema.json | 4 ++++ src/Composer/Command/ConfigCommand.php | 1 + src/Composer/Config.php | 3 +++ src/Composer/Installer.php | 4 +++- 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/doc/06-config.md b/doc/06-config.md index f3afc4eb1..6ffbea411 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -296,4 +296,9 @@ Example: Defaults to `true`. If set to `false`, Composer will not create `.htaccess` files in the composer home, cache, and data directories. +## lock + +Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` +file. + ← [Repositories](05-repositories.md) | [Community](07-community.md) → diff --git a/res/composer-schema.json b/res/composer-schema.json index cb3594f7b..a45574f77 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -290,6 +290,10 @@ "sort-packages": { "type": "boolean", "description": "Defaults to false. If set to true, Composer will sort packages when adding/updating a new dependency." + }, + "lock": { + "type": "boolean", + "description": "Defaults to true. If set to false, Composer will not create a composer.lock file." } } }, diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 71ec9d816..24e2bd3c3 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -412,6 +412,7 @@ EOT ), 'github-expose-hostname' => array($booleanValidator, $booleanNormalizer), 'htaccess-protect' => array($booleanValidator, $booleanNormalizer), + 'lock' => array($booleanValidator, $booleanNormalizer), ); $multiConfigValues = array( 'github-protocols' => array( diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 7abca7dfa..1050096b1 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -63,6 +63,7 @@ class Config 'archive-dir' => '.', 'htaccess-protect' => true, 'use-github-api' => true, + 'lock' => true, // valid keys without defaults (auth config stuff): // bitbucket-oauth // github-oauth @@ -329,6 +330,8 @@ class Config return $this->config[$key] !== 'false' && (bool) $this->config[$key]; case 'use-github-api': return $this->config[$key] !== 'false' && (bool) $this->config[$key]; + case 'lock': + return $this->config[$key] !== 'false' && (bool) $this->config[$key]; default: if (!isset($this->config[$key])) { return null; diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 11edcbe72..d688a4f1f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -118,7 +118,7 @@ class Installer protected $preferStable = false; protected $preferLowest = false; protected $skipSuggest = false; - protected $writeLock = true; + protected $writeLock; protected $executeOperations = true; /** @@ -164,6 +164,8 @@ class Installer $this->installationManager = $installationManager; $this->eventDispatcher = $eventDispatcher; $this->autoloadGenerator = $autoloadGenerator; + + $this->writeLock = $config->get('lock'); } /** From 22caa0f097a873db2abbbc90ce05c284688f0b47 Mon Sep 17 00:00:00 2001 From: Arnout Boks Date: Sun, 20 Oct 2019 10:51:59 +0200 Subject: [PATCH 34/63] Add tests for installer with lock: false https://github.com/composer/composer/issues/8354 --- .../installer/install-without-lock.test | 25 +++++++++++++++++++ .../installer/update-without-lock.test | 25 +++++++++++++++++++ tests/Composer/Test/InstallerTest.php | 9 ++++++- 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Fixtures/installer/install-without-lock.test create mode 100644 tests/Composer/Test/Fixtures/installer/update-without-lock.test diff --git a/tests/Composer/Test/Fixtures/installer/install-without-lock.test b/tests/Composer/Test/Fixtures/installer/install-without-lock.test new file mode 100644 index 000000000..c5d73dcbb --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-without-lock.test @@ -0,0 +1,25 @@ +--TEST-- +Installs from composer.json without writing a lock file +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.0" + }, + "config": { + "lock": "false" + } +} +--RUN-- +install +--EXPECT-- +Installing a/a (1.0.0) +--EXPECT-LOCK-- +false diff --git a/tests/Composer/Test/Fixtures/installer/update-without-lock.test b/tests/Composer/Test/Fixtures/installer/update-without-lock.test new file mode 100644 index 000000000..0fd1562c3 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-without-lock.test @@ -0,0 +1,25 @@ +--TEST-- +Updates when no lock file is present without writing a lock file +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.0" + }, + "config": { + "lock": false + } +} +--RUN-- +update +--EXPECT-- +Installing a/a (1.0.0) +--EXPECT-LOCK-- +false diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index c3b957afb..fa093a460 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -197,6 +197,9 @@ class InstallerTest extends TestCase // so store value temporarily in reference for later assetion $actualLock = $hash; })); + } elseif ($expectLock === false) { + $lockJsonMock->expects($this->never()) + ->method('write'); } $contents = json_encode($composerConfig); @@ -319,7 +322,11 @@ class InstallerTest extends TestCase } $run = $testData['RUN']; if (!empty($testData['EXPECT-LOCK'])) { - $expectLock = JsonFile::parseJson($testData['EXPECT-LOCK']); + if ($testData['EXPECT-LOCK'] === 'false') { + $expectLock = false; + } else { + $expectLock = JsonFile::parseJson($testData['EXPECT-LOCK']); + } } $expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null; $expect = $testData['EXPECT']; From c2f1a6b643b09fc5a095e4c75f437370165cf1e5 Mon Sep 17 00:00:00 2001 From: Mikhail Fesenko Date: Thu, 17 Oct 2019 13:17:13 +0300 Subject: [PATCH 35/63] 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 5687ac7de708aa47f253f4718dba0760689b6597 Mon Sep 17 00:00:00 2001 From: lasseeee Date: Sun, 20 Oct 2019 07:20:45 +1300 Subject: [PATCH 36/63] Don't necessarily mention Google There's other search engines as well. --- doc/00-intro.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index ed7a17c7c..067e89061 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -106,7 +106,8 @@ Linux distributions. > `mkdir -p /usr/local/bin`. > **Note:** For information on changing your PATH, please read the -> [Wikipedia article](https://en.wikipedia.org/wiki/PATH_(variable)) and/or use Google. +> [Wikipedia article](https://en.wikipedia.org/wiki/PATH_(variable)) and/or use +> your search engine of choice. Now run `composer` in order to run Composer instead of `php composer.phar`. @@ -139,7 +140,7 @@ C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat Add the directory to your PATH environment variable if it isn't already. For information on changing your PATH variable, please see [this article](https://www.computerhope.com/issues/ch000549.htm) and/or -use Google. +use your search engine of choice. Close your current terminal. Test usage with a new terminal: From 8d9b822413c87eed8b7f7bb67285ba9c9c482d19 Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Tue, 8 Oct 2019 18:12:56 +0100 Subject: [PATCH 37/63] 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 38/63] 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 45d616c447ab3626bd93d304a8a78e7e2938f6b7 Mon Sep 17 00:00:00 2001 From: John Stevenson Date: Tue, 8 Oct 2019 14:46:37 +0100 Subject: [PATCH 39/63] Add Windows proc-open errors to troubleshooting.md As per these issues: https://github.com/composer/composer/issues/7186 https://github.com/composer/composer/issues/8152 --- doc/articles/troubleshooting.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/articles/troubleshooting.md b/doc/articles/troubleshooting.md index d0a64506f..7a5725ec4 100644 --- a/doc/articles/troubleshooting.md +++ b/doc/articles/troubleshooting.md @@ -211,6 +211,19 @@ To enable the swap you can use for example: ``` You can make a permanent swap file following this [tutorial](https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04). +## proc_open(): failed to open stream errors (Windows) + +If composer shows proc_open(NUL) errors on Windows: + +`proc_open(NUL): failed to open stream: No such file or directory` + +This could be happening because you are working in a _OneDrive_ directory and +using a version of PHP that does not support the file system semantics of this +service. The issue was fixed in PHP 7.2.23 and 7.3.10. + +Alternatively it could be because the Windows Null Service is not enabled. For +more information, see this [issue](https://github.com/composer/composer/issues/7186#issuecomment-373134916). + ## Degraded Mode Due to some intermittent issues on Travis and other systems, we introduced a 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 40/63] 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')) { From d73cef3fb4d03da5fc846604caa629bf684da79c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 24 Oct 2019 11:16:42 +0200 Subject: [PATCH 41/63] Avoid calling findPackage for non-platform packages --- src/Composer/Command/SuggestsCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index 13f742023..a7ec3dad8 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -90,7 +90,7 @@ EOT continue; } foreach ($package['suggest'] as $suggestion => $reason) { - if (null !== $platform->findPackage($suggestion, '*')) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $suggestion) && null !== $platform->findPackage($suggestion, '*')) { continue; } if (!isset($installed[$suggestion])) { From 8d92048c8c6a1fe19484fd91631e716aa002f194 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 24 Oct 2019 15:20:14 +0200 Subject: [PATCH 42/63] Update safeguard code, fixes #8383 --- src/Composer/DependencyResolver/Decisions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Decisions.php b/src/Composer/DependencyResolver/Decisions.php index 86b62c3d3..abd1a110b 100644 --- a/src/Composer/DependencyResolver/Decisions.php +++ b/src/Composer/DependencyResolver/Decisions.php @@ -183,7 +183,7 @@ class Decisions implements \Iterator, \Countable $previousDecision = isset($this->decisionMap[$packageId]) ? $this->decisionMap[$packageId] : null; if ($previousDecision != 0) { - $literalString = $this->pool->literalToString($literal); + $literalString = $this->pool->literalToPrettyString($literal, []); $package = $this->pool->literalToPackage($literal); throw new SolverBugException( "Trying to decide $literalString on level $level, even though $package was previously decided as ".(int) $previousDecision."." From 46657a8a7f7f8f606466fe5cc9b853f650e716b0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 24 Oct 2019 15:26:55 +0200 Subject: [PATCH 43/63] 5.3 support :/ --- src/Composer/DependencyResolver/Decisions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/DependencyResolver/Decisions.php b/src/Composer/DependencyResolver/Decisions.php index abd1a110b..e2773501f 100644 --- a/src/Composer/DependencyResolver/Decisions.php +++ b/src/Composer/DependencyResolver/Decisions.php @@ -183,7 +183,7 @@ class Decisions implements \Iterator, \Countable $previousDecision = isset($this->decisionMap[$packageId]) ? $this->decisionMap[$packageId] : null; if ($previousDecision != 0) { - $literalString = $this->pool->literalToPrettyString($literal, []); + $literalString = $this->pool->literalToPrettyString($literal, array()); $package = $this->pool->literalToPackage($literal); throw new SolverBugException( "Trying to decide $literalString on level $level, even though $package was previously decided as ".(int) $previousDecision."." From 4fc647983762c0b7b372b06b83a988b3b4ab90ea Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 25 Oct 2019 14:08:30 +0200 Subject: [PATCH 44/63] Fix require command to allow working on network mounts, fixes #8231 --- src/Composer/Command/RequireCommand.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 8f91b6675..1fa280d3f 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -99,7 +99,9 @@ EOT return 1; } - if (!is_readable($this->file)) { + // check for readability by reading the file as is_readable can not be trusted on network-mounts + // see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926 + if (!is_readable($this->file) && false === Silencer::call('file_get_contents', $this->file)) { $io->writeError(''.$this->file.' is not readable.'); return 1; From 8cbc595790c1f8713efbdfb3825a34b3db1a12f9 Mon Sep 17 00:00:00 2001 From: Andreas Schempp Date: Mon, 5 Aug 2019 11:13:48 +0200 Subject: [PATCH 45/63] Validate schema name, type and version --- res/composer-schema.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index cb3594f7b..b4e9d65af 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -11,7 +11,8 @@ }, "type": { "description": "Package type, either 'library' for common packages, 'composer-plugin' for plugins, 'metapackage' for empty packages, or a custom type ([a-z0-9-]+) defined by whatever project this package applies to.", - "type": "string" + "type": "string", + "pattern": "^[a-z0-9-]+$" }, "target-dir": { "description": "DEPRECATED: Forces the package to be installed into the given subdirectory path. This is used for autoloading PSR-0 packages that do not contain their full path. Use forward slashes for cross-platform compatibility.", @@ -39,7 +40,8 @@ }, "version": { "type": "string", - "description": "Package version, see https://getcomposer.org/doc/04-schema.md#version for more info on valid schemes." + "description": "Package version, see https://getcomposer.org/doc/04-schema.md#version for more info on valid schemes.", + "pattern": "^v?\\d+(((\\.\\d+)?\\.\\d+)?\\.\\d+)?" }, "time": { "type": "string", From e47aa38ad40b31964d2951cb5be9f19fe9959ba0 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 29 Oct 2019 14:08:33 +0100 Subject: [PATCH 46/63] Added phpdoc for ComposerAutoloaderInit$SHA1::getLoader() (#8393) --- src/Composer/Autoload/AutoloadGenerator.php | 3 +++ .../Autoload/Fixtures/autoload_real_files_by_dependency.php | 3 +++ .../Test/Autoload/Fixtures/autoload_real_functions.php | 3 +++ .../Fixtures/autoload_real_functions_with_include_paths.php | 3 +++ ..._functions_with_removed_include_paths_and_autolad_files.php | 3 +++ .../Test/Autoload/Fixtures/autoload_real_include_path.php | 3 +++ .../Test/Autoload/Fixtures/autoload_real_target_dir.php | 3 +++ 7 files changed, 21 insertions(+) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index d970ca5b1..d3bc7bf90 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -592,6 +592,9 @@ class ComposerAutoloaderInit$suffix } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::\$loader) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php index e830e21f8..4e5e0a5cb 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_files_by_dependency.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInitFilesAutoloadOrder } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index ede883fd7..0f1201b8e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInitFilesAutoload } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php index 6459cac05..fc457c406 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_include_paths.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInitFilesAutoload } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php index 75306684a..b07825176 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions_with_removed_include_paths_and_autolad_files.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInitFilesAutoload } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php index 32f822812..12ac24108 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInitIncludePath } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php index 1d6676c83..084d04f30 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -13,6 +13,9 @@ class ComposerAutoloaderInitTargetDir } } + /** + * @return \Composer\Autoload\ClassLoader + */ public static function getLoader() { if (null !== self::$loader) { From 99e23d52631aada48bc645fe169246e7b06d68c2 Mon Sep 17 00:00:00 2001 From: MichaelKo Date: Tue, 29 Oct 2019 10:38:24 +0100 Subject: [PATCH 47/63] Don't show root warning for docker containers Signed-off-by: Viacheslav Sychov --- src/Composer/Console/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 171f4fc5b..e45ea8027 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -212,7 +212,7 @@ class Application extends BaseApplication $io->writeError(sprintf('Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); } - if (!Platform::isWindows() && function_exists('exec') && !getenv('COMPOSER_ALLOW_SUPERUSER')) { + if (!Platform::isWindows() && function_exists('exec') && !getenv('COMPOSER_ALLOW_SUPERUSER') && !file_exists('/.dockerenv')) { if (function_exists('posix_getuid') && posix_getuid() === 0) { if ($commandName !== 'self-update' && $commandName !== 'selfupdate') { $io->writeError('Do not run Composer as root/super user! See https://getcomposer.org/root for details'); From 82d939d7f78c7920511b4336276fb5501557e678 Mon Sep 17 00:00:00 2001 From: Armando Miani Date: Tue, 29 Oct 2019 15:06:07 +0100 Subject: [PATCH 48/63] Validate composer show with --tree and --path options set (#8390) --- src/Composer/Command/ShowCommand.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 4152dc9d6..441fe6818 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -129,6 +129,12 @@ EOT return 1; } + if ($input->getOption('tree') && $input->getOption('path')) { + $io->writeError('The --tree (-t) option is not usable in combination with --path (-P)'); + + return 1; + } + $format = $input->getOption('format'); if (!in_array($format, array('text', 'json'))) { $io->writeError(sprintf('Unsupported format "%s". See help for supported formats.', $format)); From ec293adabc8e58f614e1092486a4b28fe61b7d09 Mon Sep 17 00:00:00 2001 From: Andriy Maletsky Date: Tue, 29 Oct 2019 20:18:48 +0200 Subject: [PATCH 49/63] make optimized autoloader respect PSR standards --- src/Composer/Autoload/AutoloadGenerator.php | 15 ++-- src/Composer/Autoload/ClassMapGenerator.php | 78 +++++++++++++++++-- .../Test/Autoload/AutoloadGeneratorTest.php | 39 ++++++++++ 3 files changed, 118 insertions(+), 14 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index d970ca5b1..f0872fb7e 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -256,15 +256,14 @@ EOF; continue; } - $namespaceFilter = $namespace === '' ? null : $namespace; - $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $classMap); + $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap); } } } } foreach ($autoloads['classmap'] as $dir) { - $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, $classMap); + $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap); } ksort($classMap); @@ -317,9 +316,9 @@ EOF; return count($classMap); } - private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, array $classMap = array()) + private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array()) { - foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter) as $class => $path) { + foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) { $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n"; if (!isset($classMap[$class])) { $classMap[$class] = $pathCode; @@ -334,9 +333,9 @@ EOF; return $classMap; } - private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null, $showAmbiguousWarning = true) + private function generateClassMap($dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, $showAmbiguousWarning = true) { - return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter); + return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType); } public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages) @@ -447,7 +446,7 @@ EOF; foreach ($autoloads['classmap'] as $dir) { try { - $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, false)); + $loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false)); } catch (\RuntimeException $e) { $this->io->writeError(''.$e->getMessage().''); } diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 8467c742e..be8bcdb11 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -50,17 +50,19 @@ class ClassMapGenerator /** * Iterate over all files in the given directory searching for classes * - * @param \Iterator|string $path The path to search in or an iterator - * @param string $blacklist Regex that matches against the file path that exclude from the classmap. - * @param IOInterface $io IO object - * @param string $namespace Optional namespace prefix to filter by + * @param \Iterator|string $path The path to search in or an iterator + * @param string $blacklist Regex that matches against the file path that exclude from the classmap. + * @param IOInterface $io IO object + * @param string $namespace Optional namespace prefix to filter by + * @param string $autoloadType psr-0|psr-4 Optional autoload standard to use mapping rules * * @throws \RuntimeException When the path is neither an existing file nor directory * @return array A class map array */ - public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null) + public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null, $autoloadType = null) { if (is_string($path)) { + $basePath = $path; if (is_file($path)) { $path = array(new \SplFileInfo($path)); } elseif (is_dir($path)) { @@ -71,6 +73,8 @@ class ClassMapGenerator '" which does not appear to be a file nor a folder' ); } + } elseif (null !== $autoloadType) { + throw new \RuntimeException('Path must be a string when specifying an autoload type'); } $map = array(); @@ -100,10 +104,13 @@ class ClassMapGenerator } $classes = self::findClasses($filePath); + if (null !== $autoloadType) { + $classes = self::filterByNamespace($classes, $filePath, $namespace, $autoloadType, $basePath, $io); + } foreach ($classes as $class) { // skip classes not within the given namespace prefix - if (null !== $namespace && 0 !== strpos($class, $namespace)) { + if (null === $autoloadType && null !== $namespace && 0 !== strpos($class, $namespace)) { continue; } @@ -121,6 +128,65 @@ class ClassMapGenerator return $map; } + /** + * Remove classes which could not have been loaded by namespace autoloaders + * + * @param array $classes found classes in given file + * @param string $filePath current file + * @param string $baseNamespace prefix of given autoload mapping + * @param string $namespaceType psr-0|psr-4 + * @param string $basePath root directory of given autoload mapping + * @param IOInterface $io IO object + * @return array valid classes + */ + private static function filterByNamespace($classes, $filePath, $baseNamespace, $namespaceType, $basePath, $io) + { + $validClasses = array(); + $rejectedClasses = array(); + foreach ($classes as $class) { + // silently skip if ns doesn't have common root + if ('' !== $baseNamespace && 0 !== strpos($class, $baseNamespace)) { + continue; + } + // transform class name to file path and validate + if ('psr-0' === $namespaceType) { + $namespaceLength = strrpos($class, '\\'); + if (false !== $namespaceLength) { + $namespace = substr($class, 0, $namespaceLength + 1); + $className = substr($class, $namespaceLength + 1); + $subPath = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) + . str_replace('_', DIRECTORY_SEPARATOR, $className); + } + else { + $subPath = str_replace('_', DIRECTORY_SEPARATOR, $class); + } + } elseif ('psr-4' === $namespaceType) { + $subNamespace = ('' !== $baseNamespace) ? substr($class, strlen($baseNamespace)) : $class; + $subPath = str_replace('\\', DIRECTORY_SEPARATOR, $subNamespace); + } else { + throw new \RuntimeException("namespaceType must be psr-0 or psr-4, $namespaceType given"); + } + $realSubPath = substr($filePath, strlen($basePath) + 1); + $realSubPath = substr($realSubPath, 0, strrpos($realSubPath, '.')); + if ($subPath === $realSubPath) { + $validClasses[] = $class; + } else { + $rejectedClasses[] = $class; + } + } + // warn only if no valid classes, else silently skip invalid + if (!empty($validClasses)) { + return $validClasses; + } + if ($io) { + foreach ($rejectedClasses as $class) { + $io->writeError("Warning: class $class located in $filePath " + . "doesn't comply with $namespaceType autoloading standard. Skipping."); + } + } + return array(); + } + /** * Extract the classes in the given file * diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 84ac16df7..86d8bb247 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -548,6 +548,45 @@ class AutoloadGeneratorTest extends TestCase ); } + public function testPSRToClassMapIgnoresNonPSRClasses() + { + $package = new Package('a', '1.0', '1.0'); + + $package->setAutoload(array( + 'psr-0' => array('psr0_' => 'psr0/'), + 'psr-4' => array('psr4\\' => 'psr4/'), + )); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue(array())); + + $this->fs->ensureDirectoryExists($this->workingDir.'/psr0/psr0'); + $this->fs->ensureDirectoryExists($this->workingDir.'/psr4'); + file_put_contents($this->workingDir.'/psr0/psr0/match.php', 'workingDir.'/psr0/psr0/badfile.php', 'workingDir.'/psr4/match.php', 'workingDir.'/psr4/badfile.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); + $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated."); + $expectedClassmap = << \$baseDir . '/psr0/psr0/match.php', + 'psr4\\\\match' => \$baseDir . '/psr4/match.php', +); + +EOF; + $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_classmap.php', $expectedClassmap); + } + public function testVendorsClassMapAutoloading() { $package = new Package('a', '1.0', '1.0'); From 6a335a459cd0c244813ff98251c4da6e6eb8c089 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 29 Oct 2019 23:08:03 +0100 Subject: [PATCH 50/63] Move test file parsing into try/catch block to avoid phpunit swallowing errors --- tests/Composer/Test/InstallerTest.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index c3b957afb..a5da19c05 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -280,15 +280,15 @@ class InstallerTest extends TestCase continue; } - $testData = $this->readTestFile($file, $fixturesDir); - - $installed = array(); - $installedDev = array(); - $lock = array(); - $expectLock = array(); - $expectResult = 0; - try { + $testData = $this->readTestFile($file, $fixturesDir); + + $installed = array(); + $installedDev = array(); + $lock = array(); + $expectLock = array(); + $expectResult = 0; + $message = $testData['TEST']; $condition = !empty($testData['CONDITION']) ? $testData['CONDITION'] : null; $composer = JsonFile::parseJson($testData['COMPOSER']); From ae9cc3db58f80ed2703ad6fd06ffa84405c745ef Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 30 Oct 2019 10:45:40 +0100 Subject: [PATCH 51/63] Avoid clearing the error output during removeDirectory execution, losing git error output, fixes #8351 --- src/Composer/Util/Git.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 20457d708..574856dc7 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -215,10 +215,12 @@ class Git } } + $errorMsg = $this->process->getErrorOutput(); if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } - $this->throwException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput(), $url); + + $this->throwException('Failed to execute ' . $command . "\n\n" . $errorMsg, $url); } } From b925d06861a045188f7fe1b2730d1e564e4ca6ac Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 30 Oct 2019 11:25:00 +0100 Subject: [PATCH 52/63] Fix gitlab support for basic-auth fallback from ssh URLs --- src/Composer/Util/Git.php | 12 ++++++++++-- src/Composer/Util/RemoteFilesystem.php | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 574856dc7..c30178001 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -153,7 +153,14 @@ class Git return; } } - } elseif (preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}', $url, $match)) { + } elseif ( + preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) + || preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}', $url, $match) + ) { + if ($match[1] === 'git') { + $match[1] = 'https'; + } + if (!$this->io->hasAuthentication($match[2])) { $gitLabUtil = new GitLab($this->io, $this->config, $this->process); $message = 'Cloning failed, enter your GitLab credentials to access private repos'; @@ -165,11 +172,12 @@ class Git if ($this->io->hasAuthentication($match[2])) { $auth = $this->io->getAuthentication($match[2]); - if($auth['password'] === 'private-token' || $auth['password'] === 'oauth2') { + if ($auth['password'] === 'private-token' || $auth['password'] === 'oauth2' || $auth['password'] === 'gitlab-ci-token') { $authUrl = $match[1] . '://' . rawurlencode($auth['password']) . ':' . rawurlencode($auth['username']) . '@' . $match[2] . '/' . $match[3]; // swap username and password } else { $authUrl = $match[1] . '://' . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[2] . '/' . $match[3]; } + $command = call_user_func($commandCallable, $authUrl); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index b3f51aae5..ed6d37241 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -689,7 +689,7 @@ class RemoteFilesystem $message = "\n".'Could not fetch '.$this->fileUrl.', enter your ' . $this->originUrl . ' credentials ' .($httpStatus === 401 ? 'to access private repos' : 'to go over the API rate limit'); $gitLabUtil = new GitLab($this->io, $this->config, null); - if ($this->io->hasAuthentication($this->originUrl) && ($auth = $this->io->getAuthentication($this->originUrl)) && $auth['password'] === 'private-token') { + if ($this->io->hasAuthentication($this->originUrl) && ($auth = $this->io->getAuthentication($this->originUrl)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token'), true)) { throw new TransportException("Invalid credentials for '" . $this->fileUrl . "', aborting.", $httpStatus); } @@ -820,7 +820,7 @@ class RemoteFilesystem } elseif ($this->config && in_array($originUrl, $this->config->get('gitlab-domains'), true)) { if ($auth['password'] === 'oauth2') { $headers[] = 'Authorization: Bearer '.$auth['username']; - } elseif ($auth['password'] === 'private-token') { + } elseif ($auth['password'] === 'private-token' || $auth['password'] === 'gitlab-ci-token') { $headers[] = 'PRIVATE-TOKEN: '.$auth['username']; } } elseif ('bitbucket.org' === $originUrl From 12184aa9c51d5339c0b8c8a53c553794549f78d7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 30 Oct 2019 12:01:23 +0100 Subject: [PATCH 53/63] Fix github auth to try https with pwd also, fixes #8356 --- src/Composer/Util/Git.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index c30178001..73e3139ce 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -95,8 +95,10 @@ class Git $auth = null; if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { - // private github repository without git access, try https with auth - if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match)) { + // private github repository without ssh key access, try https with auth + if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) + || preg_match('{^(https?)://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match) + ) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; @@ -183,7 +185,7 @@ class Git return; } } - } elseif ($this->isAuthenticationFailure($url, $match)) { // private non-github repo that failed to authenticate + } elseif ($this->isAuthenticationFailure($url, $match)) { // private non-github/gitlab/bitbucket repo that failed to authenticate if (strpos($match[2], '@')) { list($authParts, $match[2]) = explode('@', $match[2], 2); } From 4e43f849c7a89dd9c83136a78da19673aba12ee4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 30 Oct 2019 12:56:08 +0100 Subject: [PATCH 54/63] Avoid overwriting credentials with existing ones from git repos, refs #8293 --- src/Composer/Util/Git.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 73e3139ce..2da64d80d 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -54,9 +54,9 @@ class Git } if (!$initialClone) { - // capture username/password from URL if there is one + // capture username/password from URL if there is one and we have no auth configured yet $this->process->execute('git remote -v', $output, $cwd); - if (preg_match('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) { + if (preg_match('{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im', $output, $match) && !$this->io->hasAuthentication($match[3])) { $this->io->setAuthentication($match[3], rawurldecode($match[1]), rawurldecode($match[2])); } } From 149250ab92feadd9fe045b54d6e42f33d185a595 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 30 Oct 2019 15:24:53 +0100 Subject: [PATCH 55/63] Remove credentials from git remotes in cache and vendor dirs This only removes the credentials if they are managed by composer auth.json or equivalent, if the credentials were present in the package URL to begin with they might remain Refs #8293 Fixes #3644 Closes #3608 --- src/Composer/Downloader/GitDownloader.php | 23 +++++++--- src/Composer/Util/Git.php | 21 +++++++-- .../Test/Downloader/GitDownloaderTest.php | 43 +++++++++++++------ 3 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index f1657c6c4..edeaa7686 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -51,31 +51,37 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface $gitVersion = $this->gitUtil->getVersion(); $msg = "Cloning ".$this->getShortHash($ref); - $command = 'git clone --no-checkout %url% %path% && cd '.$flag.'%path% && git remote add composer %url% && git fetch composer'; + $command = 'git clone --no-checkout %url% %path% && cd '.$flag.'%path% && git remote add composer %url% && git fetch composer && git remote set-url origin %sanitizedUrl% && git remote set-url composer %sanitizedUrl%'; if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) { $this->io->writeError('', true, IOInterface::DEBUG); $this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG); try { - $this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref); + if (!$this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref)) { + $this->io->writeError('Failed to update '.$url.' in cache, package installation for '.$package->getPrettyName().' might fail.'); + } if (is_dir($cachePath)) { $command = 'git clone --no-checkout %cachePath% %path% --dissociate --reference %cachePath% ' . '&& cd '.$flag.'%path% ' - . '&& git remote set-url origin %url% && git remote add composer %url%'; + . '&& git remote set-url origin %sanitizedUrl% && git remote add composer %sanitizedUrl%'; $msg = "Cloning ".$this->getShortHash($ref).' from cache'; } } catch (\RuntimeException $e) { + if (0 === strpos(get_class($e), 'PHPUnit')) { + throw $e; + } } } $this->io->writeError($msg); $commandCallable = function ($url) use ($path, $command, $cachePath) { return str_replace( - array('%url%', '%path%', '%cachePath%'), + array('%url%', '%path%', '%cachePath%', '%sanitizedUrl%'), array( ProcessExecutor::escape($url), ProcessExecutor::escape($path), ProcessExecutor::escape($cachePath), + ProcessExecutor::escape(preg_replace('{://([^@]+?):(.+?)@}', '://', $url)), ), $command ); @@ -119,10 +125,15 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface $ref = $target->getSourceReference(); $this->io->writeError(" Checking out ".$this->getShortHash($ref)); - $command = 'git remote set-url composer %s && git rev-parse --quiet --verify %s || (git fetch composer && git fetch --tags composer)'; + $command = '(git remote set-url composer %s && git rev-parse --quiet --verify %s || (git fetch composer && git fetch --tags composer)) && git remote set-url composer %s'; $commandCallable = function ($url) use ($command, $ref) { - return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($ref.'^{commit}')); + return sprintf( + $command, + ProcessExecutor::escape($url), + ProcessExecutor::escape($ref.'^{commit}'), + ProcessExecutor::escape(preg_replace('{://([^@]+?):(.+?)@}', '://', $url)) + ); }; $this->gitUtil->runCommand($commandCallable, $url, $path); diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 2da64d80d..15c46d080 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -240,7 +240,9 @@ class Git if (is_dir($dir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $dir) && trim($output) === '.') { try { $commandCallable = function ($url) { - return sprintf('git remote set-url origin %s && git remote update --prune origin', ProcessExecutor::escape($url)); + $sanitizedUrl = preg_replace('{://([^@]+?):(.+?)@}', '://', $url); + + return sprintf('git remote set-url origin %s && git remote update --prune origin && git remote set-url origin %s', ProcessExecutor::escape($url), ProcessExecutor::escape($sanitizedUrl)); }; $this->runCommand($commandCallable, $url, $dir); } catch (\Exception $e) { @@ -263,17 +265,28 @@ class Git } public function fetchRefOrSyncMirror($url, $dir, $ref) + { + if ($this->checkRefIsInMirror($url, $dir, $ref)) { + return true; + } + + if ($this->syncMirror($url, $dir)) { + return $this->checkRefIsInMirror($url, $dir, $ref); + } + + return false; + } + + private function checkRefIsInMirror($url, $dir, $ref) { if (is_dir($dir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $dir) && trim($output) === '.') { $escapedRef = ProcessExecutor::escape($ref.'^{commit}'); - $exitCode = $this->process->execute(sprintf('git rev-parse --quiet --verify %s', $escapedRef), $output, $dir); + $exitCode = $this->process->execute(sprintf('git rev-parse --quiet --verify %s', $escapedRef), $ignoredOutput, $dir); if ($exitCode === 0) { return true; } } - $this->syncMirror($url, $dir); - return false; } diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index c3cd31a4a..cd3375464 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -108,7 +108,7 @@ class GitDownloaderTest extends TestCase return 0; })); - $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer && git remote set-url origin 'https://example.com/composer/composer' && git remote set-url composer 'https://example.com/composer/composer'"); $processExecutor->expects($this->at(1)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -163,6 +163,9 @@ class GitDownloaderTest extends TestCase $this->setupConfig($config); $cachePath = $config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', 'https://example.com/composer/composer').'/'; + $filesystem = new \Composer\Util\Filesystem; + $filesystem->removeDirectory($cachePath); + $expectedGitCommand = $this->winCompat(sprintf("git clone --mirror 'https://example.com/composer/composer' '%s'", $cachePath)); $processExecutor->expects($this->at(1)) ->method('execute') @@ -172,24 +175,36 @@ class GitDownloaderTest extends TestCase return 0; })); + $processExecutor->expects($this->at(2)) + ->method('execute') + ->with($this->equalTo('git rev-parse --git-dir'), $this->anything(), $this->equalTo($this->winCompat($cachePath))) + ->will($this->returnCallback(function ($command, &$output = null) { + $output = '.'; + + return 0; + })); + $processExecutor->expects($this->at(3)) + ->method('execute') + ->with($this->equalTo($this->winCompat('git rev-parse --quiet --verify \'1234567890123456789012345678901234567890^{commit}\'')), $this->equalTo(null), $this->equalTo($this->winCompat($cachePath))) + ->will($this->returnValue(0)); $expectedGitCommand = $this->winCompat(sprintf("git clone --no-checkout '%1\$s' 'composerPath' --dissociate --reference '%1\$s' && cd 'composerPath' && git remote set-url origin 'https://example.com/composer/composer' && git remote add composer 'https://example.com/composer/composer'", $cachePath)); - $processExecutor->expects($this->at(2)) + $processExecutor->expects($this->at(4)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) ->will($this->returnValue(0)); - $processExecutor->expects($this->at(3)) + $processExecutor->expects($this->at(5)) ->method('execute') ->with($this->equalTo($this->winCompat("git branch -r")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); - $processExecutor->expects($this->at(4)) + $processExecutor->expects($this->at(6)) ->method('execute') ->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); - $processExecutor->expects($this->at(5)) + $processExecutor->expects($this->at(7)) ->method('execute') ->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); @@ -225,7 +240,7 @@ class GitDownloaderTest extends TestCase return 0; })); - $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/mirrors/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/mirrors/composer' && git fetch composer && git remote set-url origin 'https://github.com/mirrors/composer' && git remote set-url composer 'https://github.com/mirrors/composer'"); $processExecutor->expects($this->at(1)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -236,7 +251,7 @@ class GitDownloaderTest extends TestCase ->with() ->will($this->returnValue('Error1')); - $expectedGitCommand = $this->winCompat("git clone --no-checkout 'git@github.com:mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git@github.com:mirrors/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'git@github.com:mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git@github.com:mirrors/composer' && git fetch composer && git remote set-url origin 'git@github.com:mirrors/composer' && git remote set-url composer 'git@github.com:mirrors/composer'"); $processExecutor->expects($this->at(3)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -309,7 +324,7 @@ class GitDownloaderTest extends TestCase return 0; })); - $expectedGitCommand = $this->winCompat("git clone --no-checkout '{$url}' 'composerPath' && cd 'composerPath' && git remote add composer '{$url}' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout '{$url}' 'composerPath' && cd 'composerPath' && git remote add composer '{$url}' && git fetch composer && git remote set-url origin '{$url}' && git remote set-url composer '{$url}'"); $processExecutor->expects($this->at(1)) ->method('execute') ->with($this->equalTo($expectedGitCommand)) @@ -337,7 +352,7 @@ class GitDownloaderTest extends TestCase */ public function testDownloadThrowsRuntimeExceptionIfGitCommandFails() { - $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer"); + $expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer && git remote set-url origin 'https://example.com/composer/composer' && git remote set-url composer 'https://example.com/composer/composer'"); $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); $packageMock->expects($this->any()) ->method('getSourceReference') @@ -380,7 +395,7 @@ class GitDownloaderTest extends TestCase public function testUpdate() { - $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)"); + $expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'"); $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); $packageMock->expects($this->any()) @@ -429,7 +444,7 @@ class GitDownloaderTest extends TestCase public function testUpdateWithNewRepoUrl() { - $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)"); + $expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'"); $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); $packageMock->expects($this->any()) @@ -501,7 +516,7 @@ composer https://github.com/old/url (push) */ public function testUpdateThrowsRuntimeExceptionIfGitCommandFails() { - $expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)"); + $expectedGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'"); $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); $packageMock->expects($this->any()) @@ -542,8 +557,8 @@ composer https://github.com/old/url (push) public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover() { - $expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)"); - $expectedSecondGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)"); + $expectedFirstGitUpdateCommand = $this->winCompat("(git remote set-url composer '' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer ''"); + $expectedSecondGitUpdateCommand = $this->winCompat("(git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)) && git remote set-url composer 'https://github.com/composer/composer'"); $packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); $packageMock->expects($this->any()) From 050707ed0cdb2d3f9a2c446a9689101a7ad6146f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 30 Oct 2019 16:35:13 +0100 Subject: [PATCH 56/63] Fix output of dump-autoload command to avoid interfering with warnings, refs #8397 --- src/Composer/Command/DumpAutoloadCommand.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index dbda29d63..f1e91f15c 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -63,11 +63,11 @@ EOT $apcu = $input->getOption('apcu') || $config->get('apcu-autoloader'); if ($authoritative) { - $this->getIO()->writeError('Generating optimized autoload files (authoritative)', false); + $this->getIO()->write('Generating optimized autoload files (authoritative)'); } elseif ($optimize) { - $this->getIO()->writeError('Generating optimized autoload files', false); + $this->getIO()->write('Generating optimized autoload files'); } else { - $this->getIO()->writeError('Generating autoload files', false); + $this->getIO()->write('Generating autoload files'); } $generator = $composer->getAutoloadGenerator(); @@ -78,11 +78,11 @@ EOT $numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); if ($authoritative) { - $this->getIO()->overwriteError('Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes'); + $this->getIO()->write('Generated optimized autoload files (authoritative) containing '. $numberOfClasses .' classes'); } elseif ($optimize) { - $this->getIO()->overwriteError('Generated optimized autoload files containing '. $numberOfClasses .' classes'); + $this->getIO()->write('Generated optimized autoload files containing '. $numberOfClasses .' classes'); } else { - $this->getIO()->overwriteError('Generated autoload files containing '. $numberOfClasses .' classes'); + $this->getIO()->write('Generated autoload files containing '. $numberOfClasses .' classes'); } return 0; From f6b8643dcdbad43d0373ec03089634daed32b324 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 Nov 2019 14:50:15 +0100 Subject: [PATCH 57/63] Change PSR-fix for optimized autoloader to only warn for now, refs #8397 --- src/Composer/Autoload/ClassMapGenerator.php | 25 +++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index be8bcdb11..49414883c 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -143,6 +143,10 @@ class ClassMapGenerator { $validClasses = array(); $rejectedClasses = array(); + + $realSubPath = substr($filePath, strlen($basePath) + 1); + $realSubPath = substr($realSubPath, 0, strrpos($realSubPath, '.')); + foreach ($classes as $class) { // silently skip if ns doesn't have common root if ('' !== $baseNamespace && 0 !== strpos($class, $baseNamespace)) { @@ -166,8 +170,6 @@ class ClassMapGenerator } else { throw new \RuntimeException("namespaceType must be psr-0 or psr-4, $namespaceType given"); } - $realSubPath = substr($filePath, strlen($basePath) + 1); - $realSubPath = substr($realSubPath, 0, strrpos($realSubPath, '.')); if ($subPath === $realSubPath) { $validClasses[] = $class; } else { @@ -175,16 +177,21 @@ class ClassMapGenerator } } // warn only if no valid classes, else silently skip invalid - if (!empty($validClasses)) { - return $validClasses; - } - if ($io) { + if (empty($validClasses)) { foreach ($rejectedClasses as $class) { - $io->writeError("Warning: class $class located in $filePath " - . "doesn't comply with $namespaceType autoloading standard. Skipping."); + trigger_error( + "Class $class located in ".preg_replace('{^'.preg_quote(getcwd()).'}', '.', $filePath, 1)." does not comply with $namespaceType autoloading standard. It will not autoload anymore in Composer v1.11+.", + E_USER_DEPRECATED + ); } + + // TODO enable in Composer v1.11 or 2.0 whichever comes first + //return array(); } - return array(); + + // TODO enable in Composer v1.11 or 2.0 whichever comes first + //return $validClasses; + return $classes; } /** From c3f034e33bea599186db955554d384b9ff4699bd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 Nov 2019 15:13:28 +0100 Subject: [PATCH 58/63] Fix tests for PSR-fix in optimized autoloader, refs #8397 --- .../Test/Autoload/AutoloadGeneratorTest.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 86d8bb247..42aff0868 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -568,8 +568,22 @@ class AutoloadGeneratorTest extends TestCase file_put_contents($this->workingDir.'/psr4/match.php', 'workingDir.'/psr4/badfile.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); + set_error_handler(function ($errno, $errstr) { + if ($errno !== E_USER_DEPRECATED || !preg_match('{^Class (psr4\\\\badclass|psr0_badclass) located in .+? does not comply with psr-[04] autoloading standard}', $errstr)) { + throw new \UnexpectedValueException('Unexpected error: '.$errstr); + } + }); + + try { + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); + } catch (\Exception $e) { + restore_error_handler(); + throw $e; + } + restore_error_handler(); + $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated."); + $expectedClassmap = << \$baseDir . '/psr0/psr0/badfile.php', 'psr0_match' => \$baseDir . '/psr0/psr0/match.php', + 'psr4\\\\badclass' => \$baseDir . '/psr4/badfile.php', 'psr4\\\\match' => \$baseDir . '/psr4/match.php', ); From 502b68967ac7998f2479a7d0b51fc25548cb6a8e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 Nov 2019 15:31:55 +0100 Subject: [PATCH 59/63] Fix tests for PSR-fix in optimized autoloader, refs #8397 --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- .../Test/Autoload/AutoloadGeneratorTest.php | 19 +++---------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 49414883c..80d56b4d0 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -189,7 +189,7 @@ class ClassMapGenerator //return array(); } - // TODO enable in Composer v1.11 or 2.0 whichever comes first + // TODO enable in Composer v1.11 or 2.0 whichever comes first & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses //return $validClasses; return $classes; } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 42aff0868..ba89ccea9 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -552,6 +552,8 @@ class AutoloadGeneratorTest extends TestCase { $package = new Package('a', '1.0', '1.0'); + $this->markTestSkipped('Skipped until ClassMapGenerator ignoring of invalid PSR-x classes is enabled'); + $package->setAutoload(array( 'psr-0' => array('psr0_' => 'psr0/'), 'psr-4' => array('psr4\\' => 'psr4/'), @@ -568,20 +570,7 @@ class AutoloadGeneratorTest extends TestCase file_put_contents($this->workingDir.'/psr4/match.php', 'workingDir.'/psr4/badfile.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); - } catch (\Exception $e) { - restore_error_handler(); - throw $e; - } - restore_error_handler(); - + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated."); $expectedClassmap = << \$baseDir . '/psr0/psr0/badfile.php', 'psr0_match' => \$baseDir . '/psr0/psr0/match.php', - 'psr4\\\\badclass' => \$baseDir . '/psr4/badfile.php', 'psr4\\\\match' => \$baseDir . '/psr4/match.php', ); From d059d90ecf326132b8da2dc0ad79e3d6e00ebb47 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 Nov 2019 16:18:42 +0100 Subject: [PATCH 60/63] Fix PSR warnings for optimized autoloader, refs #8397, refs #8403 --- src/Composer/Autoload/ClassMapGenerator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 80d56b4d0..0a2123d20 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -110,7 +110,8 @@ class ClassMapGenerator foreach ($classes as $class) { // skip classes not within the given namespace prefix - if (null === $autoloadType && null !== $namespace && 0 !== strpos($class, $namespace)) { + // TODO enable in Composer v1.11 or 2.0 whichever comes first + if (/* null === $autoloadType && */ null !== $namespace && 0 !== strpos($class, $namespace)) { continue; } From af86ca1fb3bde63ec351d78f431da5502318bc3f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 Nov 2019 16:32:34 +0100 Subject: [PATCH 61/63] Output a hint that maybe you are not in the right directory, fixes #8404 --- src/Composer/Command/RequireCommand.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 1fa280d3f..bb99fc310 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -139,7 +139,15 @@ EOT } $phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion(); - $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update')); + try { + $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update')); + } catch (\Exception $e) { + if ($this->newlyCreated) { + throw new \RuntimeException('No composer.json present in the current directory, this may be the cause of the following exception.', 0, $e); + } + + throw $e; + } $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; From 1a797c16a0e641c91cfa201da72a59e0925da0c4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 Nov 2019 16:40:30 +0100 Subject: [PATCH 62/63] Prepare 1.9.1 changelog --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15526d860..ade8d99cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +### [1.9.1] 2019-11-01 + + * Fixed various credential handling issues with gitlab and github + * Fixed credentials being present in git remotes in Composer cache and vendor directory when not using SSH keys + * Fixed `composer why` not listing replacers as a reason something is present + * Fixed various PHP 7.4 compatibility issues + * Fixed root warnings always present in Docker containers, setting COMPOSER_ALLOW_SUPERUSER is not necessary anymore + * Fixed GitHub access tokens leaking into debug-verbosity output + * Fixed several edge case issues detecting GitHub, Bitbucket and GitLab repository types + * Fixed Composer asking if you want to use a composer.json in a parent directory when ran in non-interactive mode + * Fixed classmap autoloading issue finding classes located within a few non-PHP context blocks (?>... Date: Fri, 1 Nov 2019 17:13:02 +0100 Subject: [PATCH 63/63] Fix PSR warnings for optimized autoloader, refs #8397, refs #8403 --- src/Composer/Autoload/ClassMapGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 0a2123d20..ef12809d5 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -111,7 +111,7 @@ class ClassMapGenerator foreach ($classes as $class) { // skip classes not within the given namespace prefix // TODO enable in Composer v1.11 or 2.0 whichever comes first - if (/* null === $autoloadType && */ null !== $namespace && 0 !== strpos($class, $namespace)) { + if (/* null === $autoloadType && */ null !== $namespace && '' !== $namespace && 0 !== strpos($class, $namespace)) { continue; }