From 94c7afb69b4bf44671e4d0d3d1a545ba30988c95 Mon Sep 17 00:00:00 2001 From: Fabien Villepinte Date: Sun, 12 Apr 2020 21:54:51 +0200 Subject: [PATCH 001/193] Check syntax of PHP files in src --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0cf228882..22c4c9db5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,6 +71,7 @@ before_script: - git config --global user.email travis@example.com script: + - ( set -e; for f in $(find src/ -name *.php); do php -l "$f" || exit 1; done ) - if [[ $PHPSTAN == "1" ]]; then bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --no-update && bin/composer update phpstan/* phpunit/* sebastian/* --with-all-dependencies && From 3e8021ed066a4bf9994f5aa0267879671676e463 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 15 Apr 2020 13:29:03 +0200 Subject: [PATCH 002/193] Add package naming pattern to the composer.json JSON schema fixes #8749 --- res/composer-schema.json | 3 ++- .../Composer/Test/Json/ComposerSchemaTest.php | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index 1bdf1f438..cdd93d8af 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -7,7 +7,8 @@ "properties": { "name": { "type": "string", - "description": "Package name, including 'vendor-name/' prefix." + "description": "Package name, including 'vendor-name/' prefix.", + "pattern": "^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$" }, "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.", diff --git a/tests/Composer/Test/Json/ComposerSchemaTest.php b/tests/Composer/Test/Json/ComposerSchemaTest.php index 13ead0ad2..a6f85d4cf 100644 --- a/tests/Composer/Test/Json/ComposerSchemaTest.php +++ b/tests/Composer/Test/Json/ComposerSchemaTest.php @@ -20,6 +20,23 @@ use Composer\Test\TestCase; */ class ComposerSchemaTest extends TestCase { + public function testNamePattern() + { + $expectedError = array( + array( + 'property' => 'name', + 'message' => 'Does not match the regex pattern ^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$', + 'constraint' => 'pattern', + 'pattern' => '^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$' + ), + ); + + $json = '{"name": "vendor/-pack__age", "description": "description"}'; + $this->assertEquals($expectedError, $this->check($json)); + $json = '{"name": "Vendor/Package", "description": "description"}'; + $this->assertEquals($expectedError, $this->check($json)); + } + public function testRequiredProperties() { $json = '{ }'; @@ -41,13 +58,13 @@ class ComposerSchemaTest extends TestCase public function testOptionalAbandonedProperty() { - $json = '{"name": "name", "description": "description", "abandoned": true}'; + $json = '{"name": "vendor/package", "description": "description", "abandoned": true}'; $this->assertTrue($this->check($json)); } public function testRequireTypes() { - $json = '{"name": "name", "description": "description", "require": {"a": ["b"]} }'; + $json = '{"name": "vendor/package", "description": "description", "require": {"a": ["b"]} }'; $this->assertEquals(array( array('property' => 'require.a', 'message' => 'Array value found, but a string is required', 'constraint' => 'type'), ), $this->check($json)); From c1f6f339c2aa47b5ea86d8bcb5a24871fd0fa9f3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 15 Apr 2020 16:36:49 +0200 Subject: [PATCH 003/193] Tweak wording --- UPGRADE-2.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index eb4acc53f..10e5a587f 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -5,7 +5,7 @@ - If a packages exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. - Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10 - Package names now must comply to our naming guidelines or Composer will abort, as per the warnings introduced in 1.8.1 -- Removed --no-suggest flag as it is not needed anymore +- Deprecated --no-suggest flag as it is not needed anymore - `update` now lists changes to the lock file first, and then the changes applied when installing the lock file to the vendor dir - `HTTPS_PROXY_REQUEST_FULLURI` if not specified will now default to false as this seems to work better in most environments From 80a7c40c764e6ed3359e3e1648511c51cc50f8f0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 15 Apr 2020 00:58:35 +0200 Subject: [PATCH 004/193] Shorten long lists of similar versions in problem output, fixes #8743 --- src/Composer/DependencyResolver/Problem.php | 43 +++++-- src/Composer/DependencyResolver/Rule.php | 18 +-- src/Composer/DependencyResolver/RuleSet.php | 4 +- .../SolverProblemsException.php | 4 +- src/Composer/Installer.php | 6 +- .../Test/DependencyResolver/RuleTest.php | 2 +- .../Test/DependencyResolver/SolverTest.php | 8 +- .../installer/problems-reduce-versions.test | 116 ++++++++++++++++++ 8 files changed, 168 insertions(+), 33 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 6861b814c..683c06e78 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -66,7 +66,7 @@ class Problem * @param array $installedMap A map of all present packages * @return string */ - public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array()) + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) { // TODO doesn't this entirely defeat the purpose of the problem sections? what's the point of sections? $reasons = call_user_func_array('array_merge', array_reverse($this->reasons)); @@ -90,13 +90,13 @@ class Problem } if (empty($packages)) { - return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $pool, $packageName, $constraint)); + return "\n ".implode(self::getMissingPackageReason($repositorySet, $request, $pool, $isVerbose, $packageName, $constraint)); } } $messages = array(); foreach ($reasons as $rule) { - $messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool); + $messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); } return "\n - ".implode("\n - ", array_unique($messages)); @@ -138,7 +138,7 @@ class Problem /** * @internal */ - public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $packageName, $constraint = null) + public static function getMissingPackageReason(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $packageName, $constraint = null) { // handle php/hhvm if ($packageName === 'php' || $packageName === 'php-64bit' || $packageName === 'hhvm') { @@ -210,7 +210,7 @@ class Problem return $rootReqs[$packageName]->matches(new Constraint('==', $p->getVersion())); }); if (0 === count($filtered)) { - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with your root composer.json require ('.$rootReqs[$packageName]->getPrettyString().').'); } } @@ -220,7 +220,7 @@ class Problem return $fixedConstraint->matches(new Constraint('==', $p->getVersion())); }); if (0 === count($filtered)) { - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but the package is fixed to '.$fixedPackage->getPrettyVersion().' (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command.'); } } @@ -229,15 +229,15 @@ class Problem }); if (!$nonLockedPackages) { - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file.'); } - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with another require.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these conflict' : 'it conflicts').' with another require.'); } // check if the package is found when bypassing stability checks if ($packages = $repositorySet->findPackages($packageName, $constraint, RepositorySet::ALLOW_UNACCEPTABLE_STABILITIES)) { - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your minimum-stability.'); } // check if the package is found when bypassing the constraint check @@ -257,10 +257,10 @@ class Problem } } - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.'); } - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your constraint.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your constraint.'); } if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) { @@ -287,7 +287,7 @@ class Problem /** * @internal */ - public static function getPackageList(array $packages) + public static function getPackageList(array $packages, $isVerbose) { $prepared = array(); foreach ($packages as $package) { @@ -299,6 +299,25 @@ class Problem if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) { unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]); } + if (!$isVerbose && count($package['versions']) > 4) { + $filtered = array(); + $byMajor = array(); + foreach ($package['versions'] as $version => $pretty) { + $byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty; + } + foreach ($byMajor as $versions) { + if (count($versions) > 4) { + $filtered[] = $versions[0]; + $filtered[] = '...'; + $filtered[] = $versions[count($versions) - 1]; + } else { + $filtered = array_merge($filtered, $versions); + } + } + + $package['versions'] = $filtered; + } + $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']'; } diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 2e82dab82..07550d8d0 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -127,7 +127,7 @@ abstract class Rule return $this->getReason() === self::RULE_FIXED && $this->reasonData['lockable']; } - public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, array $installedMap = array(), array $learnedPool = array()) + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) { $literals = $this->getLiterals(); @@ -152,7 +152,7 @@ abstract class Rule return 'No package found to satisfy root composer.json require '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : ''); } - return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages).'.'; + return 'Root composer.json requires '.$packageName.($constraint ? ' '.$constraint->getPrettyString() : '').' -> satisfiable by '.$this->formatPackagesUnique($pool, $packages, $isVerbose).'.'; case self::RULE_FIXED: $package = $this->deduplicateMasterAlias($this->reasonData['package']); @@ -179,11 +179,11 @@ abstract class Rule $text = $this->reasonData->getPrettyString($sourcePackage); if ($requires) { - $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires) . '.'; + $text .= ' -> satisfiable by ' . $this->formatPackagesUnique($pool, $requires, $isVerbose) . '.'; } else { $targetName = $this->reasonData->getTarget(); - $reason = Problem::getMissingPackageReason($repositorySet, $request, $pool, $targetName, $this->reasonData->getConstraint()); + $reason = Problem::getMissingPackageReason($repositorySet, $request, $pool, $isVerbose, $targetName, $this->reasonData->getConstraint()); return $text . ' -> ' . $reason[1]; } @@ -227,13 +227,13 @@ abstract class Rule } if ($installedPackages && $removablePackages) { - return $this->formatPackagesUnique($pool, $removablePackages).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages).'. '.$reason; + return $this->formatPackagesUnique($pool, $removablePackages, $isVerbose).' cannot be installed as that would require removing '.$this->formatPackagesUnique($pool, $installedPackages, $isVerbose).'. '.$reason; } - return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals).'. '.$reason; + return 'Only one of these can be installed: '.$this->formatPackagesUnique($pool, $literals, $isVerbose).'. '.$reason; } - return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals) . '.'; + return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose) . '.'; case self::RULE_LEARNED: if (isset($learnedPool[$this->reasonData])) { $learnedString = ', learned rules:'."\n - "; @@ -260,7 +260,7 @@ abstract class Rule * * @return string */ - protected function formatPackagesUnique($pool, array $packages) + protected function formatPackagesUnique($pool, array $packages, $isVerbose) { $prepared = array(); foreach ($packages as $index => $package) { @@ -269,7 +269,7 @@ abstract class Rule } } - return Problem::getPackageList($packages); + return Problem::getPackageList($packages, $isVerbose); } private function getReplacedNames(PackageInterface $package) diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index d37ca1e9f..8058f9f68 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -157,13 +157,13 @@ class RuleSet implements \IteratorAggregate, \Countable return array_keys($types); } - public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null, Pool $pool = null) + public function getPrettyString(RepositorySet $repositorySet = null, Request $request = null, Pool $pool = null, $isVerbose = false) { $string = "\n"; foreach ($this->rules as $type => $rules) { $string .= str_pad(self::$types[$type], 8, ' ') . ": "; foreach ($rules as $rule) { - $string .= ($repositorySet && $request && $pool ? $rule->getPrettyString($repositorySet, $request, $pool) : $rule)."\n"; + $string .= ($repositorySet && $request && $pool ? $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose) : $rule)."\n"; } $string .= "\n\n"; } diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 1821bde91..082b4c4f7 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -31,7 +31,7 @@ class SolverProblemsException extends \RuntimeException parent::__construct('Failed resolving dependencies with '.count($problems).' problems, call getPrettyString to get formatted details', 2); } - public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isDevExtraction = false) + public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, $isDevExtraction = false) { $installedMap = $request->getPresentMap(true); $hasExtensionProblems = false; @@ -39,7 +39,7 @@ class SolverProblemsException extends \RuntimeException $problems = array(); foreach ($this->problems as $problem) { - $problems[] = $problem->getPrettyString($repositorySet, $request, $pool, $installedMap, $this->learnedPool)."\n"; + $problems[] = $problem->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $this->learnedPool)."\n"; if (!$hasExtensionProblems && $this->hasExtensionProblems($problem->getReasons())) { $hasExtensionProblems = true; diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 7d1da54a4..c1e8099c4 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -402,7 +402,7 @@ class Installer $solver = null; } catch (SolverProblemsException $e) { $this->io->writeError('Your requirements could not be resolved to an installable set of packages.', true, IOInterface::QUIET); - $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool)); + $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose())); if (!$this->devMode) { $this->io->writeError('Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.', true, IOInterface::QUIET); } @@ -563,7 +563,7 @@ class Installer $this->io->writeError('Unable to find a compatible set of packages based on your non-dev requirements alone.', true, IOInterface::QUIET); $this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.'); $this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.'); - $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, true)); + $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true)); return max(1, $e->getCode()); } @@ -627,7 +627,7 @@ class Installer } } catch (SolverProblemsException $e) { $this->io->writeError('Your lock file does not contain a compatible set of packages. Please run composer update.', true, IOInterface::QUIET); - $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool)); + $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose())); return max(1, $e->getCode()); } diff --git a/tests/Composer/Test/DependencyResolver/RuleTest.php b/tests/Composer/Test/DependencyResolver/RuleTest.php index f819397fb..2e1a9921d 100644 --- a/tests/Composer/Test/DependencyResolver/RuleTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleTest.php @@ -104,6 +104,6 @@ class RuleTest extends TestCase $rule = new GenericRule(array($p1->getId(), -$p2->getId()), Rule::RULE_PACKAGE_REQUIRES, new Link('baz', 'foo')); - $this->assertEquals('baz 1.1 relates to foo -> satisfiable by foo[2.1].', $rule->getPrettyString($repositorySetMock, $requestMock, $pool)); + $this->assertEquals('baz 1.1 relates to foo -> satisfiable by foo[2.1].', $rule->getPrettyString($repositorySetMock, $requestMock, $pool, false)); } } diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 3090d9d83..5fca6bfc2 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -83,7 +83,7 @@ class SolverTest extends TestCase $problems = $e->getProblems(); $this->assertCount(1, $problems); $this->assertEquals(2, $e->getCode()); - $this->assertEquals("\n - Root composer.json requires b, it could not be found in any version, there may be a typo in the package name.", $problems[0]->getPrettyString($this->repoSet, $this->request, $this->pool)); + $this->assertEquals("\n - Root composer.json requires b, it could not be found in any version, there may be a typo in the package name.", $problems[0]->getPrettyString($this->repoSet, $this->request, $this->pool, false)); } } @@ -654,7 +654,7 @@ class SolverTest extends TestCase $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n"; $msg .= " - A 1.0 conflicts with B 1.0.\n"; $msg .= " - Root composer.json requires b -> satisfiable by B[1.0].\n"; - $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool)); + $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool, false)); } } @@ -684,7 +684,7 @@ class SolverTest extends TestCase $msg .= " Problem 1\n"; $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n"; $msg .= " - A 1.0 requires b >= 2.0 -> found B[1.0] but it does not match your constraint.\n"; - $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool)); + $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool, false)); } } @@ -729,7 +729,7 @@ class SolverTest extends TestCase $msg .= " - You can only install one version of a package, so only one of these can be installed: B[0.9, 1.0].\n"; $msg .= " - A 1.0 requires b >= 1.0 -> satisfiable by B[1.0].\n"; $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n"; - $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool)); + $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool, false)); } } diff --git a/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test b/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test new file mode 100644 index 000000000..1235e9467 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test @@ -0,0 +1,116 @@ +--TEST-- +Test the error output minifies version lists +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + {"name": "a/a", "version": "1.0.0", "require": {"b/b": "1.0.0"}}, + {"name": "b/b", "version": "1.0.0"}, + {"name": "b/b", "version": "1.0.1"}, + {"name": "b/b", "version": "1.0.2"}, + {"name": "b/b", "version": "1.0.3"}, + {"name": "b/b", "version": "1.0.4"}, + {"name": "b/b", "version": "1.0.5"}, + {"name": "b/b", "version": "1.0.6"}, + {"name": "b/b", "version": "1.0.7"}, + {"name": "b/b", "version": "1.0.8"}, + {"name": "b/b", "version": "1.0.9"}, + {"name": "b/b", "version": "1.1.0"}, + {"name": "b/b", "version": "1.1.1"}, + {"name": "b/b", "version": "1.1.2"}, + {"name": "b/b", "version": "1.1.3"}, + {"name": "b/b", "version": "v1.1.4"}, + {"name": "b/b", "version": "1.1.5"}, + {"name": "b/b", "version": "v1.1.6"}, + {"name": "b/b", "version": "1.1.7-alpha"}, + {"name": "b/b", "version": "1.1.8"}, + {"name": "b/b", "version": "1.1.9"}, + {"name": "b/b", "version": "1.2.0"}, + {"name": "b/b", "version": "1.2.1"}, + {"name": "b/b", "version": "1.2.2"}, + {"name": "b/b", "version": "1.2.3"}, + {"name": "b/b", "version": "1.2.4"}, + {"name": "b/b", "version": "1.2.5"}, + {"name": "b/b", "version": "1.2.6"}, + {"name": "b/b", "version": "1.2.7"}, + {"name": "b/b", "version": "1.2.8"}, + {"name": "b/b", "version": "1.2.9"}, + {"name": "b/b", "version": "2.0.0"}, + {"name": "b/b", "version": "2.0.1"}, + {"name": "b/b", "version": "2.0.2"}, + {"name": "b/b", "version": "2.0.3"}, + {"name": "b/b", "version": "2.0.4"}, + {"name": "b/b", "version": "2.0.5"}, + {"name": "b/b", "version": "2.0.6"}, + {"name": "b/b", "version": "2.0.7"}, + {"name": "b/b", "version": "2.0.8"}, + {"name": "b/b", "version": "2.0.9"}, + {"name": "b/b", "version": "2.1.0"}, + {"name": "b/b", "version": "2.1.1"}, + {"name": "b/b", "version": "2.1.2"}, + {"name": "b/b", "version": "2.1.3"}, + {"name": "b/b", "version": "2.1.4"}, + {"name": "b/b", "version": "2.1.5"}, + {"name": "b/b", "version": "2.1.6"}, + {"name": "b/b", "version": "2.1.7"}, + {"name": "b/b", "version": "2.1.8"}, + {"name": "b/b", "version": "2.1.9"}, + {"name": "b/b", "version": "2.2.0"}, + {"name": "b/b", "version": "2.2.1"}, + {"name": "b/b", "version": "2.2.2"}, + {"name": "b/b", "version": "2.2.3"}, + {"name": "b/b", "version": "2.2.4"}, + {"name": "b/b", "version": "2.2.5"}, + {"name": "b/b", "version": "2.2.6"}, + {"name": "b/b", "version": "2.2.7"}, + {"name": "b/b", "version": "2.2.8"}, + {"name": "b/b", "version": "2.2.9"}, + {"name": "b/b", "version": "2.3.0-RC"}, + {"name": "b/b", "version": "3.0.0"}, + {"name": "b/b", "version": "3.0.1"}, + {"name": "b/b", "version": "3.0.2"}, + {"name": "b/b", "version": "3.0.3"}, + {"name": "b/b", "version": "4.0.0"} + ] + } + ], + "require": { + "a/a": "*", + "b/b": "^1.1 || ^2.0 || ^3.0" + }, + "minimum-stability": "dev" +} + +--LOCK-- +{ + "packages": [ + {"name": "b/b", "version": "1.0.0"} + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} + +--RUN-- +update a/a + +--EXPECT-EXIT-CODE-- +2 + +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies +Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - Root composer.json requires b/b ^1.1 || ^2.0 || ^3.0, found b/b[1.1.0, ..., 1.2.9, 2.0.0, ..., 2.3.0-RC, 3.0.0, 3.0.1, 3.0.2, 3.0.3] but the package is fixed to 1.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. + +--EXPECT-- + From bd34ff13287aa26cad87da7233c6b2ad655cc2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 16 Apr 2020 01:14:56 +0200 Subject: [PATCH 005/193] Remove unused property $httpDownloader --- src/Composer/Downloader/DownloadManager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 3ee786c17..2fcfdd392 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -25,7 +25,6 @@ use React\Promise\PromiseInterface; class DownloadManager { private $io; - private $httpDownloader; private $preferDist = false; private $preferSource = false; private $packagePreferences = array(); From 4ec73874cbae382807f3db70121643f1e8befbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Wer=C5=82os?= Date: Fri, 17 Apr 2020 22:38:14 +0200 Subject: [PATCH 006/193] Add "no-check-version" option to ValidateCommand --- src/Composer/Command/ValidateCommand.php | 6 ++++-- src/Composer/Util/ConfigValidator.php | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 6ab95ed1c..cb178d263 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -42,6 +42,7 @@ class ValidateCommand extends BaseCommand new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'), new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'), new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'), + new InputOption('no-check-version', null, InputOption::VALUE_NONE, 'Do not check if version field is present'), new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'), new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'), new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file'), @@ -86,8 +87,9 @@ EOT $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL; $checkPublish = !$input->getOption('no-check-publish'); $checkLock = !$input->getOption('no-check-lock'); + $checkVersion = !$input->getOption('no-check-version'); $isStrict = $input->getOption('strict'); - list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); + list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion); $lockErrors = array(); $composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins')); @@ -107,7 +109,7 @@ EOT $path = $composer->getInstallationManager()->getInstallPath($package); $file = $path . '/composer.json'; if (is_dir($path) && file_exists($file)) { - list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); + list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion); $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors); diff --git a/src/Composer/Util/ConfigValidator.php b/src/Composer/Util/ConfigValidator.php index 98a6016f2..4d6998dec 100644 --- a/src/Composer/Util/ConfigValidator.php +++ b/src/Composer/Util/ConfigValidator.php @@ -40,10 +40,11 @@ class ConfigValidator * * @param string $file The path to the file * @param int $arrayLoaderValidationFlags Flags for ArrayLoader validation + * @param bool $checkVersion Whether or not check if version field is present * * @return array a triple containing the errors, publishable errors, and warnings */ - public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL) + public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL, $checkVersion = true) { $errors = array(); $publishErrors = array(); @@ -109,7 +110,7 @@ class ConfigValidator } } - if (isset($manifest['version'])) { + if ($checkVersion && isset($manifest['version'])) { $warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.'; } From a54bf0e2d4003c9a442a96f3501a90667048e965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Wer=C5=82os?= Date: Sat, 18 Apr 2020 09:24:54 +0200 Subject: [PATCH 007/193] Use flags instead of boolean in ConfigValidator for checking version field --- src/Composer/Command/ValidateCommand.php | 2 +- src/Composer/Util/ConfigValidator.php | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index cb178d263..8da0854a3 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -87,7 +87,7 @@ EOT $checkAll = $input->getOption('no-check-all') ? 0 : ValidatingArrayLoader::CHECK_ALL; $checkPublish = !$input->getOption('no-check-publish'); $checkLock = !$input->getOption('no-check-lock'); - $checkVersion = !$input->getOption('no-check-version'); + $checkVersion = $input->getOption('no-check-version') ? 0 : ConfigValidator::CHECK_VERSION; $isStrict = $input->getOption('strict'); list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll, $checkVersion); diff --git a/src/Composer/Util/ConfigValidator.php b/src/Composer/Util/ConfigValidator.php index 4d6998dec..aae5879b3 100644 --- a/src/Composer/Util/ConfigValidator.php +++ b/src/Composer/Util/ConfigValidator.php @@ -28,6 +28,8 @@ use Composer\Spdx\SpdxLicenses; */ class ConfigValidator { + const CHECK_VERSION = 1; + private $io; public function __construct(IOInterface $io) @@ -40,11 +42,11 @@ class ConfigValidator * * @param string $file The path to the file * @param int $arrayLoaderValidationFlags Flags for ArrayLoader validation - * @param bool $checkVersion Whether or not check if version field is present + * @param int $flags Flags for validation * * @return array a triple containing the errors, publishable errors, and warnings */ - public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL, $checkVersion = true) + public function validate($file, $arrayLoaderValidationFlags = ValidatingArrayLoader::CHECK_ALL, $flags = self::CHECK_VERSION) { $errors = array(); $publishErrors = array(); @@ -110,7 +112,7 @@ class ConfigValidator } } - if ($checkVersion && isset($manifest['version'])) { + if (($flags & self::CHECK_VERSION) && isset($manifest['version'])) { $warnings[] = 'The version field is present, it is recommended to leave it out if the package is published on Packagist.'; } From c12a1a6d64dc87fb7e5d63f4ab6c9e701adc9f3a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sat, 18 Apr 2020 14:41:04 +0200 Subject: [PATCH 008/193] Update flag description --- src/Composer/Command/ValidateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 8da0854a3..1c76d678d 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -42,7 +42,7 @@ class ValidateCommand extends BaseCommand new InputOption('no-check-all', null, InputOption::VALUE_NONE, 'Do not validate requires for overly strict/loose constraints'), new InputOption('no-check-lock', null, InputOption::VALUE_NONE, 'Do not check if lock file is up to date'), new InputOption('no-check-publish', null, InputOption::VALUE_NONE, 'Do not check for publish errors'), - new InputOption('no-check-version', null, InputOption::VALUE_NONE, 'Do not check if version field is present'), + new InputOption('no-check-version', null, InputOption::VALUE_NONE, 'Do not report a warning if the version field is present'), new InputOption('with-dependencies', 'A', InputOption::VALUE_NONE, 'Also validate the composer.json of all installed dependencies'), new InputOption('strict', null, InputOption::VALUE_NONE, 'Return a non-zero exit code for warnings as well as errors'), new InputArgument('file', InputArgument::OPTIONAL, 'path to composer.json file'), From 424c08d6b291cbbadfa5f29a975629c500c3885d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Apr 2020 15:30:58 +0200 Subject: [PATCH 009/193] Fix bug loading ~dev in some circumstances --- src/Composer/Repository/ComposerRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 6dbe1cf38..ec7f9346d 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -662,7 +662,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito // load ~dev versions of the packages as well if needed foreach ($packageNames as $name => $constraint) { - if ($acceptableStabilities && $stabilityFlags && StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) { + if ($acceptableStabilities === null || $stabilityFlags === null || StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) { $packageNames[$name.'~dev'] = $constraint; } } From 3f338ee8d9c960f8f0bc2abc94888a5cb1c19a9d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Apr 2020 15:51:13 +0200 Subject: [PATCH 010/193] Make sure versions are sorted before they get trimmed in error output --- src/Composer/DependencyResolver/Problem.php | 1 + .../Test/Fixtures/installer/problems-reduce-versions.test | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 683c06e78..1754ef82c 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -300,6 +300,7 @@ class Problem unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]); } if (!$isVerbose && count($package['versions']) > 4) { + uksort($package['versions'], 'version_compare'); $filtered = array(); $byMajor = array(); foreach ($package['versions'] as $version => $pretty) { diff --git a/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test b/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test index 1235e9467..e67f33a43 100644 --- a/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test +++ b/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test @@ -11,29 +11,30 @@ Test the error output minifies version lists {"name": "b/b", "version": "1.0.1"}, {"name": "b/b", "version": "1.0.2"}, {"name": "b/b", "version": "1.0.3"}, + {"name": "b/b", "version": "v1.1.4"}, {"name": "b/b", "version": "1.0.4"}, {"name": "b/b", "version": "1.0.5"}, {"name": "b/b", "version": "1.0.6"}, {"name": "b/b", "version": "1.0.7"}, + {"name": "b/b", "version": "1.1.0"}, + {"name": "b/b", "version": "2.0.5"}, {"name": "b/b", "version": "1.0.8"}, {"name": "b/b", "version": "1.0.9"}, - {"name": "b/b", "version": "1.1.0"}, {"name": "b/b", "version": "1.1.1"}, {"name": "b/b", "version": "1.1.2"}, {"name": "b/b", "version": "1.1.3"}, - {"name": "b/b", "version": "v1.1.4"}, {"name": "b/b", "version": "1.1.5"}, {"name": "b/b", "version": "v1.1.6"}, {"name": "b/b", "version": "1.1.7-alpha"}, {"name": "b/b", "version": "1.1.8"}, {"name": "b/b", "version": "1.1.9"}, {"name": "b/b", "version": "1.2.0"}, - {"name": "b/b", "version": "1.2.1"}, {"name": "b/b", "version": "1.2.2"}, {"name": "b/b", "version": "1.2.3"}, {"name": "b/b", "version": "1.2.4"}, {"name": "b/b", "version": "1.2.5"}, {"name": "b/b", "version": "1.2.6"}, + {"name": "b/b", "version": "1.2.1"}, {"name": "b/b", "version": "1.2.7"}, {"name": "b/b", "version": "1.2.8"}, {"name": "b/b", "version": "1.2.9"}, @@ -42,7 +43,6 @@ Test the error output minifies version lists {"name": "b/b", "version": "2.0.2"}, {"name": "b/b", "version": "2.0.3"}, {"name": "b/b", "version": "2.0.4"}, - {"name": "b/b", "version": "2.0.5"}, {"name": "b/b", "version": "2.0.6"}, {"name": "b/b", "version": "2.0.7"}, {"name": "b/b", "version": "2.0.8"}, From 82502684b21bb179186ec392a2d86c8877636da3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Apr 2020 21:44:15 +0200 Subject: [PATCH 011/193] Allow testing for installed repo state --- tests/Composer/Test/InstallerTest.php | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 03d301164..f9e1b1793 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -184,7 +184,7 @@ class InstallerTest extends TestCase /** * @dataProvider getIntegrationTests */ - public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectResult) + public function testIntegration($file, $message, $condition, $composerConfig, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expect, $expectResult) { if ($condition) { eval('$res = '.$condition.';'); @@ -327,6 +327,23 @@ class InstallerTest extends TestCase $this->assertEquals($expectLock, $actualLock); } + if ($expectInstalled !== null) { + $actualInstalled = array(); + $dumper = new ArrayDumper(); + + foreach ($repositoryManager->getLocalRepository()->getCanonicalPackages() as $package) { + $package = $dumper->dump($package); + unset($package['version_normalized']); + $actualInstalled[] = $package; + } + + usort($actualInstalled, function ($a, $b) { + return strcmp($a['name'], $b['name']); + }); + + $this->assertSame($expectInstalled, $actualInstalled); + } + $installationManager = $composer->getInstallationManager(); $this->assertSame(rtrim($expect), implode("\n", $installationManager->getTrace())); @@ -355,6 +372,7 @@ class InstallerTest extends TestCase $installedDev = array(); $lock = array(); $expectLock = array(); + $expectInstalled = null; $expectResult = 0; $message = $testData['TEST']; @@ -393,6 +411,9 @@ class InstallerTest extends TestCase $expectLock = JsonFile::parseJson($testData['EXPECT-LOCK']); } } + if (!empty($testData['EXPECT-INSTALLED'])) { + $expectInstalled = JsonFile::parseJson($testData['EXPECT-INSTALLED']); + } $expectOutput = isset($testData['EXPECT-OUTPUT']) ? $testData['EXPECT-OUTPUT'] : null; $expect = $testData['EXPECT']; if (!empty($testData['EXPECT-EXCEPTION'])) { @@ -409,7 +430,7 @@ class InstallerTest extends TestCase die(sprintf('Test "%s" is not valid: '.$e->getMessage(), str_replace($fixturesDir.'/', '', $file))); } - $tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectOutput, $expect, $expectResult); + $tests[basename($file)] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $composer, $lock, $installed, $run, $expectLock, $expectInstalled, $expectOutput, $expect, $expectResult); } return $tests; @@ -427,6 +448,7 @@ class InstallerTest extends TestCase 'INSTALLED' => false, 'RUN' => true, 'EXPECT-LOCK' => false, + 'EXPECT-INSTALLED' => false, 'EXPECT-OUTPUT' => false, 'EXPECT-EXIT-CODE' => false, 'EXPECT-EXCEPTION' => false, From 17ed09be2ee33afaf9838f82ed01eccf6fd3c7db Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Apr 2020 21:46:56 +0200 Subject: [PATCH 012/193] Add failing test showing that packages fail to be installed if they match a previous alias which was not removed yet --- .../installer/update-alias-lock2.test | 84 +++++++++++++++++++ .../Test/Mock/InstallationManagerMock.php | 4 +- 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Fixtures/installer/update-alias-lock2.test diff --git a/tests/Composer/Test/Fixtures/installer/update-alias-lock2.test b/tests/Composer/Test/Fixtures/installer/update-alias-lock2.test new file mode 100644 index 000000000..b06ce3119 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-alias-lock2.test @@ -0,0 +1,84 @@ +--TEST-- +Updating an aliased package where the old alias matches the new package should not fail +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "a/a", "version": "1.10.x-dev", + "extra": { "branch-alias": { "dev-master": "1.10.x-dev" } }, + "source": { "type": "git", "url": "", "reference": "downgradedref" } + }, + { + "name": "a/a", "version": "dev-master", + "extra": { "branch-alias": { "dev-master": "2.x-dev" } }, + "source": { "type": "git", "url": "", "reference": "newref" } + } + ] + } + ], + "require": { + "a/a": "^1.0" + }, + "minimum-stability": "dev" +} +--LOCK-- +{ + "_": "outdated lock file, should not have to be loaded in an update", + "packages": [ + { + "name": "a/a", "version": "dev-master", + "extra": { "branch-alias": { "dev-master": "1.10.x-dev" } }, + "source": { "type": "git", "url": "", "reference": "installedref" } + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false +} +--INSTALLED-- +[ + { + "name": "a/a", "version": "dev-master", + "extra": { "branch-alias": { "dev-master": "1.10.x-dev" } }, + "source": { "type": "git", "url": "", "reference": "installedref" } + } +] +--RUN-- +update +--EXPECT-LOCK-- +{ + "packages": [ + { + "name": "a/a", "version": "1.10.x-dev", + "extra": { "branch-alias": { "dev-master": "1.10.x-dev" } }, + "source": { "type": "git", "url": "", "reference": "downgradedref" }, + "type": "library" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} +--EXPECT-INSTALLED-- +[ + { + "name": "a/a", "version": "1.10.x-dev", + "source": { "type": "git", "url": "", "reference": "downgradedref" }, + "type": "library", + "extra": { "branch-alias": { "dev-master": "1.10.x-dev" } } + } +] +--EXPECT-- +Marking a/a (1.10.x-dev installedref) as uninstalled, alias of a/a (dev-master installedref) +Downgrading a/a (dev-master installedref => 1.10.x-dev downgradedref) diff --git a/tests/Composer/Test/Mock/InstallationManagerMock.php b/tests/Composer/Test/Mock/InstallationManagerMock.php index 2d4a8781b..1d1dfef8c 100644 --- a/tests/Composer/Test/Mock/InstallationManagerMock.php +++ b/tests/Composer/Test/Mock/InstallationManagerMock.php @@ -66,7 +66,9 @@ class InstallationManagerMock extends InstallationManager $this->updated[] = array($operation->getInitialPackage(), $operation->getTargetPackage()); $this->trace[] = strip_tags((string) $operation); $repo->removePackage($operation->getInitialPackage()); - $repo->addPackage(clone $operation->getTargetPackage()); + if (!$repo->hasPackage($operation->getTargetPackage())) { + $repo->addPackage(clone $operation->getTargetPackage()); + } } public function uninstall(RepositoryInterface $repo, UninstallOperation $operation) From 3e2163de5cb0bfe3a70a480eff13d4a0a660c0cd Mon Sep 17 00:00:00 2001 From: Lucas D Hedding Date: Mon, 20 Apr 2020 13:50:42 -0600 Subject: [PATCH 013/193] #8809: expand context for post file download event --- src/Composer/Downloader/FileDownloader.php | 2 +- src/Composer/Plugin/PostFileDownloadEvent.php | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 77a310888..153f11e06 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -159,7 +159,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface } if ($eventDispatcher) { - $postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, $fileName, $checksum, $url['processed']); + $postFileDownloadEvent = new PostFileDownloadEvent(PluginEvents::POST_FILE_DOWNLOAD, $fileName, $checksum, $url['processed'], $package); $eventDispatcher->dispatch($postFileDownloadEvent->getName(), $postFileDownloadEvent); } diff --git a/src/Composer/Plugin/PostFileDownloadEvent.php b/src/Composer/Plugin/PostFileDownloadEvent.php index 081e64507..a4bc2ac4a 100644 --- a/src/Composer/Plugin/PostFileDownloadEvent.php +++ b/src/Composer/Plugin/PostFileDownloadEvent.php @@ -13,7 +13,7 @@ namespace Composer\Plugin; use Composer\EventDispatcher\Event; -use Composer\Util\RemoteFilesystem; +use Composer\Package\PackageInterface; /** * The post file download event. @@ -38,6 +38,11 @@ class PostFileDownloadEvent extends Event */ private $url; + /** + * @var \Composer\Package\PackageInterface + */ + private $package; + /** * Constructor. * @@ -45,13 +50,15 @@ class PostFileDownloadEvent extends Event * @param string $fileName The file name * @param string|null $checksum The checksum * @param string $url The processed url + * @param PackageInterface $package The package. */ - public function __construct($name, $fileName, $checksum, $url) + public function __construct($name, $fileName, $checksum, $url, PackageInterface $package) { parent::__construct($name); $this->fileName = $fileName; $this->checksum = $checksum; $this->url = $url; + $this->package = $package; } /** @@ -82,4 +89,14 @@ class PostFileDownloadEvent extends Event return $this->url; } + /** + * Get the package. + * + * @return \Composer\Package\PackageInterface + * The package. + */ + public function getPackage() { + return $this->package; + } + } From ba9d4793bc31d36c16fe275bc9197a780a031ed6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 20 Apr 2020 22:02:07 +0200 Subject: [PATCH 014/193] Fix transaction order --- src/Composer/DependencyResolver/Transaction.php | 2 +- tests/Composer/Test/DependencyResolver/TransactionTest.php | 2 +- .../partial-update-installs-from-lock-even-missing.test | 4 ++-- tests/Composer/Test/Fixtures/installer/update-alias.test | 2 +- .../installer/update-allow-list-with-dependencies-alias.test | 2 +- .../installer/update-downgrades-unstable-packages.test | 2 +- .../Fixtures/installer/update-no-dev-still-resolves-dev.test | 2 +- .../installer/updating-dev-from-lock-removes-old-deps.test | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index 6f7cc79fb..1e681a04f 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -295,7 +295,7 @@ class Transaction { $uninstOps = array(); foreach ($operations as $idx => $op) { - if ($op instanceof Operation\UninstallOperation) { + if ($op instanceof Operation\UninstallOperation || $op instanceof Operation\MarkAliasUninstalledOperation) { $uninstOps[] = $op; unset($operations[$idx]); } diff --git a/tests/Composer/Test/DependencyResolver/TransactionTest.php b/tests/Composer/Test/DependencyResolver/TransactionTest.php index 8b3e66b68..b5d209041 100644 --- a/tests/Composer/Test/DependencyResolver/TransactionTest.php +++ b/tests/Composer/Test/DependencyResolver/TransactionTest.php @@ -53,6 +53,7 @@ class TransactionTest extends TestCase $expectedOperations = array( array('job' => 'uninstall', 'package' => $packageC), array('job' => 'uninstall', 'package' => $packageE), + array('job' => 'markAliasUninstalled', 'package' => $packageEalias), array('job' => 'install', 'package' => $packageA0first), array('job' => 'update', 'from' => $packageB, 'to' => $packageBnew), array('job' => 'install', 'package' => $packageG), @@ -60,7 +61,6 @@ class TransactionTest extends TestCase array('job' => 'markAliasInstalled', 'package' => $packageFalias1), array('job' => 'markAliasInstalled', 'package' => $packageFalias2), array('job' => 'install', 'package' => $packageD), - array('job' => 'markAliasUninstalled', 'package' => $packageEalias), ); $transaction = new Transaction($presentPackages, $resultPackages); diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test b/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test index 7530ca862..9d971b384 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-installs-from-lock-even-missing.test @@ -97,9 +97,9 @@ update b/b "platform-dev": [] } --EXPECT-- +Marking a/a (2.1.x-dev oldmaster-a) as uninstalled, alias of a/a (dev-master oldmaster-a) +Marking b/b (2.1.x-dev oldmaster-b) as uninstalled, alias of b/b (dev-master oldmaster-b) Upgrading a/a (dev-master oldmaster-a => dev-master newmaster-a) Marking a/a (2.2.x-dev newmaster-a) as installed, alias of a/a (dev-master newmaster-a) Upgrading b/b (dev-master oldmaster-b => dev-master newmaster-b2) Marking b/b (2.3.x-dev newmaster-b2) as installed, alias of b/b (dev-master newmaster-b2) -Marking a/a (2.1.x-dev oldmaster-a) as uninstalled, alias of a/a (dev-master oldmaster-a) -Marking b/b (2.1.x-dev oldmaster-b) as uninstalled, alias of b/b (dev-master oldmaster-b) diff --git a/tests/Composer/Test/Fixtures/installer/update-alias.test b/tests/Composer/Test/Fixtures/installer/update-alias.test index 8da3d4d23..944eed259 100644 --- a/tests/Composer/Test/Fixtures/installer/update-alias.test +++ b/tests/Composer/Test/Fixtures/installer/update-alias.test @@ -33,5 +33,5 @@ Update aliased package to non-aliased version --RUN-- update --EXPECT-- -Upgrading a/a (dev-master master => dev-foo foo) Marking a/a (1.0.x-dev master) as uninstalled, alias of a/a (dev-master master) +Upgrading a/a (dev-master master => dev-foo foo) diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test index 2b68c6c69..193a5d85d 100644 --- a/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test +++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-with-dependencies-alias.test @@ -91,9 +91,9 @@ update new/pkg --with-all-dependencies "platform-dev": [] } --EXPECT-- +Marking current/dep2 (1.0.x-dev) as uninstalled, alias of current/dep2 (dev-foo) Marking current/dep (1.1.0) as installed, alias of current/dep (dev-master) Upgrading current/dep2 (dev-foo => dev-master) Marking current/dep2 (1.1.2) as installed, alias of current/dep2 (dev-master) Marking current/dep2 (2.x-dev) as installed, alias of current/dep2 (dev-master) Installing new/pkg (1.0.0) -Marking current/dep2 (1.0.x-dev) as uninstalled, alias of current/dep2 (dev-foo) diff --git a/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test b/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test index f0755d0d0..b213e480d 100644 --- a/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test +++ b/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test @@ -46,5 +46,5 @@ Downgrading from unstable to more stable package should work even if already ins --RUN-- update --EXPECT-- -Downgrading a/a (dev-master abcd => 1.0.0) Marking a/a (9999999-dev abcd) as uninstalled, alias of a/a (dev-master abcd) +Downgrading a/a (dev-master abcd => 1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test b/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test index 06cbc27c7..9844616f4 100644 --- a/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test +++ b/tests/Composer/Test/Fixtures/installer/update-no-dev-still-resolves-dev.test @@ -61,8 +61,8 @@ Updates with --no-dev but we still end up with a complete lock file including de update --no-dev --EXPECT-- Removing a/b (1.0.0) +Marking dev/pkg (1.0.x-dev old) as uninstalled, alias of dev/pkg (dev-master old) Upgrading a/a (1.0.0 => 1.0.1) Installing a/c (1.0.0) Upgrading dev/pkg (dev-master old => dev-master new) Marking dev/pkg (1.1.x-dev new) as installed, alias of dev/pkg (dev-master new) -Marking dev/pkg (1.0.x-dev old) as uninstalled, alias of dev/pkg (dev-master old) diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test index 3fb6654ab..ec92ea607 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test @@ -42,5 +42,5 @@ Installing locked dev packages should remove old dependencies install --EXPECT-- Removing a/dependency (dev-master ref) -Upgrading a/devpackage (dev-master oldref => dev-master newref) Marking a/dependency (9999999-dev ref) as uninstalled, alias of a/dependency (dev-master ref) +Upgrading a/devpackage (dev-master oldref => dev-master newref) From 2bf2e9fc603eac654e6e5f647fa6adf9a84f63fd Mon Sep 17 00:00:00 2001 From: Lucas D Hedding Date: Mon, 20 Apr 2020 14:40:25 -0600 Subject: [PATCH 015/193] fix failing tests --- src/Composer/Downloader/FileDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 153f11e06..21a3e858b 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -141,7 +141,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface ->then($accept, $reject); } - return $result->then(function ($result) use ($fileName, $checksum, $url, $eventDispatcher) { + return $result->then(function ($result) use ($fileName, $checksum, $url, $package, $eventDispatcher) { // in case of retry, the first call's Promise chain finally calls this twice at the end, // once with $result being the returned $fileName from $accept, and then once for every // failed request with a null result, which can be skipped. From e23710f92d6c6403ffe2c11a61f581035cb31c08 Mon Sep 17 00:00:00 2001 From: Yanick Witschi Date: Tue, 21 Apr 2020 08:59:36 +0200 Subject: [PATCH 016/193] Implemented php version check in autoload.php (#8546) --- composer.lock | 174 ++---------------- src/Composer/Autoload/AutoloadGenerator.php | 92 +++++++++ .../Test/Autoload/AutoloadGeneratorTest.php | 65 +++++++ .../autoload_real_files_by_dependency.php | 2 + .../Fixtures/autoload_real_functions.php | 2 + ...load_real_functions_with_include_paths.php | 2 + ...emoved_include_paths_and_autolad_files.php | 2 + .../Fixtures/autoload_real_include_path.php | 2 + .../Fixtures/autoload_real_target_dir.php | 2 + .../platform/no_extensions_required.php | 20 ++ .../Fixtures/platform/no_php_lower_bound.php | 20 ++ .../Fixtures/platform/no_php_required.php | 22 +++ .../Fixtures/platform/no_php_upper_bound.php | 20 ++ .../platform/specific_php_release.php | 20 ++ .../Autoload/Fixtures/platform/typical.php | 22 +++ 15 files changed, 305 insertions(+), 162 deletions(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/typical.php diff --git a/composer.lock b/composer.lock index 69e9fb589..d0a3ab768 100644 --- a/composer.lock +++ b/composer.lock @@ -60,35 +60,20 @@ "ssl", "tls" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.2.7" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], "time": "2020-04-08T08:27:21+00:00" }, { "name": "composer/semver", - "version": "2.0.x-dev", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "4df5ff3249f01018504939d66040d8d2b783d820" + "reference": "2fbee6b82f09aeaa3905444eb6652c554411fe55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4df5ff3249f01018504939d66040d8d2b783d820", - "reference": "4df5ff3249f01018504939d66040d8d2b783d820", + "url": "https://api.github.com/repos/composer/semver/zipball/2fbee6b82f09aeaa3905444eb6652c554411fe55", + "reference": "2fbee6b82f09aeaa3905444eb6652c554411fe55", "shasum": "" }, "require": { @@ -136,22 +121,7 @@ "validation", "versioning" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/2.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2020-03-11T13:41:23+00:00" + "time": "2020-04-16T13:02:51+00:00" }, { "name": "composer/spdx-licenses", @@ -211,11 +181,6 @@ "spdx", "validator" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/spdx-licenses/issues", - "source": "https://github.com/composer/spdx-licenses/tree/1.5.3" - }, "time": "2020-02-14T07:44:31+00:00" }, { @@ -260,17 +225,6 @@ "Xdebug", "performance" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/master" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - } - ], "time": "2020-03-01T12:26:26+00:00" }, { @@ -337,10 +291,6 @@ "json", "schema" ], - "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.9" - }, "time": "2019-09-25T14:49:45+00:00" }, { @@ -388,9 +338,6 @@ "psr", "psr-3" ], - "support": { - "source": "https://github.com/php-fig/log/tree/1.1.3" - }, "time": "2020-03-23T09:12:05+00:00" }, { @@ -428,6 +375,12 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", "time": "2016-03-07T13:46:50+00:00" }, @@ -478,10 +431,6 @@ "parser", "validator" ], - "support": { - "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.7.2" - }, "time": "2019-10-24T14:27:39+00:00" }, { @@ -526,10 +475,6 @@ "keywords": [ "phar" ], - "support": { - "issues": "https://github.com/Seldaek/phar-utils/issues", - "source": "https://github.com/Seldaek/phar-utils/tree/1.1.0" - }, "time": "2020-02-14T15:25:33+00:00" }, { @@ -591,9 +536,6 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/console/tree/v2.8.52" - }, "time": "2018-11-20T15:55:20+00:00" }, { @@ -651,9 +593,6 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/debug/tree/v2.8.50" - }, "time": "2018-11-11T11:18:13+00:00" }, { @@ -704,9 +643,6 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v2.8.52" - }, "time": "2018-11-11T11:18:13+00:00" }, { @@ -756,9 +692,6 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v2.8.50" - }, "time": "2018-11-11T11:18:13+00:00" }, { @@ -817,23 +750,6 @@ "polyfill", "portable" ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-02-27T09:26:54+00:00" }, { @@ -893,23 +809,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-03-09T19:04:49+00:00" }, { @@ -959,9 +858,6 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v2.8.50" - }, "time": "2018-11-11T11:18:13+00:00" } ], @@ -1018,10 +914,6 @@ "constructor", "instantiate" ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/master" - }, "time": "2015-06-14T21:17:01+00:00" }, { @@ -1071,10 +963,6 @@ "email": "mike.vanriel@naenius.com" } ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x" - }, "time": "2016-01-25T08:17:30+00:00" }, { @@ -1138,10 +1026,6 @@ "spy", "stub" ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, "time": "2020-03-05T15:02:03+00:00" }, { @@ -1206,10 +1090,6 @@ "compare", "equality" ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" - }, "time": "2017-01-29T09:50:25+00:00" }, { @@ -1262,10 +1142,6 @@ "keywords": [ "diff" ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/1.4" - }, "time": "2017-05-22T07:24:03+00:00" }, { @@ -1333,10 +1209,6 @@ "export", "exporter" ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/master" - }, "time": "2016-11-19T08:54:04+00:00" }, { @@ -1390,10 +1262,6 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" - }, "time": "2016-11-19T07:33:16+00:00" }, { @@ -1459,23 +1327,6 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v3.4.38" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2020-02-21T08:01:47+00:00" } ], @@ -1492,6 +1343,5 @@ "platform-dev": [], "platform-overrides": { "php": "5.3.9" - }, - "plugin-api-version": "2.0.0" + } } diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 371f3ed76..6500e8b24 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -19,6 +19,7 @@ use Composer\IO\IOInterface; use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; +use Composer\Semver\Constraint\Bound; use Composer\Util\Filesystem; use Composer\Script\ScriptEvents; use Composer\Util\PackageSorter; @@ -311,6 +312,7 @@ EOF; unlink($includeFilesFilePath); } $this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); + $this->filePutContentsIfModified($targetDir.'/platform_check.php', $this->getPlatformCheck($packageMap)); $this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); $this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion)); @@ -570,6 +572,94 @@ EOF; return $baseDir . (($path !== false) ? var_export($path, true) : ""); } + protected function getPlatformCheck($packageMap) + { + $lowestPhpVersion = Bound::zero(); + $highestPhpVersion = Bound::positiveInfinity(); + $requiredExtensions = array(); + + foreach ($packageMap as $item) { + list($package, $installPath) = $item; + foreach ($package->getRequires() as $link) { + if ('php' === $link->getTarget() && ($constraint = $link->getConstraint())) { + if ($constraint->getLowerBound()->compareTo($lowestPhpVersion, '>')) { + $lowestPhpVersion = $constraint->getLowerBound(); + } + if ($constraint->getUpperBound()->compareTo($highestPhpVersion, '<')) { + $highestPhpVersion = $constraint->getUpperBound(); + } + } + + if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { + $requiredExtensions[] = $match[1]; + } + } + } + + $requiredExtensions = array_values(array_unique($requiredExtensions)); + sort($requiredExtensions); + + $formatToPhpVersionId = function (Bound $bound) { + if ($bound->isZero()) { + return 0; + } + + if ($bound->isPositiveInfinity()) { + return 99999; + } + + $version = str_replace('-', '.', $bound->getVersion()); + $chunks = array_map('intval', explode('.', $version)); + return $chunks[0] * 10000 + $chunks[1] * 100 + $chunks[2]; + }; + + $formatToHumanReadable = function (Bound $bound) { + if ($bound->isZero()) { + return 0; + } + + if ($bound->isPositiveInfinity()) { + return 99999; + } + + $version = str_replace('-', '.', $bound->getVersion()); + $chunks = explode('.', $version); + $chunks = array_slice($chunks, 0, 3); + return implode('.', $chunks); + }; + + $lowestOperator = $lowestPhpVersion->isInclusive() ? '>=' : '>'; + $highestOperator = $highestPhpVersion->isInclusive() ? '<=' : '<'; + $lowestPhpVersionId = $formatToPhpVersionId($lowestPhpVersion); + $highestPhpVersionId = $formatToPhpVersionId($highestPhpVersion); + $lowestPhpVersion = $formatToHumanReadable($lowestPhpVersion); + $highestPhpVersion = $formatToHumanReadable($highestPhpVersion); + $requiredExtensions = var_export($requiredExtensions, true); + + return <<assertAutoloadFiles('classmap', $this->vendorDir.'/composer', 'classmap'); } + /** + * @dataProvider platformCheckProvider + */ + public function testGeneratesPlatformCheck(array $requires, $expectedFixture) + { + $package = new Package('a', '1.0', '1.0'); + $package->setRequires($requires); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue(array())); + + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); + + $this->assertFileContentEquals(__DIR__ . '/Fixtures/platform/' . $expectedFixture . '.php', $this->vendorDir . '/composer/platform_check.php'); + } + + public function platformCheckProvider() + { + $versionParser = new VersionParser(); + + return array( + 'Typical project requirements' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('^7.2')), + new Link('a', 'ext-xml', $versionParser->parseConstraints('*')), + new Link('a', 'ext-json', $versionParser->parseConstraints('*')) + ), + 'typical' + ), + 'No PHP lower bound' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('< 8')), + ), + 'no_php_lower_bound' + ), + 'No PHP upper bound' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('>= 7.2')), + ), + 'no_php_upper_bound' + ), + 'Specific PHP release version' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('^7.2.8')), + ), + 'specific_php_release' + ), + 'No PHP required' => array( + array( + new Link('a', 'ext-xml', $versionParser->parseConstraints('*')), + new Link('a', 'ext-json', $versionParser->parseConstraints('*')) + ), + 'no_php_required' + ), + 'No extensions required' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('^7.2')), + ), + 'no_extensions_required' + ) + ); + } + private function assertAutoloadFiles($name, $dir, $type = 'namespaces') { $a = __DIR__.'/Fixtures/autoload_'.$name.'.php'; 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 4e5e0a5cb..bbcf0f5e2 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 @@ -22,6 +22,8 @@ class ComposerAutoloaderInitFilesAutoloadOrder return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader')); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index 0f1201b8e..2474f5f0e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -22,6 +22,8 @@ class ComposerAutoloaderInitFilesAutoload return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); 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 fc457c406..0a3eeb7c3 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 @@ -22,6 +22,8 @@ class ComposerAutoloaderInitFilesAutoload return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); 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 b07825176..123db2a2d 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 @@ -22,6 +22,8 @@ class ComposerAutoloaderInitFilesAutoload return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); 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 12ac24108..bbe4562da 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -22,6 +22,8 @@ class ComposerAutoloaderInitIncludePath return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader')); 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 084d04f30..eeec70417 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -22,6 +22,8 @@ class ComposerAutoloaderInitTargetDir return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader')); diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php new file mode 100644 index 000000000..a81e90df6 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php @@ -0,0 +1,20 @@ += 70200 && PHP_VERSION_ID < 80000)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array_diff(array ( +), array_map('strtolower', get_loaded_extensions())); + +if (0 !== count($missingExtensions)) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if (0 !== count($issues)) { + die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); +} diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php new file mode 100644 index 000000000..c6f3082e5 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php @@ -0,0 +1,20 @@ += 0 && PHP_VERSION_ID < 80000)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array_diff(array ( +), array_map('strtolower', get_loaded_extensions())); + +if (0 !== count($missingExtensions)) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if (0 !== count($issues)) { + die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); +} diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php new file mode 100644 index 000000000..4f45a440f --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php @@ -0,0 +1,22 @@ += 0 && PHP_VERSION_ID < 99999)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 99999". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array_diff(array ( + 0 => 'json', + 1 => 'xml', +), array_map('strtolower', get_loaded_extensions())); + +if (0 !== count($missingExtensions)) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if (0 !== count($issues)) { + die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); +} diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php new file mode 100644 index 000000000..7ae0cf1e7 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php @@ -0,0 +1,20 @@ += 70200 && PHP_VERSION_ID < 99999)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 99999". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array_diff(array ( +), array_map('strtolower', get_loaded_extensions())); + +if (0 !== count($missingExtensions)) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if (0 !== count($issues)) { + die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); +} diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php b/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php new file mode 100644 index 000000000..d837add39 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php @@ -0,0 +1,20 @@ += 70208 && PHP_VERSION_ID < 80000)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.8" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array_diff(array ( +), array_map('strtolower', get_loaded_extensions())); + +if (0 !== count($missingExtensions)) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if (0 !== count($issues)) { + die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); +} diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php new file mode 100644 index 000000000..f2cafaf3b --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php @@ -0,0 +1,22 @@ += 70200 && PHP_VERSION_ID < 80000)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array_diff(array ( + 0 => 'json', + 1 => 'xml', +), array_map('strtolower', get_loaded_extensions())); + +if (0 !== count($missingExtensions)) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if (0 !== count($issues)) { + die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); +} From 6463ab9e49c35ce71452b8a8a536b6ad194fce60 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 21 Apr 2020 09:35:34 +0200 Subject: [PATCH 017/193] Optimize extension checks --- src/Composer/Autoload/AutoloadGenerator.php | 30 +++++++++++-------- .../platform/no_extensions_required.php | 9 +----- .../Fixtures/platform/no_php_lower_bound.php | 9 +----- .../Fixtures/platform/no_php_required.php | 11 ++++--- .../Fixtures/platform/no_php_upper_bound.php | 9 +----- .../platform/specific_php_release.php | 9 +----- .../Autoload/Fixtures/platform/typical.php | 11 ++++--- 7 files changed, 32 insertions(+), 56 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 6500e8b24..0a9e2928e 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -591,13 +591,13 @@ EOF; } if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { - $requiredExtensions[] = $match[1]; + $extension = var_export($match[1], true); + $requiredExtensions[$extension] = "extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; } } } - $requiredExtensions = array_values(array_unique($requiredExtensions)); - sort($requiredExtensions); + ksort($requiredExtensions); $formatToPhpVersionId = function (Bound $bound) { if ($bound->isZero()) { @@ -634,7 +634,19 @@ EOF; $highestPhpVersionId = $formatToPhpVersionId($highestPhpVersion); $lowestPhpVersion = $formatToHumanReadable($lowestPhpVersion); $highestPhpVersion = $formatToHumanReadable($highestPhpVersion); - $requiredExtensions = var_export($requiredExtensions, true); + $requiredExtensions = implode('', $requiredExtensions); + + if ('' !== $requiredExtensions) { + $requiredExtensions = <<= 70200 && PHP_VERSION_ID < 80000)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; } -$missingExtensions = array_diff(array ( -), array_map('strtolower', get_loaded_extensions())); - -if (0 !== count($missingExtensions)) { - $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); -} - -if (0 !== count($issues)) { +if ($issues) { die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php index c6f3082e5..5d1c368ad 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php @@ -8,13 +8,6 @@ if (!(PHP_VERSION_ID >= 0 && PHP_VERSION_ID < 80000)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; } -$missingExtensions = array_diff(array ( -), array_map('strtolower', get_loaded_extensions())); - -if (0 !== count($missingExtensions)) { - $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); -} - -if (0 !== count($issues)) { +if ($issues) { die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php index 4f45a440f..ed2ecf25e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php @@ -8,15 +8,14 @@ if (!(PHP_VERSION_ID >= 0 && PHP_VERSION_ID < 99999)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 99999". You are running ' . PHP_VERSION . '.'; } -$missingExtensions = array_diff(array ( - 0 => 'json', - 1 => 'xml', -), array_map('strtolower', get_loaded_extensions())); +$missingExtensions = array(); +extension_loaded('json') || $missingExtensions[] = 'json'; +extension_loaded('xml') || $missingExtensions[] = 'xml'; -if (0 !== count($missingExtensions)) { +if ($missingExtensions) { $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); } -if (0 !== count($issues)) { +if ($issues) { die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php index 7ae0cf1e7..f11188b31 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php @@ -8,13 +8,6 @@ if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 99999)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 99999". You are running ' . PHP_VERSION . '.'; } -$missingExtensions = array_diff(array ( -), array_map('strtolower', get_loaded_extensions())); - -if (0 !== count($missingExtensions)) { - $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); -} - -if (0 !== count($issues)) { +if ($issues) { die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php b/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php index d837add39..3ba4d2cc4 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php @@ -8,13 +8,6 @@ if (!(PHP_VERSION_ID >= 70208 && PHP_VERSION_ID < 80000)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.8" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; } -$missingExtensions = array_diff(array ( -), array_map('strtolower', get_loaded_extensions())); - -if (0 !== count($missingExtensions)) { - $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); -} - -if (0 !== count($issues)) { +if ($issues) { die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php index f2cafaf3b..45d85c8c2 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php @@ -8,15 +8,14 @@ if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 80000)) { $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; } -$missingExtensions = array_diff(array ( - 0 => 'json', - 1 => 'xml', -), array_map('strtolower', get_loaded_extensions())); +$missingExtensions = array(); +extension_loaded('json') || $missingExtensions[] = 'json'; +extension_loaded('xml') || $missingExtensions[] = 'xml'; -if (0 !== count($missingExtensions)) { +if ($missingExtensions) { $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); } -if (0 !== count($issues)) { +if ($issues) { die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); } From c41bb909ff60bd378c6a8c17fc11b7aeb412e705 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 11:18:02 +0200 Subject: [PATCH 018/193] Add platform check file in phars, fixes #8813 --- src/Composer/Compiler.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index 41e834993..f6d0cd5ac 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -139,6 +139,7 @@ class Compiler $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_static.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/platform_check.php')); if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) { $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php')); } From af18c2bd7d170095f194db185164aa8e526388a8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 14:50:11 +0200 Subject: [PATCH 019/193] Update semver, fix poolbuilder usage and optimize creation of filtering nameConstraints --- composer.lock | 151 ++++++++++++++++-- .../DependencyResolver/PoolBuilder.php | 19 ++- 2 files changed, 151 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index d0a3ab768..aa0f00386 100644 --- a/composer.lock +++ b/composer.lock @@ -60,6 +60,21 @@ "ssl", "tls" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.2.7" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], "time": "2020-04-08T08:27:21+00:00" }, { @@ -68,19 +83,20 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "2fbee6b82f09aeaa3905444eb6652c554411fe55" + "reference": "03c921a6ef6ac36ca3aa0113a726e1b473f0f528" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/2fbee6b82f09aeaa3905444eb6652c554411fe55", - "reference": "2fbee6b82f09aeaa3905444eb6652c554411fe55", + "url": "https://api.github.com/repos/composer/semver/zipball/03c921a6ef6ac36ca3aa0113a726e1b473f0f528", + "reference": "03c921a6ef6ac36ca3aa0113a726e1b473f0f528", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5" + "phpstan/phpstan": "^0.12.19", + "phpunit/phpunit": "^4.5 || ^5.0.5 || ^7" }, "type": "library", "extra": { @@ -121,7 +137,12 @@ "validation", "versioning" ], - "time": "2020-04-16T13:02:51+00:00" + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/master" + }, + "time": "2020-04-21T12:22:29+00:00" }, { "name": "composer/spdx-licenses", @@ -181,6 +202,11 @@ "spdx", "validator" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.3" + }, "time": "2020-02-14T07:44:31+00:00" }, { @@ -225,6 +251,17 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/master" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + } + ], "time": "2020-03-01T12:26:26+00:00" }, { @@ -291,6 +328,10 @@ "json", "schema" ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.9" + }, "time": "2019-09-25T14:49:45+00:00" }, { @@ -338,6 +379,9 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.3" + }, "time": "2020-03-23T09:12:05+00:00" }, { @@ -375,12 +419,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" - } - ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", "time": "2016-03-07T13:46:50+00:00" }, @@ -431,6 +469,10 @@ "parser", "validator" ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.7.2" + }, "time": "2019-10-24T14:27:39+00:00" }, { @@ -475,6 +517,10 @@ "keywords": [ "phar" ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.1.0" + }, "time": "2020-02-14T15:25:33+00:00" }, { @@ -536,6 +582,9 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v2.8.52" + }, "time": "2018-11-20T15:55:20+00:00" }, { @@ -593,6 +642,9 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug/tree/v2.8.50" + }, "time": "2018-11-11T11:18:13+00:00" }, { @@ -643,6 +695,9 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v2.8.52" + }, "time": "2018-11-11T11:18:13+00:00" }, { @@ -692,6 +747,9 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v2.8.50" + }, "time": "2018-11-11T11:18:13+00:00" }, { @@ -750,6 +808,23 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-02-27T09:26:54+00:00" }, { @@ -809,6 +884,23 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-03-09T19:04:49+00:00" }, { @@ -858,6 +950,9 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v2.8.50" + }, "time": "2018-11-11T11:18:13+00:00" } ], @@ -914,6 +1009,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/master" + }, "time": "2015-06-14T21:17:01+00:00" }, { @@ -963,6 +1062,10 @@ "email": "mike.vanriel@naenius.com" } ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x" + }, "time": "2016-01-25T08:17:30+00:00" }, { @@ -1026,6 +1129,10 @@ "spy", "stub" ], + "support": { + "issues": "https://github.com/phpspec/prophecy/issues", + "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" + }, "time": "2020-03-05T15:02:03+00:00" }, { @@ -1090,6 +1197,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" + }, "time": "2017-01-29T09:50:25+00:00" }, { @@ -1142,6 +1253,10 @@ "keywords": [ "diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/1.4" + }, "time": "2017-05-22T07:24:03+00:00" }, { @@ -1209,6 +1324,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/master" + }, "time": "2016-11-19T08:54:04+00:00" }, { @@ -1262,6 +1381,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" + }, "time": "2016-11-19T07:33:16+00:00" }, { @@ -1327,6 +1450,9 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/v3.4.38" + }, "time": "2020-02-21T08:01:47+00:00" } ], @@ -1343,5 +1469,6 @@ "platform-dev": [], "platform-overrides": { "php": "5.3.9" - } + }, + "plugin-api-version": "2.0.0" } diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index 44f7e2f45..f03a6bf24 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -122,7 +122,7 @@ class PoolBuilder } $loadNames[$packageName] = $constraint; - $this->nameConstraints[$packageName] = $constraint ? new MultiConstraint(array($constraint), false) : null; + $this->nameConstraints[$packageName] = $constraint && !($constraint instanceof EmptyConstraint) ? array($constraint) : null; } // clean up loadNames for anything we manually marked loaded above @@ -159,11 +159,17 @@ class PoolBuilder } // filter packages according to all the require statements collected for each package + $nameConstraints = array(); + foreach ($this->nameConstraints as $name => $constraints) { + if (is_array($constraints)) { + $nameConstraints[$name] = MultiConstraint::create(array_values(array_unique($constraints)), false); + } + } foreach ($this->packages as $i => $package) { // we check all alias related packages at once, so no need to check individual aliases // isset also checks non-null value - if (!$package instanceof AliasPackage && isset($this->nameConstraints[$package->getName()])) { - $constraint = $this->nameConstraints[$package->getName()]; + if (!$package instanceof AliasPackage && isset($nameConstraints[$package->getName()])) { + $constraint = $nameConstraints[$package->getName()]; $aliasedPackages = array($i => $package); if (isset($this->aliasMap[spl_object_hash($package)])) { @@ -270,10 +276,9 @@ class PoolBuilder $linkConstraint = $link->getConstraint(); if ($linkConstraint && !($linkConstraint instanceof EmptyConstraint)) { if (!array_key_exists($require, $this->nameConstraints)) { - $this->nameConstraints[$require] = new MultiConstraint(array($linkConstraint), false); - } elseif ($this->nameConstraints[$require]) { - // TODO addConstraint function? - $this->nameConstraints[$require] = new MultiConstraint(array_merge(array($linkConstraint), $this->nameConstraints[$require]->getConstraints()), false); + $this->nameConstraints[$require] = array($linkConstraint); + } elseif (is_array($this->nameConstraints[$require])) { + $this->nameConstraints[$require][] = $linkConstraint; } // else it is null and should stay null } else { From da24d0a7d9d96ad2a98081ada6db4dc440d85631 Mon Sep 17 00:00:00 2001 From: Fabien Villepinte Date: Tue, 21 Apr 2020 14:56:51 +0200 Subject: [PATCH 020/193] Check syntax of PHP files in src --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 22c4c9db5..d1230605e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ matrix: include: - php: 5.3 dist: precise + env: + - PHP_LINT=1 - php: 5.4 dist: trusty - php: 5.5 @@ -71,7 +73,7 @@ before_script: - git config --global user.email travis@example.com script: - - ( set -e; for f in $(find src/ -name *.php); do php -l "$f" || exit 1; done ) + - if [[ "$PHP_LINT" == "1" ]]; then find src/ -type f -name '*.php' -print0 | xargs -0 -L1 -P4 -- php -l -f; fi - if [[ $PHPSTAN == "1" ]]; then bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --no-update && bin/composer update phpstan/* phpunit/* sebastian/* --with-all-dependencies && From 3c09e4021ef4ff449b189f0f80388b5227b01caa Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 21 Apr 2020 15:04:47 +0200 Subject: [PATCH 021/193] fixed typo --- src/Composer/Package/PackageInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 18f72960f..cda65f5cb 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -283,7 +283,7 @@ interface PackageInterface * directories for autoloading using the type specified. * * @return array Mapping of autoloading rules - * @psalm-return array{psr-0?: array, psr-4?: array, classmap?: list, file?: list} + * @psalm-return array{psr-0?: array, psr-4?: array, classmap?: list, files?: list} */ public function getAutoload(); @@ -296,7 +296,7 @@ interface PackageInterface * directories for autoloading using the type specified. * * @return array Mapping of dev autoloading rules - * @psalm-return array{psr-0?: array, psr-4?: array, classmap?: list, file?: list} + * @psalm-return array{psr-0?: array, psr-4?: array, classmap?: list, files?: list} */ public function getDevAutoload(); From afa18f20925f5fc222dd70780e937a260364a108 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 15:13:01 +0200 Subject: [PATCH 022/193] Fix semver usage --- tests/Composer/Test/DependencyResolver/SolverTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 5fca6bfc2..423a4bf1b 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -25,6 +25,7 @@ use Composer\Repository\InstalledArrayRepository; use Composer\Repository\RepositorySet; use Composer\Test\TestCase; use Composer\Semver\Constraint\MultiConstraint; +use Composer\Semver\Constraint\EmptyConstraint; class SolverTest extends TestCase { @@ -378,7 +379,7 @@ class SolverTest extends TestCase { $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageB->setReplaces(array('a' => new Link('B', 'A', new MultiConstraint(array())))); + $packageB->setReplaces(array('a' => new Link('B', 'A', new EmptyConstraint()))); $this->reposComplete(); From 2c8a4a1b938fc980f4a3923d9678a536b15ba030 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 15:25:35 +0200 Subject: [PATCH 023/193] Add platform-check config option to disable platform_check.php generation, and disable it for Composer --- composer.json | 3 ++- doc/06-config.md | 5 ++++ res/composer-schema.json | 4 ++++ src/Composer/Autoload/AutoloadGenerator.php | 23 +++++++++++++++---- src/Composer/Command/ConfigCommand.php | 1 + src/Composer/Compiler.php | 4 +++- src/Composer/Config.php | 1 + .../Test/Autoload/AutoloadGeneratorTest.php | 3 +++ 8 files changed, 38 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index e0ced56f3..824d20330 100644 --- a/composer.json +++ b/composer.json @@ -52,7 +52,8 @@ "config": { "platform": { "php": "5.3.9" - } + }, + "platform-check": false }, "extra": { "branch-alias": { diff --git a/doc/06-config.md b/doc/06-config.md index 603de014a..a79f3fe5d 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -307,4 +307,9 @@ in the composer home, cache, and data directories. Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` file. +## platform-check + +Defaults to `true`. If set to `false`, Composer will not create and require a +`platform_check.php` file as part of the autoloader bootstrap. + ← [Repositories](05-repositories.md) | [Community](07-community.md) → diff --git a/res/composer-schema.json b/res/composer-schema.json index 407817b78..47666a843 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -306,6 +306,10 @@ "lock": { "type": "boolean", "description": "Defaults to true. If set to false, Composer will not create a composer.lock file." + }, + "platform-check": { + "type": "boolean", + "description": "Defaults to true. If set to false, Composer will not create and require a platform_check.php file as part of the autoloader bootstrap." } } }, diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 0a9e2928e..386c2717a 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -312,9 +312,14 @@ EOF; unlink($includeFilesFilePath); } $this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); - $this->filePutContentsIfModified($targetDir.'/platform_check.php', $this->getPlatformCheck($packageMap)); + $checkPlatform = $config->get('platform-check'); + if ($checkPlatform) { + $this->filePutContentsIfModified($targetDir.'/platform_check.php', $this->getPlatformCheck($packageMap)); + } elseif (file_exists($targetDir.'/platform_check.php')) { + unlink($targetDir.'/platform_check.php'); + } $this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); - $this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion)); + $this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform)); $this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); $this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE'); @@ -687,7 +692,7 @@ return ComposerAutoloaderInit$suffix::getLoader(); AUTOLOAD; } - protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion = 70000) + protected function getAutoloadRealFile($useClassMap, $useIncludePath, $targetDirLoader, $useIncludeFiles, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform) { $file = <<
array($booleanValidator, $booleanNormalizer), 'htaccess-protect' => array($booleanValidator, $booleanNormalizer), 'lock' => array($booleanValidator, $booleanNormalizer), + 'platform-check' => array($booleanValidator, $booleanNormalizer), ); $multiConfigValues = array( 'github-protocols' => array( diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index f6d0cd5ac..fdae04720 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -139,7 +139,9 @@ class Compiler $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_static.php')); - $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/platform_check.php')); + if (file_exists(__DIR__.'/../../vendor/composer/platform_check.php')) { + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/platform_check.php')); + } if (file_exists(__DIR__.'/../../vendor/composer/include_paths.php')) { $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/include_paths.php')); } diff --git a/src/Composer/Config.php b/src/Composer/Config.php index e57d21cab..455ae6db7 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -64,6 +64,7 @@ class Config 'htaccess-protect' => true, 'use-github-api' => true, 'lock' => true, + 'platform-check' => true, // valid keys without defaults (auth config stuff): // bitbucket-oauth // github-oauth diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index d756b2994..f9de00199 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -101,6 +101,9 @@ class AutoloadGeneratorTest extends TestCase 'vendor-dir' => function () use ($that) { return $that->vendorDir; }, + 'platform-check' => function () { + return true; + }, ); $this->config->expects($this->atLeastOnce()) From 62cd332a572ee8a8bc8557e8cb2b050b4f50d9c7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 15:30:46 +0200 Subject: [PATCH 024/193] Go back to stable composer/semver --- composer.json | 2 +- composer.lock | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index 824d20330..8331bc8a1 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require": { "php": "^5.3.2 || ^7.0", "composer/ca-bundle": "^1.0", - "composer/semver": "^2.0@dev", + "composer/semver": "^2.0", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", diff --git a/composer.lock b/composer.lock index aa0f00386..7096a1a36 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a0a9399315ac0b612d4296b8df745112", + "content-hash": "0a043906e568eec0b2608c6684018480", "packages": [ { "name": "composer/ca-bundle", @@ -79,16 +79,16 @@ }, { "name": "composer/semver", - "version": "dev-master", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "03c921a6ef6ac36ca3aa0113a726e1b473f0f528" + "reference": "0ec124f57c7e23925c006cbad0de853e3aec3ba2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/03c921a6ef6ac36ca3aa0113a726e1b473f0f528", - "reference": "03c921a6ef6ac36ca3aa0113a726e1b473f0f528", + "url": "https://api.github.com/repos/composer/semver/zipball/0ec124f57c7e23925c006cbad0de853e3aec3ba2", + "reference": "0ec124f57c7e23925c006cbad0de853e3aec3ba2", "shasum": "" }, "require": { @@ -140,9 +140,9 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/master" + "source": "https://github.com/composer/semver/tree/2.0.0" }, - "time": "2020-04-21T12:22:29+00:00" + "time": "2020-04-21T13:19:12+00:00" }, { "name": "composer/spdx-licenses", @@ -1458,9 +1458,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "composer/semver": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From e25bea03437e6371f791c3b492b6e6b8e22a0c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Sun, 1 Mar 2020 17:34:52 +0100 Subject: [PATCH 025/193] Enhancement: Run jobs on GitHub Actions --- .github/workflows/continuous-integration.yml | 77 ++++++++++++++++++++ README.md | 1 + 2 files changed, 78 insertions(+) create mode 100644 .github/workflows/continuous-integration.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml new file mode 100644 index 000000000..1bda77ec8 --- /dev/null +++ b/.github/workflows/continuous-integration.yml @@ -0,0 +1,77 @@ +# https://help.github.com/en/categories/automating-your-workflow-with-github-actions + +name: "Continuous Integration" + +on: + - "pull_request" + +env: + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist --optimize-autoloader" + SYMFONY_PHPUNIT_VERSION: "" + +jobs: + tests: + name: "Tests" + + runs-on: "ubuntu-latest" + + strategy: + matrix: + php-version: + - "5.3" + - "5.4" + - "5.5" + - "5.6" + - "7.0" + - "7.1" + - "7.2" + - "7.3" + - "7.4" + + dependencies: + - "locked" + - "highest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + ini-values: "memory_limit=-1" + php-version: "${{ matrix.php-version }}" + + - name: "Determine composer cache directory" + id: "determine-composer-cache-directory" + run: "echo \"::set-output name=directory::$(composer config cache-dir)\"" + + - name: "Cache dependencies installed with composer" + uses: "actions/cache@v1" + with: + path: "${{ steps.determine-composer-cache-directory.outputs.directory }}" + key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}" + restore-keys: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}" + + - name: "Install highest dependencies from composer.json using composer binary provided by system" + if: "matrix.dependencies == 'highest'" + run: "composer update ${{ env.COMPOSER_FLAGS }}" + + - name: "Install dependencies from composer.lock using composer binary provided by system" + if: "matrix.dependencies == 'locked'" + run: "composer install ${{ env.COMPOSER_FLAGS }}" + + - name: "Install dependencies from composer.lock using composer binary from source" + if: "matrix.dependencies == 'locked'" + run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" + + - name: "Prepare git environment" + run: "git config --global user.name composer && git config --global user.email composer@example.com" + + - name: "Set SYMFONY_PHPUNIT_VERSION environment variable" + if: "matrix.php-version == '7.4'" + run: "echo \"::set-env name=SYMFONY_PHPUNIT_VERSION::7.5\"" + + - name: "Run tests" + run: "vendor/bin/simple-phpunit" diff --git a/README.md b/README.md index c538df803..1134e088c 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Composer helps you declare, manage, and install dependencies of PHP projects. See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation. +[![Continuous Integration](https://github.com/composer/composer.svg/workflows/Continuous%20Integration/badge.svg?branch=master)](https://github.com/composer/composer.svg/actions) [![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer) Installation / Usage From bf39fab82de651263e683cea4930ef4beab9794d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 23:01:20 +0200 Subject: [PATCH 026/193] Add test for root alias presence in lock --- .../Fixtures/installer/alias-in-lock.test | 65 +++++++++++++++++++ .../installer/alias-with-reference.test | 33 +++++++++- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Fixtures/installer/alias-in-lock.test diff --git a/tests/Composer/Test/Fixtures/installer/alias-in-lock.test b/tests/Composer/Test/Fixtures/installer/alias-in-lock.test new file mode 100644 index 000000000..25660566f --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/alias-in-lock.test @@ -0,0 +1,65 @@ +--TEST-- +Root-defined aliases end up in lock file only if required to solve deps +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "a/aliased", "version": "3.0.2" + }, + { + "name": "a/aliased2", "version": "3.0.2" + }, + { + "name": "b/requirer", "version": "1.0.0", + "require": { "a/aliased": "^3.0.3", "a/aliased2": "^3.0.0" } + } + ] + } + ], + "require": { + "a/aliased": "3.0.2 as 3.0.3", + "a/aliased2": "3.0.2 as 3.0.3", + "b/requirer": "*" + } +} +--RUN-- +update +--EXPECT-LOCK-- +{ + "packages": [ + { + "name": "a/aliased", "version": "3.0.2", + "type": "library" + }, + { + "name": "a/aliased2", "version": "3.0.2", + "type": "library" + }, + { + "name": "b/requirer", "version": "1.0.0", + "require": { "a/aliased": "^3.0.3", "a/aliased2": "^3.0.0" }, + "type": "library" + } + ], + "packages-dev": [], + "aliases": [{ + "package": "a/aliased", + "version": "3.0.2.0", + "alias": "3.0.3", + "alias_normalized": "3.0.3.0" + }], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} +--EXPECT-- +Installing a/aliased2 (3.0.2) +Installing a/aliased (3.0.2) +Marking a/aliased (3.0.3) as installed, alias of a/aliased (3.0.2) +Installing b/requirer (1.0.0) diff --git a/tests/Composer/Test/Fixtures/installer/alias-with-reference.test b/tests/Composer/Test/Fixtures/installer/alias-with-reference.test index df25f7478..451e1f8b9 100644 --- a/tests/Composer/Test/Fixtures/installer/alias-with-reference.test +++ b/tests/Composer/Test/Fixtures/installer/alias-with-reference.test @@ -24,7 +24,38 @@ Aliases of referenced packages work } } --RUN-- -install +update +--EXPECT-LOCK-- +{ + "packages": [ + { + "name": "a/aliased", "version": "dev-master", + "source": { "reference": "abcd", "type": "git", "url": "" }, + "type": "library" + }, + { + "name": "b/requirer", "version": "1.0.0", + "require": { "a/aliased": "1.0.0" }, + "source": { "reference": "1.0.0", "type": "git", "url": "" }, + "type": "library" + } + ], + "packages-dev": [], + "aliases": [{ + "package": "a/aliased", + "version": "dev-master", + "alias": "1.0.0", + "alias_normalized": "1.0.0.0" + }], + "minimum-stability": "stable", + "stability-flags": { + "a/aliased": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} --EXPECT-- Installing a/aliased (dev-master abcd) Marking a/aliased (1.0.0) as installed, alias of a/aliased (dev-master abcd) From a42c6ceff3846c681d65322304f8b0fa8be85eec Mon Sep 17 00:00:00 2001 From: Pierre Grimaud Date: Wed, 22 Apr 2020 00:43:34 +0200 Subject: [PATCH 027/193] Fix typos --- UPGRADE-2.0.md | 2 +- src/Composer/Repository/ComposerRepository.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 10e5a587f..3a5928efc 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -24,7 +24,7 @@ - packages are now wrapped into a `"packages"` top level key instead of the whole file being the package array - packages now contain an `"installed-path"` key which lists where they were installed - there is a top level `"dev"` key which stores whether dev requirements were installed or not -- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overriden by listeners anymore +- `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overridden by listeners anymore - `IOInterface` now extends PSR-3's `LoggerInterface`, and has new `writeRaw` + `writeErrorRaw` methods - `RepositoryInterface` changes: - A new `loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags)` function was added for use during pool building diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index ec7f9346d..5e9e0c5b3 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -818,7 +818,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $this->hasPartialPackages = !empty($data['packages']) && is_array($data['packages']); } - // metadata-url indiates V2 repo protocol so it takes over from all the V1 types + // metadata-url indicates V2 repo protocol so it takes over from all the V1 types // V2 only has lazyProviders and possibly partial packages, but no ability to process anything else, // V2 also supports async loading if (!empty($data['metadata-url'])) { From f9032087bab1c38fa1d715aea54bd3e55678b6a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Wed, 22 Apr 2020 07:57:06 +0200 Subject: [PATCH 028/193] Fix: Install ext-intl --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 1bda77ec8..bd992949e 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -40,6 +40,7 @@ jobs: uses: "shivammathur/setup-php@v2" with: coverage: "none" + extensions: "intl" ini-values: "memory_limit=-1" php-version: "${{ matrix.php-version }}" From ef8c410098f9d29c25d46c00ee41140a0ee5fa8c Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Tue, 21 Apr 2020 16:42:22 +0200 Subject: [PATCH 029/193] GithubActions: build master branch + pull requests --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index bd992949e..dede30fdd 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -3,6 +3,7 @@ name: "Continuous Integration" on: + - "push" - "pull_request" env: From acef74d1abffeb20a934709c7fde964aae742444 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 08:28:14 +0200 Subject: [PATCH 030/193] Attempt at optimizing the job matrix --- .github/workflows/continuous-integration.yml | 33 +++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index dede30fdd..14c39e8d3 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -7,17 +7,19 @@ on: - "pull_request" env: - COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist --optimize-autoloader" + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" SYMFONY_PHPUNIT_VERSION: "" jobs: tests: name: "Tests" - runs-on: "ubuntu-latest" + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest] + dependencies: [locked] php-version: - "5.3" - "5.4" @@ -28,10 +30,22 @@ jobs: - "7.2" - "7.3" - "7.4" - - dependencies: - - "locked" - - "highest" + include: + - php-version: 5.3 + os: ubuntu-latest + dependencies: highest + - php-version: 5.3 + os: ubuntu-latest + dependencies: lowest + - php-version: 7.4 + os: ubuntu-latest + dependencies: highest + - php-version: 7.4 + os: windows-latest + dependencies: locked + - php-version: 7.4 + os: macos-latest + dependencies: locked steps: - name: "Checkout" @@ -60,12 +74,15 @@ jobs: if: "matrix.dependencies == 'highest'" run: "composer update ${{ env.COMPOSER_FLAGS }}" + - name: "Install lowest dependencies from composer.lock using composer binary provided by system" + if: "matrix.dependencies == 'lowest'" + run: "composer install ${{ env.COMPOSER_FLAGS }} --prefer-lowest" + - name: "Install dependencies from composer.lock using composer binary provided by system" if: "matrix.dependencies == 'locked'" run: "composer install ${{ env.COMPOSER_FLAGS }}" - - name: "Install dependencies from composer.lock using composer binary from source" - if: "matrix.dependencies == 'locked'" + - name: "Run install again using composer binary from source" run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" - name: "Prepare git environment" From ebd30cf8310c010eca7a980fa0811d209fdfc3e3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 08:30:44 +0200 Subject: [PATCH 031/193] Use correct command.. --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 14c39e8d3..3a794c5a5 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -76,7 +76,7 @@ jobs: - name: "Install lowest dependencies from composer.lock using composer binary provided by system" if: "matrix.dependencies == 'lowest'" - run: "composer install ${{ env.COMPOSER_FLAGS }} --prefer-lowest" + run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest" - name: "Install dependencies from composer.lock using composer binary provided by system" if: "matrix.dependencies == 'locked'" From a849e2b24426e9434c63cac20890d3d842d28a57 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:27:58 +0200 Subject: [PATCH 032/193] Add linting, php8, release builds to github actions --- .github/workflows/continuous-integration.yml | 36 ++++++++----- .github/workflows/lint.yml | 38 +++++++++++++ .github/workflows/release.yml | 57 ++++++++++++++++++++ 3 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 3a794c5a5..d4dc5f01a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -1,10 +1,14 @@ -# https://help.github.com/en/categories/automating-your-workflow-with-github-actions - name: "Continuous Integration" on: - - "push" - - "pull_request" + push: + paths-ignore: + - '.github/**' + - 'doc/**' + pull_request: + paths-ignore: + - '.github/**' + - 'doc/**' env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" @@ -12,14 +16,13 @@ env: jobs: tests: - name: "Tests" + name: "CI" runs-on: ${{ matrix.os }} + continue-on-error: ${{ matrix.experimental }} strategy: matrix: - os: [ubuntu-latest] - dependencies: [locked] php-version: - "5.3" - "5.4" @@ -30,22 +33,28 @@ jobs: - "7.2" - "7.3" - "7.4" + dependencies: [locked] + os: [ubuntu-latest] include: - php-version: 5.3 - os: ubuntu-latest dependencies: highest + os: ubuntu-latest - php-version: 5.3 - os: ubuntu-latest dependencies: lowest - - php-version: 7.4 os: ubuntu-latest + - php-version: 7.4 dependencies: highest + os: ubuntu-latest - php-version: 7.4 os: windows-latest dependencies: locked - php-version: 7.4 os: macos-latest dependencies: locked + - php-version: 8.0 + dependencies: highest + os: ubuntu-latest + experimental: true steps: - name: "Checkout" @@ -72,9 +81,9 @@ jobs: - name: "Install highest dependencies from composer.json using composer binary provided by system" if: "matrix.dependencies == 'highest'" - run: "composer update ${{ env.COMPOSER_FLAGS }}" + run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }}" - - name: "Install lowest dependencies from composer.lock using composer binary provided by system" + - name: "Install lowest dependencies from composer.json using composer binary provided by system" if: "matrix.dependencies == 'lowest'" run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest" @@ -85,6 +94,9 @@ jobs: - name: "Run install again using composer binary from source" run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" + - name: "Validate composer.json" + run: "bin/composer validate" + - name: "Prepare git environment" run: "git config --global user.name composer && git config --global user.email composer@example.com" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..ba699953e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,38 @@ +name: "PHP Lint" + +on: + push: + paths-ignore: + - '.github/**' + - 'doc/**' + pull_request: + paths-ignore: + - '.github/**' + - 'doc/**' + +jobs: + tests: + name: "Lint" + + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: + - "5.3" + - "7.4" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "intl" + ini-values: "memory_limit=-1" + php-version: "${{ matrix.php-version }}" + + - name: "Lint PHP files" + run: "find src/ -type f -name '*.php' -print0 | xargs -0 -L1 -P4 -- php -l -f" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..3e5f37fef --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,57 @@ +name: "Release" + +on: + push: + - tags + +env: + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" + +jobs: + build: + name: Upload Release Asset + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "intl" + ini-values: "memory_limit=-1" + php-version: "7.4" + + - name: "Install dependencies from composer.lock using composer binary provided by system" + run: "composer install ${{ env.COMPOSER_FLAGS }}" + + - name: "Run install again using composer binary from source" + run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" + + - name: "Validate composer.json" + run: "bin/composer validate" + + - name: Build phar file + run: "php -d phar.readonly=0 bin/compile" + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: true + body: TODO + + - name: Upload phar + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./composer.phar + asset_name: composer.phar + asset_content_type: application/octet-stream From ac3fcfc9ec0caf9d8f244aa61e509f814ee14935 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:30:34 +0200 Subject: [PATCH 033/193] Fix build --- .github/workflows/release.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3e5f37fef..8c4ca1d40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,8 @@ name: "Release" on: push: - - tags + tags: + - * env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" From 9a0e6e68853c80378c0f21cf8dee2c3cc72efd7f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:31:04 +0200 Subject: [PATCH 034/193] Fix build --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8c4ca1d40..a67b4e6e5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: "Release" on: push: tags: - - * + - "*" env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" From eeaa099021ba04c935a4ab80dc898c621e3b7136 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:32:50 +0200 Subject: [PATCH 035/193] Stop ignoring build changes to trigger build, that was smart :D --- .github/workflows/continuous-integration.yml | 2 -- .github/workflows/lint.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d4dc5f01a..7fe67829a 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -3,11 +3,9 @@ name: "Continuous Integration" on: push: paths-ignore: - - '.github/**' - 'doc/**' pull_request: paths-ignore: - - '.github/**' - 'doc/**' env: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ba699953e..0db4f34cb 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -3,11 +3,9 @@ name: "PHP Lint" on: push: paths-ignore: - - '.github/**' - 'doc/**' pull_request: paths-ignore: - - '.github/**' - 'doc/**' jobs: From f51810f1c9f3694e9d615323222bb52f09e0c7c4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:33:55 +0200 Subject: [PATCH 036/193] Fix continue-on-error failure --- .github/workflows/continuous-integration.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7fe67829a..97e2b6558 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -33,22 +33,28 @@ jobs: - "7.4" dependencies: [locked] os: [ubuntu-latest] + experimental: [false] include: - php-version: 5.3 dependencies: highest os: ubuntu-latest + experimental: false - php-version: 5.3 dependencies: lowest os: ubuntu-latest + experimental: false - php-version: 7.4 dependencies: highest os: ubuntu-latest + experimental: false - php-version: 7.4 os: windows-latest dependencies: locked + experimental: false - php-version: 7.4 os: macos-latest dependencies: locked + experimental: false - php-version: 8.0 dependencies: highest os: ubuntu-latest From 4ccd8e10810bd63c8b2d01c1a1a7c5f26e04d1f5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:37:04 +0200 Subject: [PATCH 037/193] Mark 7.4 latest deps build experimental for now --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 97e2b6558..89eafea47 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -46,7 +46,7 @@ jobs: - php-version: 7.4 dependencies: highest os: ubuntu-latest - experimental: false + experimental: true # TODO fix build errors there if possible - php-version: 7.4 os: windows-latest dependencies: locked From ca25ff8b0d7e8931161dfccae9b0237be0b54f95 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:40:35 +0200 Subject: [PATCH 038/193] Remove travis/appveyor configs --- .travis.yml | 76 ---------------------------------------------------- appveyor.yml | 33 ----------------------- 2 files changed, 109 deletions(-) delete mode 100644 .travis.yml delete mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 64ee75f3a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,76 +0,0 @@ -language: php - -dist: trusty - -git: - depth: 5 - -cache: - directories: - - $HOME/.composer/cache - -addons: - apt: - packages: - - parallel - -matrix: - include: - - php: 5.3 - dist: precise - - php: 5.4 - - php: 5.5 - - php: 5.6 - - php: 7.0 - - php: 7.1 - - php: 7.2 - - php: 7.3 - - php: nightly - - php: 7.4 - env: - - deps=high - - SYMFONY_PHPUNIT_VERSION=7.5 - fast_finish: true - allow_failures: - - php: nightly - -before_install: - # disable Xdebug if available - - phpenv config-rm xdebug.ini || echo "xdebug not available" - # disable default memory limit - - export INI=~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - - echo memory_limit = -1 >> $INI - - composer validate - -install: - # flags to pass to install - - flags="--ansi --prefer-dist --no-interaction --optimize-autoloader --no-suggest --no-progress" - # update deps to latest in case of high deps build - - if [ "$deps" == "high" ]; then composer config platform.php 7.2.4; composer update $flags; fi - # install dependencies using system provided composer binary - - composer install $flags - # install dependencies using composer from source - - bin/composer install $flags - -before_script: - # make sure git tests do not complain about user/email not being set - - git config --global user.name travis-ci - - git config --global user.email travis@example.com - -script: - - ./vendor/bin/simple-phpunit - # run test suite directories in parallel using GNU parallel -# - ls -d tests/Composer/Test/* | grep -v TestCase.php | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml --colors=always {} || (echo -e "\e[41mFAILED\e[0m {}" && exit 1);' - -before_deploy: - - php -d phar.readonly=0 bin/compile - -deploy: - provider: releases - api_key: $GITHUB_TOKEN - file: composer.phar - skip_cleanup: true - on: - tags: true - repo: composer/composer - php: '7.2' diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 922a20e75..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,33 +0,0 @@ -build: false -clone_depth: 5 - -environment: - # This sets the PHP version (from Chocolatey) - PHPCI_CHOCO_VERSION: 7.3.1 - PHPCI_CACHE: C:\tools\phpci - PHPCI_PHP: C:\tools\phpci\php - PHPCI_COMPOSER: C:\tools\phpci\composer - -cache: - - '%PHPCI_CACHE% -> appveyor.yml' - -init: - - SET PATH=%PHPCI_PHP%;%PHPCI_COMPOSER%;%PATH% - - SET COMPOSER_HOME=%PHPCI_COMPOSER%\home - - SET COMPOSER_CACHE_DIR=%PHPCI_COMPOSER%\cache - - SET COMPOSER_NO_INTERACTION=1 - - SET PHP=0 - - SET ANSICON=121x90 (121x90) - -install: - - IF EXIST %PHPCI_CACHE% (SET PHP=1) - - IF %PHP%==0 cinst php -i -y --version %PHPCI_CHOCO_VERSION% --params "/InstallDir:%PHPCI_PHP%" - - IF %PHP%==0 cinst composer -i -y --ia "/DEV=%PHPCI_COMPOSER%" - - php -v - - IF %PHP%==0 (composer --version) ELSE (composer self-update) - - cd %APPVEYOR_BUILD_FOLDER% - - composer install --prefer-dist --no-progress - -test_script: - - cd %APPVEYOR_BUILD_FOLDER% - - vendor\bin\simple-phpunit --colors=always From e0465236311d7a4349d47905c1bc9435eaf471a4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 09:46:33 +0200 Subject: [PATCH 039/193] Fix phpstan workflow --- .github/workflows/phpstan.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index c6b84ca7a..8993d6c42 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -51,6 +51,7 @@ jobs: - name: Run PHPStan run: | + bin/composer config platform --unset bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --no-update bin/composer update phpstan/* phpunit/* sebastian/* --with-all-dependencies vendor/bin/phpstan analyse --configuration=phpstan/config.neon From c0b014904041407ac3c9b4a6c3f985b64d67959a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 10:33:36 +0200 Subject: [PATCH 040/193] Try and fix phpstan --- .github/workflows/phpstan.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 8993d6c42..e2cb477f1 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -46,12 +46,10 @@ jobs: key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}" restore-keys: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}" - - name: "Install dependencies from composer.lock using composer binary provided by system" - run: "composer install ${{ env.COMPOSER_FLAGS }}" + - name: "Install highest dependencies from composer.json using composer binary provided by system" + run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }}" - name: Run PHPStan run: | - bin/composer config platform --unset - bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --no-update - bin/composer update phpstan/* phpunit/* sebastian/* --with-all-dependencies + bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --with-all-dependencies vendor/bin/phpstan analyse --configuration=phpstan/config.neon From e9a2f922d5e060396d74c552bd903036632c3032 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 10:37:21 +0200 Subject: [PATCH 041/193] Fix phpstan config --- phpstan/autoload.php | 5 ----- phpstan/config.neon | 5 +---- 2 files changed, 1 insertion(+), 9 deletions(-) delete mode 100644 phpstan/autoload.php diff --git a/phpstan/autoload.php b/phpstan/autoload.php deleted file mode 100644 index 7d1ed7671..000000000 --- a/phpstan/autoload.php +++ /dev/null @@ -1,5 +0,0 @@ - Date: Wed, 22 Apr 2020 10:57:07 +0200 Subject: [PATCH 042/193] 7.4 highest errors are fixed on 2.0 branch already --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 89eafea47..97e2b6558 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -46,7 +46,7 @@ jobs: - php-version: 7.4 dependencies: highest os: ubuntu-latest - experimental: true # TODO fix build errors there if possible + experimental: false - php-version: 7.4 os: windows-latest dependencies: locked From 0d1922dc2772b6577b12e64449e49e7272ba51be Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 19 Apr 2020 16:00:21 +0200 Subject: [PATCH 043/193] Add a Composer\Versions class which is available in all projects at runtime to query installed packages/versions --- src/Composer/Autoload/AutoloadGenerator.php | 80 +------ src/Composer/Compiler.php | 2 + src/Composer/Factory.php | 11 +- src/Composer/InstalledVersions.php | 186 +++++++++++++++ .../Installer/InstallationManager.php | 5 + .../Repository/FilesystemRepository.php | 90 +++++++- src/Composer/Util/Filesystem.php | 59 ++++- .../Test/Autoload/AutoloadGeneratorTest.php | 26 ++- .../Autoload/Fixtures/autoload_classmap.php | 1 + .../Autoload/Fixtures/autoload_classmap2.php | 1 + .../Autoload/Fixtures/autoload_classmap3.php | 1 + .../Autoload/Fixtures/autoload_classmap4.php | 1 + .../Autoload/Fixtures/autoload_classmap5.php | 1 + .../Autoload/Fixtures/autoload_classmap6.php | 1 + .../Autoload/Fixtures/autoload_classmap7.php | 1 + .../Autoload/Fixtures/autoload_classmap8.php | 1 + .../Autoload/Fixtures/autoload_classmap9.php | 1 + .../Fixtures/autoload_phar_static.php | 5 + .../autoload_static_files_by_dependency.php | 5 + .../Fixtures/autoload_static_functions.php | 5 + ...ad_static_functions_with_include_paths.php | 5 + ...emoved_include_paths_and_autolad_files.php | 5 + .../Fixtures/autoload_static_include_path.php | 5 + .../Fixtures/autoload_static_target_dir.php | 1 + tests/Composer/Test/InstalledVersionsTest.php | 211 ++++++++++++++++++ tests/Composer/Test/Mock/FactoryMock.php | 3 +- .../Repository/FilesystemRepositoryTest.php | 45 ++++ .../Test/Repository/Fixtures/installed.php | 68 ++++++ tests/Composer/Test/TestCase.php | 30 ++- 29 files changed, 774 insertions(+), 82 deletions(-) create mode 100644 src/Composer/InstalledVersions.php create mode 100644 tests/Composer/Test/InstalledVersionsTest.php create mode 100644 tests/Composer/Test/Repository/Fixtures/installed.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 386c2717a..f52dc965f 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -277,6 +277,7 @@ EOF; ); } + $classMap['Composer\\InstalledVersions'] = "\$vendorDir . '/composer/InstalledVersions.php',\n"; ksort($classMap); foreach ($classMap as $class => $code) { $classmapFile .= ' '.var_export($class, true).' => '.$code; @@ -296,33 +297,33 @@ EOF; } } - $this->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile); - $this->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File); - $this->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_namespaces.php', $namespacesFile); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_psr4.php', $psr4File); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_classmap.php', $classmapFile); $includePathFilePath = $targetDir.'/include_paths.php'; if ($includePathFileContents = $this->getIncludePathsFile($packageMap, $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { - $this->filePutContentsIfModified($includePathFilePath, $includePathFileContents); + $filesystem->filePutContentsIfModified($includePathFilePath, $includePathFileContents); } elseif (file_exists($includePathFilePath)) { unlink($includePathFilePath); } $includeFilesFilePath = $targetDir.'/autoload_files.php'; if ($includeFilesFileContents = $this->getIncludeFilesFile($autoloads['files'], $filesystem, $basePath, $vendorPath, $vendorPathCode52, $appBaseDirCode)) { - $this->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents); + $filesystem->filePutContentsIfModified($includeFilesFilePath, $includeFilesFileContents); } elseif (file_exists($includeFilesFilePath)) { unlink($includeFilesFilePath); } - $this->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); $checkPlatform = $config->get('platform-check'); if ($checkPlatform) { - $this->filePutContentsIfModified($targetDir.'/platform_check.php', $this->getPlatformCheck($packageMap)); + $filesystem->filePutContentsIfModified($targetDir.'/platform_check.php', $this->getPlatformCheck($packageMap)); } elseif (file_exists($targetDir.'/platform_check.php')) { unlink($targetDir.'/platform_check.php'); } - $this->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); - $this->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform)); + $filesystem->filePutContentsIfModified($vendorPath.'/autoload.php', $this->getAutoloadFile($vendorPathToTargetDirCode, $suffix)); + $filesystem->filePutContentsIfModified($targetDir.'/autoload_real.php', $this->getAutoloadRealFile(true, (bool) $includePathFileContents, $targetDirLoader, (bool) $includeFilesFileContents, $vendorPathCode, $appBaseDirCode, $suffix, $useGlobalIncludePath, $prependAutoloader, $staticPhpVersion, $checkPlatform)); - $this->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); - $this->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE'); + $filesystem->safeCopy(__DIR__.'/ClassLoader.php', $targetDir.'/ClassLoader.php'); + $filesystem->safeCopy(__DIR__.'/../../../LICENSE', $targetDir.'/LICENSE'); if ($this->runScripts) { $this->eventDispatcher->dispatchScript(ScriptEvents::POST_AUTOLOAD_DUMP, $this->devMode, array(), array( @@ -333,16 +334,6 @@ EOF; return count($classMap); } - private function filePutContentsIfModified($path, $content) - { - $currentContent = @file_get_contents($path); - if (!$currentContent || ($currentContent != $content)) { - return file_put_contents($path, $content); - } - - return 0; - } - private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses, array &$scannedFiles) { foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType, true, $scannedFiles) as $class => $path) { @@ -1133,51 +1124,4 @@ INITIALIZER; return $sortedPackageMap; } - - /** - * Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463 - * - * @param string $source - * @param string $target - */ - protected function safeCopy($source, $target) - { - if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) { - $source = fopen($source, 'r'); - $target = fopen($target, 'w+'); - - stream_copy_to_stream($source, $target); - fclose($source); - fclose($target); - } - } - - /** - * compare 2 files - * https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files - */ - private function filesAreEqual($a, $b) - { - // Check if filesize is different - if (filesize($a) !== filesize($b)) { - return false; - } - - // Check if content is different - $ah = fopen($a, 'rb'); - $bh = fopen($b, 'rb'); - - $result = true; - while (!feof($ah)) { - if (fread($ah, 8192) != fread($bh, 8192)) { - $result = false; - break; - } - } - - fclose($ah); - fclose($bh); - - return $result; - } } diff --git a/src/Composer/Compiler.php b/src/Composer/Compiler.php index fdae04720..240870a1c 100644 --- a/src/Composer/Compiler.php +++ b/src/Composer/Compiler.php @@ -139,6 +139,8 @@ class Compiler $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_files.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_real.php')); $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/autoload_static.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/installed.php')); + $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/InstalledVersions.php')); if (file_exists(__DIR__.'/../../vendor/composer/platform_check.php')) { $this->addFile($phar, new \SplFileInfo(__DIR__.'/../../vendor/composer/platform_check.php')); } diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 85b958370..dd1a7b308 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -17,6 +17,7 @@ use Composer\Json\JsonFile; use Composer\IO\IOInterface; use Composer\Package\Archiver; use Composer\Package\Version\VersionGuesser; +use Composer\Package\RootPackageInterface; use Composer\Repository\RepositoryManager; use Composer\Repository\RepositoryFactory; use Composer\Repository\WritableRepositoryInterface; @@ -344,9 +345,6 @@ class Factory $rm = RepositoryFactory::manager($io, $config, $httpDownloader, $dispatcher); $composer->setRepositoryManager($rm); - // load local repository - $this->addLocalRepository($io, $rm, $vendorDir); - // force-set the version of the global package if not defined as // guessing it adds no value and only takes time if (!$fullLoad && !isset($localConfig['version'])) { @@ -360,6 +358,9 @@ class Factory $package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd); $composer->setPackage($package); + // load local repository + $this->addLocalRepository($io, $rm, $vendorDir, $package); + // initialize installation manager $im = $this->createInstallationManager($loop, $io, $dispatcher); $composer->setInstallationManager($im); @@ -431,9 +432,9 @@ class Factory * @param Repository\RepositoryManager $rm * @param string $vendorDir */ - protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir) + protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage) { - $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io))); + $rm->setLocalRepository(new Repository\InstalledFilesystemRepository(new JsonFile($vendorDir.'/composer/installed.json', null, $io), true, $rootPackage)); } /** diff --git a/src/Composer/InstalledVersions.php b/src/Composer/InstalledVersions.php new file mode 100644 index 000000000..c2c6dbd3e --- /dev/null +++ b/src/Composer/InstalledVersions.php @@ -0,0 +1,186 @@ + + */ + public static function getInstalledPackages() + { + return array_keys(self::$installed['versions']); + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @return bool + */ + public static function isInstalled($packageName) + { + return isset(self::$installed['versions'][$packageName]); + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param ?string $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints($constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + if (!isset(self::$installed['versions'][$packageName])) { + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + $ranges = array(); + if (isset(self::$installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = self::$installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', self::$installed['versions'][$packageName])) { + $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', self::$installed['versions'][$packageName])) { + $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', self::$installed['versions'][$packageName])) { + $ranges = array_merge($ranges, self::$installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + if (!isset(self::$installed['versions'][$packageName])) { + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + if (!isset(self::$installed['versions'][$packageName]['version'])) { + return null; + } + + return self::$installed['versions'][$packageName]['version']; + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + if (!isset(self::$installed['versions'][$packageName])) { + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return self::$installed['versions'][$packageName]['pretty_version']; + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + if (!isset(self::$installed['versions'][$packageName])) { + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + if (!isset(self::$installed['versions'][$packageName]['reference'])) { + return null; + } + + return self::$installed['versions'][$packageName]['reference']; + } + + /** + * @return array + * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]} + */ + public static function getRootPackage() + { + return self::$installed['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @return array[] + * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list} + */ + public static function getRawData() + { + return self::$installed; + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[]}, versions: list} $data + */ + public static function reload($data) + { + self::$installed = $data; + } +} diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 6d752ae58..4b8db7877 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -284,6 +284,11 @@ class InstallationManager throw $e; } + + // do a last write so that we write the repository even if nothing changed + // as that can trigger an update of some files like InstalledVersions.php if + // running a new composer version + $repo->write($devMode, $this); } /** diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index 3bebfbdb3..dcd94f7b3 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -14,6 +14,8 @@ namespace Composer\Repository; use Composer\Json\JsonFile; use Composer\Package\Loader\ArrayLoader; +use Composer\Package\RootPackageInterface; +use Composer\Package\AliasPackage; use Composer\Package\Dumper\ArrayDumper; use Composer\Installer\InstallationManager; use Composer\Util\Filesystem; @@ -27,16 +29,25 @@ use Composer\Util\Filesystem; class FilesystemRepository extends WritableArrayRepository { private $file; + private $dumpVersions; + private $rootPackage; /** * Initializes filesystem repository. * * @param JsonFile $repositoryFile repository json file + * @param bool $dumpVersions + * @param ?RootPackageInterface $rootPackage Must be provided if $dumpVersions is true */ - public function __construct(JsonFile $repositoryFile) + public function __construct(JsonFile $repositoryFile, $dumpVersions = false, RootPackageInterface $rootPackage = null) { parent::__construct(); $this->file = $repositoryFile; + $this->dumpVersions = $dumpVersions; + $this->rootPackage = $rootPackage; + if ($dumpVersions && !$rootPackage) { + throw new \InvalidArgumentException('Expected a root package instance if $dumpVersions is true'); + } } /** @@ -105,5 +116,82 @@ class FilesystemRepository extends WritableArrayRepository }); $this->file->write($data); + + if ($this->dumpVersions) { + $versions = array('versions' => array()); + $packages = $this->getPackages(); + $packages[] = $rootPackage = $this->rootPackage; + while ($rootPackage instanceof AliasPackage) { + $rootPackage = $rootPackage->getAliasOf(); + $packages[] = $rootPackage; + } + + // add real installed packages + foreach ($packages as $package) { + if ($package instanceof AliasPackage) { + continue; + } + + $reference = null; + if ($package->getInstallationSource()) { + $reference = $package->getInstallationSource() === 'source' ? $package->getSourceReference() : $package->getDistReference(); + } + if (null === $reference) { + $reference = ($package->getSourceReference() ?: $package->getDistReference()) ?: null; + } + + $versions['versions'][$package->getName()] = array( + 'pretty_version' => $package->getPrettyVersion(), + 'version' => $package->getVersion(), + 'aliases' => array(), + 'reference' => $reference, + ); + if ($package instanceof RootPackageInterface) { + $versions['root'] = $versions['versions'][$package->getName()]; + $versions['root']['name'] = $package->getName(); + } + } + + // add provided/replaced packages + foreach ($packages as $package) { + foreach ($package->getReplaces() as $replace) { + $replaced = $replace->getPrettyConstraint(); + if ($replaced === 'self.version') { + $replaced = $package->getPrettyVersion(); + } + if (!isset($versions['versions'][$replace->getTarget()]['replaced']) || !in_array($replaced, $versions['versions'][$replace->getTarget()]['replaced'], true)) { + $versions['versions'][$replace->getTarget()]['replaced'][] = $replaced; + } + } + foreach ($package->getProvides() as $provide) { + $provided = $provide->getPrettyConstraint(); + if ($provided === 'self.version') { + $provided = $package->getPrettyVersion(); + } + if (!isset($versions['versions'][$provide->getTarget()]['provided']) || !in_array($provided, $versions['versions'][$provide->getTarget()]['provided'], true)) { + $versions['versions'][$provide->getTarget()]['provided'][] = $provided; + } + } + } + + // add aliases + foreach ($packages as $package) { + if (!$package instanceof AliasPackage) { + continue; + } + $versions['versions'][$package->getName()]['aliases'][] = $package->getPrettyVersion(); + if ($package instanceof RootPackageInterface) { + $versions['root']['aliases'][] = $package->getPrettyVersion(); + } + } + + ksort($versions['versions']); + ksort($versions); + + $fs->filePutContentsIfModified($repoDir.'/installed.php', 'filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass); + } } } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 8df7de8b5..0e516ef29 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -313,7 +313,7 @@ class Filesystem if (!function_exists('proc_open')) { $this->copyThenRemove($source, $target); - + return; } @@ -721,4 +721,61 @@ class Filesystem return $this->rmdir($junction); } + + public function filePutContentsIfModified($path, $content) + { + $currentContent = @file_get_contents($path); + if (!$currentContent || ($currentContent != $content)) { + return file_put_contents($path, $content); + } + + return 0; + } + + /** + * Copy file using stream_copy_to_stream to work around https://bugs.php.net/bug.php?id=6463 + * + * @param string $source + * @param string $target + */ + public function safeCopy($source, $target) + { + if (!file_exists($target) || !file_exists($source) || !$this->filesAreEqual($source, $target)) { + $source = fopen($source, 'r'); + $target = fopen($target, 'w+'); + + stream_copy_to_stream($source, $target); + fclose($source); + fclose($target); + } + } + + /** + * compare 2 files + * https://stackoverflow.com/questions/3060125/can-i-use-file-get-contents-to-compare-two-files + */ + private function filesAreEqual($a, $b) + { + // Check if filesize is different + if (filesize($a) !== filesize($b)) { + return false; + } + + // Check if content is different + $ah = fopen($a, 'rb'); + $bh = fopen($b, 'rb'); + + $result = true; + while (!feof($ah)) { + if (fread($ah, 8192) != fread($bh, 8192)) { + $result = false; + break; + } + } + + fclose($ah); + fclose($bh); + + return $result; + } } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index f9de00199..4159cf003 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -452,6 +452,7 @@ class AutoloadGeneratorTest extends TestCase $this->assertEquals( array( 'B\\C\\C' => $this->vendorDir.'/b/b/src/C/C.php', + 'Composer\\InstalledVersions' => $this->vendorDir . '/composer/InstalledVersions.php', ), include $this->vendorDir.'/composer/autoload_classmap.php' ); @@ -599,7 +600,9 @@ class AutoloadGeneratorTest extends TestCase $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_8'); $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated."); $this->assertEquals( - array(), + array( + 'Composer\\InstalledVersions' => $this->vendorDir.'/composer/InstalledVersions.php', + ), include $this->vendorDir.'/composer/autoload_classmap.php' ); } @@ -636,6 +639,7 @@ class AutoloadGeneratorTest extends TestCase \$baseDir = dirname(\$vendorDir); return array( + 'Composer\\\\InstalledVersions' => \$vendorDir . '/composer/InstalledVersions.php', 'psr0_match' => \$baseDir . '/psr0/psr0/match.php', 'psr4\\\\match' => \$baseDir . '/psr4/match.php', ); @@ -677,6 +681,7 @@ EOF; 'ClassMapBar' => $this->vendorDir.'/b/b/src/b.php', 'ClassMapBaz' => $this->vendorDir.'/b/b/lib/c.php', 'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php', + 'Composer\\InstalledVersions' => $this->vendorDir.'/composer/InstalledVersions.php', ), include $this->vendorDir.'/composer/autoload_classmap.php' ); @@ -717,6 +722,7 @@ EOF; 'ClassMapBar' => $this->vendorDir.'/a/a/target/lib/b.php', 'ClassMapBaz' => $this->vendorDir.'/b/b/src/c.php', 'ClassMapFoo' => $this->vendorDir.'/a/a/target/src/a.php', + 'Composer\\InstalledVersions' => $this->vendorDir.'/composer/InstalledVersions.php', ), include $this->vendorDir.'/composer/autoload_classmap.php' ); @@ -758,6 +764,7 @@ EOF; 'ClassMapBar' => $this->vendorDir.'/b/b/test.php', 'ClassMapBaz' => $this->vendorDir.'/c/c/foo/test.php', 'ClassMapFoo' => $this->vendorDir.'/a/a/src/a.php', + 'Composer\\InstalledVersions' => $this->vendorDir.'/composer/InstalledVersions.php', ), include $this->vendorDir.'/composer/autoload_classmap.php' ); @@ -805,6 +812,7 @@ EOF; 'ClassMapBar' => $this->vendorDir.'/b/b/ClassMapBar.php', 'ClassMapBaz' => $this->vendorDir.'/c/c/foo/ClassMapBaz.php', 'ClassMapFoo' => $this->vendorDir.'/a/a/src/ClassMapFoo.php', + 'Composer\\InstalledVersions' => $this->vendorDir.'/composer/InstalledVersions.php', ), include $this->vendorDir.'/composer/autoload_classmap.php' ); @@ -852,7 +860,8 @@ EOF; $this->assertFileContentEquals(__DIR__.'/Fixtures/autoload_static_functions.php', $this->vendorDir.'/composer/autoload_static.php'); $this->assertFileContentEquals(__DIR__.'/Fixtures/autoload_files_functions.php', $this->vendorDir.'/composer/autoload_files.php'); - include $this->vendorDir . '/autoload.php'; + $loader = require $this->vendorDir . '/autoload.php'; + $loader->unregister(); $this->assertTrue(function_exists('testFilesAutoloadGeneration1')); $this->assertTrue(function_exists('testFilesAutoloadGeneration2')); $this->assertTrue(function_exists('testFilesAutoloadGeneration3')); @@ -988,7 +997,8 @@ EOF; $this->assertFileContentEquals(__DIR__ . '/Fixtures/autoload_real_files_by_dependency.php', $this->vendorDir . '/composer/autoload_real.php'); $this->assertFileContentEquals(__DIR__ . '/Fixtures/autoload_static_files_by_dependency.php', $this->vendorDir . '/composer/autoload_static.php'); - require $this->vendorDir . '/autoload.php'; + $loader = require $this->vendorDir . '/autoload.php'; + $loader->unregister(); $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency1')); $this->assertTrue(function_exists('testFilesAutoloadOrderByDependency2')); @@ -1087,6 +1097,7 @@ EOF; return array( 'A\\\\B\\\\C' => \$baseDir . '/lib/A/B/C.php', + 'Composer\\\\InstalledVersions' => \$vendorDir . '/composer/InstalledVersions.php', 'Foo\\\\Bar' => \$baseDir . '/src/classes.php', ); @@ -1155,7 +1166,8 @@ EOF; $oldIncludePath = get_include_path(); - require $this->vendorDir."/autoload.php"; + $loader = require $this->vendorDir."/autoload.php"; + $loader->unregister(); $this->assertEquals( $this->vendorDir."/a/a/lib".PATH_SEPARATOR.$oldIncludePath, @@ -1183,7 +1195,8 @@ EOF; $oldIncludePath = get_include_path(); - require $this->vendorDir."/autoload.php"; + $loader = require $this->vendorDir."/autoload.php"; + $loader->unregister(); $this->assertEquals( $this->workingDir."/lib".PATH_SEPARATOR.$this->workingDir."/src".PATH_SEPARATOR.$this->vendorDir."/a/a/lib".PATH_SEPARATOR.$oldIncludePath, @@ -1356,6 +1369,7 @@ $baseDir = dirname($vendorDir).'/working-dir'; return array( 'Bar\\Bar' => $vendorDir . '/b/b/classmaps/classes.php', 'Bar\\Foo' => $vendorDir . '/b/b/lib/Bar/Foo.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Foo\\Bar' => $baseDir . '/src/Foo/Bar.php', 'Foo\\Foo' => $baseDir . '/classmap/classes.php', ); @@ -1434,6 +1448,7 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir).'/working-dir'; return array( + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Foo\\Bar' => $baseDir . '/../src/Foo/Bar.php', 'Foo\\Foo' => $baseDir . '/../classmap/classes.php', ); @@ -1503,6 +1518,7 @@ $baseDir = dirname($vendorDir); return array( 'Classmap\\Foo' => $baseDir . '/class.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Foo\\Bar' => $baseDir . '/Foo/Bar.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php index a33c6674a..e49f8b7d8 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap.php @@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir); return array( 'Acme\\Cake\\ClassMapBar' => $baseDir . '/src-cake/ClassMapBar.php', 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Lala\\ClassMapMain' => $baseDir . '/src/Lala/ClassMapMain.php', 'Lala\\Test\\ClassMapMainTest' => $baseDir . '/src/Lala/Test/ClassMapMainTest.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php index 9cc0484f1..60cec93b3 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap2.php @@ -7,4 +7,5 @@ $baseDir = dirname(dirname($vendorDir)); return array( 'ClassMapFoo' => $baseDir . '/composersrc/foo.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php index dacb8ff90..3bfb2bb80 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap3.php @@ -7,5 +7,6 @@ $baseDir = $vendorDir; return array( 'ClassMapFoo' => $vendorDir . '/composersrc/foo.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Main\\Foo' => $vendorDir . '/src/Main/Foo.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php index ae8025544..767ec5e5d 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap4.php @@ -9,4 +9,5 @@ return array( 'ClassMapBar' => $vendorDir . '/b/b/src/b.php', 'ClassMapBaz' => $vendorDir . '/b/b/lib/c.php', 'ClassMapFoo' => $vendorDir . '/a/a/src/a.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php index 71bbc004d..e88a14606 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap5.php @@ -9,4 +9,5 @@ return array( 'ClassMapBar' => $vendorDir . '/b/b/test.php', 'ClassMapBaz' => $vendorDir . '/c/c/foo/test.php', 'ClassMapFoo' => $vendorDir . '/a/a/src/a.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap6.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap6.php index ef97fb501..d7a357ace 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap6.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap6.php @@ -8,4 +8,5 @@ $baseDir = dirname($vendorDir); return array( 'ClassMapBar' => $baseDir . '/lib/rootbar.php', 'ClassMapFoo' => $baseDir . '/src/rootfoo.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php index 5768726d1..6818e6b82 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap7.php @@ -6,5 +6,6 @@ $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); return array( + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'Main\\ClassMain' => $baseDir . '/src/Main/ClassMain.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap8.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap8.php index 0a40d114c..a002faf5a 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap8.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap8.php @@ -9,4 +9,5 @@ return array( 'ClassMapBar' => $vendorDir . '/b/b/ClassMapBar.php', 'ClassMapBaz' => $vendorDir . '/c/c/foo/ClassMapBaz.php', 'ClassMapFoo' => $vendorDir . '/a/a/src/ClassMapFoo.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php index f9ad3ca30..75bc86230 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php @@ -8,5 +8,6 @@ $baseDir = dirname($vendorDir); return array( 'A' => $vendorDir . '/a/a/src/A.php', 'C' => $vendorDir . '/c/c/src/C.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', 'D' => $vendorDir . '/d/d/src/D.php', ); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_phar_static.php b/tests/Composer/Test/Autoload/Fixtures/autoload_phar_static.php index 486a5c0dc..ceaf04a0c 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_phar_static.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_phar_static.php @@ -75,12 +75,17 @@ class ComposerStaticInitPhar ), ); + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInitPhar::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInitPhar::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInitPhar::$prefixesPsr0; + $loader->classMap = ComposerStaticInitPhar::$classMap; }, null, ClassLoader::class); } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_static_files_by_dependency.php b/tests/Composer/Test/Autoload/Fixtures/autoload_static_files_by_dependency.php index ba1f9238a..0c5ce896d 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_static_files_by_dependency.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_static_files_by_dependency.php @@ -15,9 +15,14 @@ class ComposerStaticInitFilesAutoloadOrder '334307692417e52db5a08c3271700a7e' => __DIR__ . '/../..' . '/root2.php', ); + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { + $loader->classMap = ComposerStaticInitFilesAutoloadOrder::$classMap; }, null, ClassLoader::class); } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions.php index dfe3c5bcc..e67b7803e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions.php @@ -14,9 +14,14 @@ class ComposerStaticInitFilesAutoload '61b776fd0ee84fb7d7d958ae46118ded' => __DIR__ . '/../..' . '/root.php', ); + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { + $loader->classMap = ComposerStaticInitFilesAutoload::$classMap; }, null, ClassLoader::class); } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_include_paths.php b/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_include_paths.php index dfe3c5bcc..e67b7803e 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_include_paths.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_include_paths.php @@ -14,9 +14,14 @@ class ComposerStaticInitFilesAutoload '61b776fd0ee84fb7d7d958ae46118ded' => __DIR__ . '/../..' . '/root.php', ); + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { + $loader->classMap = ComposerStaticInitFilesAutoload::$classMap; }, null, ClassLoader::class); } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_removed_include_paths_and_autolad_files.php b/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_removed_include_paths_and_autolad_files.php index 11897e11a..44fd701d6 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_removed_include_paths_and_autolad_files.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_static_functions_with_removed_include_paths_and_autolad_files.php @@ -6,9 +6,14 @@ namespace Composer\Autoload; class ComposerStaticInitFilesAutoload { + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { + $loader->classMap = ComposerStaticInitFilesAutoload::$classMap; }, null, ClassLoader::class); } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_static_include_path.php b/tests/Composer/Test/Autoload/Fixtures/autoload_static_include_path.php index 3db5da28a..82bbd2ff7 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_static_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_static_include_path.php @@ -20,10 +20,15 @@ class ComposerStaticInitIncludePath ), ); + public static $classMap = array ( + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixesPsr0 = ComposerStaticInitIncludePath::$prefixesPsr0; + $loader->classMap = ComposerStaticInitIncludePath::$classMap; }, null, ClassLoader::class); } diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_static_target_dir.php b/tests/Composer/Test/Autoload/Fixtures/autoload_static_target_dir.php index 114b5a98a..60fd33faa 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_static_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_static_target_dir.php @@ -28,6 +28,7 @@ class ComposerStaticInitTargetDir public static $classMap = array ( 'ClassMapBar' => __DIR__ . '/../..' . '/lib/rootbar.php', 'ClassMapFoo' => __DIR__ . '/../..' . '/src/rootfoo.php', + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/tests/Composer/Test/InstalledVersionsTest.php b/tests/Composer/Test/InstalledVersionsTest.php new file mode 100644 index 000000000..c136e1584 --- /dev/null +++ b/tests/Composer/Test/InstalledVersionsTest.php @@ -0,0 +1,211 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test; + +use Composer\Test\TestCase; +use Composer\InstalledVersions; +use Composer\Semver\VersionParser; + +class InstalledVersionsTest extends TestCase +{ + public function setUp() + { + InstalledVersions::reload(require __DIR__.'/Repository/Fixtures/installed.php'); + } + + public function testGetInstalledPackages() + { + $names = array( + '__root__', + 'a/provider', + 'a/provider2', + 'b/replacer', + 'c/c', + 'foo/impl', + 'foo/impl2', + 'foo/replaced', + ); + $this->assertSame($names, InstalledVersions::getInstalledPackages()); + } + + /** + * @dataProvider isInstalledProvider + */ + public function testIsInstalled($expected, $name, $constraint = null) + { + $this->assertSame($expected, InstalledVersions::isInstalled($name)); + } + + public static function isInstalledProvider() + { + return array( + array(true, 'foo/impl'), + array(true, 'foo/replaced'), + array(true, 'c/c'), + array(true, '__root__'), + array(true, 'b/replacer'), + array(false, 'not/there'), + array(false, 'not/there', '^1.0'), + ); + } + + /** + * @dataProvider satisfiesProvider + */ + public function testSatisfies($expected, $name, $constraint) + { + $this->assertSame($expected, InstalledVersions::satisfies(new VersionParser, $name, $constraint)); + } + + public static function satisfiesProvider() + { + return array( + array(true, 'foo/impl', '1.5'), + array(true, 'foo/impl', '1.2'), + array(true, 'foo/impl', '^1.0'), + array(true, 'foo/impl', '^3 || ^2'), + array(false, 'foo/impl', '^3'), + + array(true, 'foo/replaced', '3.5'), + array(true, 'foo/replaced', '^3.2'), + array(false, 'foo/replaced', '4.0'), + + array(true, 'c/c', '3.0.0'), + array(true, 'c/c', '^3'), + array(false, 'c/c', '^3.1'), + + array(true, '__root__', 'dev-master'), + array(true, '__root__', '^1.10'), + array(false, '__root__', '^2'), + + array(true, 'b/replacer', '^2.1'), + array(false, 'b/replacer', '^2.3'), + + array(true, 'a/provider2', '^1.2'), + array(true, 'a/provider2', '^1.4'), + array(false, 'a/provider2', '^1.5'), + ); + } + + /** + * @dataProvider getVersionRangesProvider + */ + public function testGetVersionRanges($expected, $name) + { + $this->assertSame($expected, InstalledVersions::getVersionRanges($name)); + } + + public static function getVersionRangesProvider() + { + return array( + array('dev-master || 1.10.x-dev', '__root__'), + array('^1.1 || 1.2 || 1.4 || 2.0', 'foo/impl'), + array('2.2 || 2.0', 'foo/impl2'), + array('^3.0', 'foo/replaced'), + array('1.1', 'a/provider'), + array('1.2 || 1.4', 'a/provider2'), + array('2.2', 'b/replacer'), + array('3.0', 'c/c'), + ); + } + + /** + * @dataProvider getVersionProvider + */ + public function testGetVersion($expected, $name) + { + $this->assertSame($expected, InstalledVersions::getVersion($name)); + } + + public static function getVersionProvider() + { + return array( + array('dev-master', '__root__'), + array(null, 'foo/impl'), + array(null, 'foo/impl2'), + array(null, 'foo/replaced'), + array('1.1.0.0', 'a/provider'), + array('1.2.0.0', 'a/provider2'), + array('2.2.0.0', 'b/replacer'), + array('3.0.0.0', 'c/c'), + ); + } + + /** + * @dataProvider getPrettyVersionProvider + */ + public function testGetPrettyVersion($expected, $name) + { + $this->assertSame($expected, InstalledVersions::getPrettyVersion($name)); + } + + public static function getPrettyVersionProvider() + { + return array( + array('dev-master', '__root__'), + array(null, 'foo/impl'), + array(null, 'foo/impl2'), + array(null, 'foo/replaced'), + array('1.1', 'a/provider'), + array('1.2', 'a/provider2'), + array('2.2', 'b/replacer'), + array('3.0', 'c/c'), + ); + } + + public function testGetVersionOutOfBounds() + { + $this->setExpectedException('OutOfBoundsException'); + InstalledVersions::getVersion('not/installed'); + } + + public function testGetRootPackage() + { + $this->assertSame(array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => array( + '1.10.x-dev', + ), + 'reference' => 'sourceref-by-default', + 'name' => '__root__', + ), InstalledVersions::getRootPackage()); + } + + public function testGetRawData() + { + $this->assertSame(require __DIR__.'/Repository/Fixtures/installed.php', InstalledVersions::getRawData()); + } + + /** + * @dataProvider getReferenceProvider + */ + public function testGetReference($expected, $name) + { + $this->assertSame($expected, InstalledVersions::getReference($name)); + } + + public static function getReferenceProvider() + { + return array( + array('sourceref-by-default', '__root__'), + array(null, 'foo/impl'), + array(null, 'foo/impl2'), + array(null, 'foo/replaced'), + array('distref-as-no-source', 'a/provider'), + array('distref-as-installed-from-dist', 'a/provider2'), + array(null, 'b/replacer'), + array(null, 'c/c'), + ); + } +} diff --git a/tests/Composer/Test/Mock/FactoryMock.php b/tests/Composer/Test/Mock/FactoryMock.php index d4dc444a0..608d572dd 100644 --- a/tests/Composer/Test/Mock/FactoryMock.php +++ b/tests/Composer/Test/Mock/FactoryMock.php @@ -17,6 +17,7 @@ use Composer\Config; use Composer\Factory; use Composer\Repository\RepositoryManager; use Composer\Repository\WritableRepositoryInterface; +use Composer\Package\RootPackageInterface; use Composer\Installer; use Composer\EventDispatcher\EventDispatcher; use Composer\IO\IOInterface; @@ -37,7 +38,7 @@ class FactoryMock extends Factory return $config; } - protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir) + protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage) { } diff --git a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php index 97747ebc5..e2959fde8 100644 --- a/tests/Composer/Test/Repository/FilesystemRepositoryTest.php +++ b/tests/Composer/Test/Repository/FilesystemRepositoryTest.php @@ -14,6 +14,7 @@ namespace Composer\Test\Repository; use Composer\Repository\FilesystemRepository; use Composer\Test\TestCase; +use Composer\Json\JsonFile; class FilesystemRepositoryTest extends TestCase { @@ -113,6 +114,50 @@ class FilesystemRepositoryTest extends TestCase $repository->write(true, $im); } + public function testRepositoryWritesInstalledPhp() + { + $dir = $this->getUniqueTmpDirectory(); + $json = new JsonFile($dir.'/installed.json'); + + $rootPackage = $this->getPackage('__root__', 'dev-master', 'Composer\Package\RootPackage'); + $rootPackage->setSourceReference('sourceref-by-default'); + $rootPackage->setDistReference('distref'); + $this->configureLinks($rootPackage, array('provide' => array('foo/impl' => '2.0'))); + $rootPackage = $this->getAliasPackage($rootPackage, '1.10.x-dev'); + + $repository = new FilesystemRepository($json, true, $rootPackage); + $pkg = $this->getPackage('a/provider', '1.1'); + $this->configureLinks($pkg, array('provide' => array('foo/impl' => '^1.1', 'foo/impl2' => '2.0'))); + $pkg->setDistReference('distref-as-no-source'); + $repository->addPackage($pkg); + + $pkg = $this->getPackage('a/provider2', '1.2'); + $this->configureLinks($pkg, array('provide' => array('foo/impl' => 'self.version', 'foo/impl2' => '2.0'))); + $pkg->setSourceReference('sourceref'); + $pkg->setDistReference('distref-as-installed-from-dist'); + $pkg->setInstallationSource('dist'); + $repository->addPackage($pkg); + + $repository->addPackage($this->getAliasPackage($pkg, '1.4')); + + $pkg = $this->getPackage('b/replacer', '2.2'); + $this->configureLinks($pkg, array('replace' => array('foo/impl2' => 'self.version', 'foo/replaced' => '^3.0'))); + $repository->addPackage($pkg); + + $pkg = $this->getPackage('c/c', '3.0'); + $repository->addPackage($pkg); + + $im = $this->getMockBuilder('Composer\Installer\InstallationManager') + ->disableOriginalConstructor() + ->getMock(); + $im->expects($this->any()) + ->method('getInstallPath') + ->will($this->returnValue('/foo/bar/vendor/woop/woop')); + + $repository->write(true, $im); + $this->assertSame(require __DIR__.'/Fixtures/installed.php', require $dir.'/installed.php'); + } + private function createJsonFileMock() { return $this->getMockBuilder('Composer\Json\JsonFile') diff --git a/tests/Composer/Test/Repository/Fixtures/installed.php b/tests/Composer/Test/Repository/Fixtures/installed.php new file mode 100644 index 000000000..567414a66 --- /dev/null +++ b/tests/Composer/Test/Repository/Fixtures/installed.php @@ -0,0 +1,68 @@ + array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => array( + '1.10.x-dev', + ), + 'reference' => 'sourceref-by-default', + 'name' => '__root__', + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-master', + 'version' => 'dev-master', + 'aliases' => array( + '1.10.x-dev', + ), + 'reference' => 'sourceref-by-default', + ), + 'a/provider' => array( + 'pretty_version' => '1.1', + 'version' => '1.1.0.0', + 'aliases' => array(), + 'reference' => 'distref-as-no-source', + ), + 'a/provider2' => array( + 'pretty_version' => '1.2', + 'version' => '1.2.0.0', + 'aliases' => array( + '1.4', + ), + 'reference' => 'distref-as-installed-from-dist', + ), + 'b/replacer' => array( + 'pretty_version' => '2.2', + 'version' => '2.2.0.0', + 'aliases' => array(), + 'reference' => NULL, + ), + 'c/c' => array( + 'pretty_version' => '3.0', + 'version' => '3.0.0.0', + 'aliases' => array(), + 'reference' => NULL, + ), + 'foo/impl' => array( + 'provided' => array( + '^1.1', + '1.2', + '1.4', + '2.0', + ) + ), + 'foo/impl2' => array( + 'provided' => array( + '2.0', + ), + 'replaced' => array( + '2.2', + ), + ), + 'foo/replaced' => array( + 'replaced' => array( + '^3.0', + ), + ), + ), +); diff --git a/tests/Composer/Test/TestCase.php b/tests/Composer/Test/TestCase.php index d43d3d911..331d41199 100644 --- a/tests/Composer/Test/TestCase.php +++ b/tests/Composer/Test/TestCase.php @@ -14,11 +14,15 @@ namespace Composer\Test; use Composer\Semver\VersionParser; use Composer\Package\AliasPackage; +use Composer\Package\RootPackageInterface; +use Composer\Package\PackageInterface; use Composer\Semver\Constraint\Constraint; use Composer\Util\Filesystem; use Composer\Util\Silencer; use PHPUnit\Framework\TestCase as BaseTestCase; use Symfony\Component\Process\ExecutableFinder; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\BasePackage; abstract class TestCase extends BaseTestCase { @@ -73,7 +77,31 @@ abstract class TestCase extends BaseTestCase { $normVersion = self::getVersionParser()->normalize($version); - return new AliasPackage($package, $normVersion, $version); + $class = 'Composer\Package\AliasPackage'; + if ($package instanceof RootPackageInterface) { + $class = 'Composer\Package\RootAliasPackage'; + } + + return new $class($package, $normVersion, $version); + } + + protected function configureLinks(PackageInterface $package, array $config) + { + $arrayLoader = new ArrayLoader(); + + foreach (BasePackage::$supportedLinkTypes as $type => $opts) { + if (isset($config[$type])) { + $method = 'set'.ucfirst($opts['method']); + $package->{$method}( + $arrayLoader->parseLinks( + $package->getName(), + $package->getPrettyVersion(), + $opts['description'], + $config[$type] + ) + ); + } + } } protected static function ensureDirectoryExistsAndClear($directory) From 0ab48a1773e55ee1706d43e739fa5719811a87c8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 21 Apr 2020 21:57:27 +0200 Subject: [PATCH 044/193] Add composer-runtime-api platform package --- src/Composer/Composer.php | 11 +++++++++++ src/Composer/DependencyResolver/Transaction.php | 4 ++-- src/Composer/Plugin/PluginInterface.php | 5 +++++ src/Composer/Plugin/PluginManager.php | 4 ++-- src/Composer/Repository/ComposerRepository.php | 4 ++-- src/Composer/Repository/PlatformRepository.php | 9 ++++++++- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index b5bbb0a0b..c7ab40039 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -55,6 +55,17 @@ class Composer const RELEASE_DATE = '@release_date@'; const SOURCE_VERSION = '2.0-dev+source'; + /** + * Version number of the internal composer-runtime-api package + * + * This is used to version features available to projects at runtime + * like the platform-check file, the Composer\InstalledVersions class + * and possibly others in the future. + * + * @var string + */ + const RUNTIME_API_VERSION = '2.0.0'; + public static function getVersion() { // no replacement done, this must be a source checkout diff --git a/src/Composer/DependencyResolver/Transaction.php b/src/Composer/DependencyResolver/Transaction.php index 1e681a04f..a919238f6 100644 --- a/src/Composer/DependencyResolver/Transaction.php +++ b/src/Composer/DependencyResolver/Transaction.php @@ -261,9 +261,9 @@ class Transaction // is this a plugin or a dependency of a plugin? if ($isPlugin || count(array_intersect($package->getNames(), $pluginRequires))) { - // get the package's requires, but filter out any platform requirements or 'composer-plugin-api' + // get the package's requires, but filter out any platform requirements $requires = array_filter(array_keys($package->getRequires()), function ($req) { - return $req !== 'composer-plugin-api' && !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); + return !preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); }); // is this a plugin with no meaningful dependencies? diff --git a/src/Composer/Plugin/PluginInterface.php b/src/Composer/Plugin/PluginInterface.php index 4390764ff..f18799e7b 100644 --- a/src/Composer/Plugin/PluginInterface.php +++ b/src/Composer/Plugin/PluginInterface.php @@ -25,6 +25,11 @@ interface PluginInterface /** * Version number of the internal composer-plugin-api package * + * This is used to denote the API version of Plugin specific + * features, but is also bumped to a new major if Composer + * includes a major break in internal APIs which are susceptible + * to be used by plugins. + * * @var string */ const PLUGIN_API_VERSION = '2.0.0'; diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 97757f4fa..1dbbd2960 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -134,8 +134,8 @@ class PluginManager $currentPluginApiVersion = $this->getPluginApiVersion(); $currentPluginApiConstraint = new Constraint('==', $this->versionParser->normalize($currentPluginApiVersion)); - if ($requiresComposer->getPrettyString() === '1.0.0' && $this->getPluginApiVersion() === '1.0.0') { - $this->io->writeError('The "' . $package->getName() . '" plugin requires composer-plugin-api 1.0.0, this *WILL* break in the future and it should be fixed ASAP (require ^1.0 for example).'); + if ($requiresComposer->getPrettyString() === $this->getPluginApiVersion()) { + $this->io->writeError('The "' . $package->getName() . '" plugin requires composer-plugin-api '.$this->getPluginApiVersion().', this *WILL* break in the future and it should be fixed ASAP (require ^'.$this->getPluginApiVersion().' instead for example).'); } elseif (!$requiresComposer->matches($currentPluginApiConstraint)) { $this->io->writeError('The "' . $package->getName() . '" plugin was skipped because it requires a Plugin API version ("' . $requiresComposer->getPrettyString() . '") that does not match your Composer installation ("' . $currentPluginApiVersion . '"). You may need to run composer update with the "--no-plugins" option.'); diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5e9e0c5b3..7082d29c2 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -502,7 +502,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito { if (!$this->hasPartialPackages() || !isset($this->partialPackagesByName[$name])) { // skip platform packages, root package and composer-plugin-api - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name || 'composer-plugin-api' === $name) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name) || '__root__' === $name) { return array(); } @@ -672,7 +672,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $realName = preg_replace('{~dev$}', '', $name); // skip platform packages, root package and composer-plugin-api - if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $realName) || '__root__' === $realName || 'composer-plugin-api' === $realName) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $realName) || '__root__' === $realName) { continue; } diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 84f3d4b66..6ee05ca60 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -20,6 +20,7 @@ use Composer\Util\ProcessExecutor; use Composer\Util\Silencer; use Composer\Util\Platform; use Composer\XdebugHandler\XdebugHandler; +use Composer\Composer; use Symfony\Component\Process\ExecutableFinder; /** @@ -27,7 +28,7 @@ use Symfony\Component\Process\ExecutableFinder; */ class PlatformRepository extends ArrayRepository { - const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-plugin-api)$}iD'; + const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD'; private $versionParser; @@ -79,6 +80,12 @@ class PlatformRepository extends ArrayRepository $composerPluginApi->setDescription('The Composer Plugin API'); $this->addPackage($composerPluginApi); + $prettyVersion = Composer::RUNTIME_API_VERSION; + $version = $this->versionParser->normalize($prettyVersion); + $composerRuntimeApi = new CompletePackage('composer-runtime-api', $version, $prettyVersion); + $composerRuntimeApi->setDescription('The Composer Runtime API'); + $this->addPackage($composerRuntimeApi); + try { $prettyVersion = PHP_VERSION; $version = $this->versionParser->normalize($prettyVersion); From 613982e88b197a9fc4b81d5bdd69079603e8e65e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elan=20Ruusam=C3=A4e?= Date: Wed, 22 Apr 2020 14:39:10 +0300 Subject: [PATCH 045/193] scripts: indicate how to see scripts-descriptions --- doc/articles/scripts.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index fae33f614..9f4504ae3 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -375,4 +375,6 @@ You can set custom script descriptions with the following in your `composer.json } ``` +The descriptions are seen in `composer run -l` command output. + > **Note:** You can only set custom descriptions of custom commands. From 1000d49145dd3d0ed96fd789f1e93bc541218dcd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 14:02:09 +0200 Subject: [PATCH 046/193] Fix error reporting when the root version changed since the last update, and does not match circular deps in lock file anymore --- src/Composer/Installer.php | 3 + .../root-alias-change-with-circular-dep.test | 67 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index c1e8099c4..bf33693b3 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -43,6 +43,7 @@ use Composer\Package\Link; use Composer\Package\LinkConstraint\VersionConstraint; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; +use Composer\Package\Version\VersionParser; use Composer\Package\Package; use Composer\Repository\ArrayRepository; use Composer\Repository\RepositorySet; @@ -741,6 +742,8 @@ class Installer $this->fixedRootPackage->setRequires(array()); $this->fixedRootPackage->setDevRequires(array()); + $stabilityFlags[$this->package->getName()] = BasePackage::$stabilities[VersionParser::parseStability($this->package->getVersion())]; + $repositorySet = new RepositorySet($minimumStability, $stabilityFlags, $rootAliases, $this->package->getReferences(), $rootRequires); $repositorySet->addRepository(new RootPackageRepository($this->fixedRootPackage)); $repositorySet->addRepository($platformRepo); diff --git a/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test b/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test new file mode 100644 index 000000000..91fad5662 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test @@ -0,0 +1,67 @@ +--TEST-- +Root alias changing after the lock file was created and invalidating it should show a decent error message +This also checks that an implicit stabilityFlag is added for the root package, if it is a dev version +--COMPOSER-- +{ + "name": "root/pkg", + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "b/requirer", "version": "1.0.0", + "require": { "root/pkg": "^1" } + } + ] + } + ], + "require": { + "b/requirer": "*" + }, + "version": "2.x-dev" +} + +--INSTALLED-- +[ + { + "name": "b/requirer", "version": "1.0.0", + "require": { "root/pkg": "^1" } + } +] + +--LOCK-- +{ + "packages": [ + { + "name": "b/requirer", "version": "1.0.0", + "require": { "root/pkg": "^1" }, + "type": "library" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} +--RUN-- +install + +--EXPECT-EXIT-CODE-- +2 + +--EXPECT-OUTPUT-- +Installing dependencies from lock file (including require-dev) +Verifying lock file contents can be installed on current platform. +Your lock file does not contain a compatible set of packages. Please run composer update. + + Problem 1 + - b/requirer is locked to version 1.0.0 and an update of this package was not requested. + - b/requirer 1.0.0 requires root/pkg ^1 -> found root/pkg[2.x-dev] but it does not match your constraint. + +Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions. + +--EXPECT-- From 8ea787baa170ee462ee791c65b469ef62790f8aa Mon Sep 17 00:00:00 2001 From: Nathan Esayeas Date: Wed, 22 Apr 2020 08:56:39 -0400 Subject: [PATCH 047/193] fix: workflow status badge (#8825) --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 10f48283d..a2a8cea69 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,7 @@ Composer helps you declare, manage, and install dependencies of PHP projects. See [https://getcomposer.org/](https://getcomposer.org/) for more information and documentation. -[![Continuous Integration](https://github.com/composer/composer.svg/workflows/Continuous%20Integration/badge.svg?branch=master)](https://github.com/composer/composer.svg/actions) -[![Build Status](https://travis-ci.org/composer/composer.svg?branch=master)](https://travis-ci.org/composer/composer) +[![Continuous Integration](https://github.com/composer/composer/workflows/Continuous%20Integration/badge.svg?branch=master)](https://github.com/composer/composer/actions) Installation / Usage -------------------- From c2f77d80bd0dde35f460512776914146cafec0e3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 16:40:28 +0200 Subject: [PATCH 048/193] Remove usage of 5.6+ constant --- src/Composer/Repository/ComposerRepository.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 7082d29c2..f9d3155c1 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -363,9 +363,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if ($this->lazyProvidersUrl && count($packageNameMap)) { if (is_array($this->availablePackages)) { $availPackages = $this->availablePackages; - $packageNameMap = array_filter($packageNameMap, function ($name) use ($availPackages) { - return isset($availPackages[strtolower($name)]); - }, ARRAY_FILTER_USE_KEY); + foreach ($packageNameMap as $name => $constraint) { + if (!isset($availPackages[strtolower($name)])) { + unset($packageNameMap[$name]); + } + } } $result = $this->loadAsyncPackages($packageNameMap, $acceptableStabilities, $stabilityFlags); From 7049bbb7149aed1866e86ec7c5147dcb1eb87ec8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 17:05:37 +0200 Subject: [PATCH 049/193] Switch require_once to require for autoload_static as the once variant seems unnecessary --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- .../Autoload/Fixtures/autoload_real_files_by_dependency.php | 2 +- .../Composer/Test/Autoload/Fixtures/autoload_real_functions.php | 2 +- .../Fixtures/autoload_real_functions_with_include_paths.php | 2 +- ...l_functions_with_removed_include_paths_and_autolad_files.php | 2 +- .../Test/Autoload/Fixtures/autoload_real_include_path.php | 2 +- .../Test/Autoload/Fixtures/autoload_real_target_dir.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index f52dc965f..44279bc04 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -742,7 +742,7 @@ INCLUDE_PATH; $file .= <<= $staticPhpVersion && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if (\$useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit$suffix::getInitializer(\$loader)); } else { 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 bbcf0f5e2..ab3dd2758 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 @@ -30,7 +30,7 @@ class ComposerAutoloaderInitFilesAutoloadOrder $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitFilesAutoloadOrder::getInitializer($loader)); } else { diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index 2474f5f0e..27dc60bf6 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -30,7 +30,7 @@ class ComposerAutoloaderInitFilesAutoload $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitFilesAutoload::getInitializer($loader)); } else { 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 0a3eeb7c3..be7dd7907 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 @@ -34,7 +34,7 @@ class ComposerAutoloaderInitFilesAutoload $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitFilesAutoload::getInitializer($loader)); } else { 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 123db2a2d..c3617696b 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 @@ -30,7 +30,7 @@ class ComposerAutoloaderInitFilesAutoload $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitFilesAutoload::getInitializer($loader)); } else { 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 bbe4562da..7639b4336 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -30,7 +30,7 @@ class ComposerAutoloaderInitIncludePath $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitIncludePath::getInitializer($loader)); } else { 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 eeec70417..0d9685d71 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -30,7 +30,7 @@ class ComposerAutoloaderInitTargetDir $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; + require __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInitTargetDir::getInitializer($loader)); } else { From b21936ab46b5dcc75da84066f39b602e50d02201 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 22 Apr 2020 17:08:33 +0200 Subject: [PATCH 050/193] Update scripts.md --- doc/articles/scripts.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/articles/scripts.md b/doc/articles/scripts.md index 9f4504ae3..bbaea9c89 100644 --- a/doc/articles/scripts.md +++ b/doc/articles/scripts.md @@ -375,6 +375,7 @@ You can set custom script descriptions with the following in your `composer.json } ``` -The descriptions are seen in `composer run -l` command output. +The descriptions are used in `composer list` or `composer run -l` commands to +describe what the scripts do when the command is run. > **Note:** You can only set custom descriptions of custom commands. From fa14cd135322e79f9feb5ff28f9e7bde61398a84 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 10:23:56 +0200 Subject: [PATCH 051/193] Show number of additional suggestions when not using --all, fixes #8788 --- src/Composer/Command/SuggestsCommand.php | 11 ++--- .../Installer/SuggestedPackagesReporter.php | 42 ++++++++++++++----- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index 61875ba0b..2ae4f4414 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -14,7 +14,7 @@ namespace Composer\Command; use Composer\Repository\PlatformRepository; use Composer\Repository\RootPackageRepository; -use Composer\Repository\CompositeRepository; +use Composer\Repository\InstalledRepository; use Composer\Installer\SuggestedPackagesReporter; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -67,15 +67,10 @@ EOT $installedRepos[] = $composer->getRepositoryManager()->getLocalRepository(); } - $installedRepo = new CompositeRepository($installedRepos); + $installedRepo = new InstalledRepository($installedRepos); $reporter = new SuggestedPackagesReporter($this->getIO()); $filter = $input->getArgument('packages'); - if (empty($filter) && !$input->getOption('all')) { - $filter = array_map(function ($link) { - return $link->getTarget(); - }, array_merge($composer->getPackage()->getRequires(), $composer->getPackage()->getDevRequires())); - } foreach ($installedRepo->getPackages() as $package) { if (!empty($filter) && !in_array($package->getName(), $filter)) { continue; @@ -100,7 +95,7 @@ EOT $mode = SuggestedPackagesReporter::MODE_LIST; } - $reporter->output($mode, $installedRepo); + $reporter->output($mode, $installedRepo, empty($filter) && !$input->getOption('all') ? $composer->getPackage() : null); return 0; } diff --git a/src/Composer/Installer/SuggestedPackagesReporter.php b/src/Composer/Installer/SuggestedPackagesReporter.php index e70422d14..804873883 100644 --- a/src/Composer/Installer/SuggestedPackagesReporter.php +++ b/src/Composer/Installer/SuggestedPackagesReporter.php @@ -14,7 +14,7 @@ namespace Composer\Installer; use Composer\IO\IOInterface; use Composer\Package\PackageInterface; -use Composer\Repository\RepositoryInterface; +use Composer\Repository\InstalledRepository; use Symfony\Component\Console\Formatter\OutputFormatter; /** @@ -98,12 +98,14 @@ class SuggestedPackagesReporter * * Do not list the ones already installed if installed repository provided. * - * @param int $mode One of the MODE_* constants from this class + * @param int $mode One of the MODE_* constants from this class + * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package will be shown * @return SuggestedPackagesReporter */ - public function output($mode, RepositoryInterface $installedRepo = null) + public function output($mode, InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) { - $suggestedPackages = $this->getFilteredSuggestions($installedRepo); + $suggestedPackages = $this->getFilteredSuggestions($installedRepo, $onlyDependentsOf); $suggesters = array(); $suggested = array(); @@ -151,19 +153,27 @@ class SuggestedPackagesReporter } } + if ($onlyDependentsOf) { + $allSuggestedPackages = $this->getFilteredSuggestions($installedRepo); + $diff = count($allSuggestedPackages) - count($suggestedPackages); + if ($diff) { + $this->io->write(''.$diff.' additional suggestions by transitive dependencies can be shown with --all'); + } + } + return $this; } /** * Output number of new suggested packages and a hint to use suggest command. - ** - * Do not list the ones already installed if installed repository provided. * + * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package will be shown * @return SuggestedPackagesReporter */ - public function outputMinimalistic(RepositoryInterface $installedRepo = null) + public function outputMinimalistic(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) { - $suggestedPackages = $this->getFilteredSuggestions($installedRepo); + $suggestedPackages = $this->getFilteredSuggestions($installedRepo, $onlyDependentsOf); if ($suggestedPackages) { $this->io->writeError(''.count($suggestedPackages).' package suggestions were added by new dependencies, use `composer suggest` to see details.'); } @@ -171,7 +181,12 @@ class SuggestedPackagesReporter return $this; } - private function getFilteredSuggestions(RepositoryInterface $installedRepo = null) + /** + * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package will be shown + * @return array[] + */ + private function getFilteredSuggestions(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) { $suggestedPackages = $this->getPackages(); $installedNames = array(); @@ -184,9 +199,16 @@ class SuggestedPackagesReporter } } + $sourceFilter = array(); + if ($onlyDependentsOf) { + $sourceFilter = array_map(function ($link) { + return $link->getTarget(); + }, array_merge($onlyDependentsOf->getRequires(), $onlyDependentsOf->getDevRequires())); + } + $suggestions = array(); foreach ($suggestedPackages as $suggestion) { - if (in_array($suggestion['target'], $installedNames)) { + if (in_array($suggestion['target'], $installedNames) || ($sourceFilter && !in_array($suggestion['source'], $sourceFilter))) { continue; } From 8a1e9744a0366512824f44d2e3a2f70a20c94f03 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 10:29:22 +0200 Subject: [PATCH 052/193] Fix tests --- .../Composer/Test/Installer/SuggestedPackagesReporterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php b/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php index 286b386d9..fb8003306 100644 --- a/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php +++ b/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php @@ -222,7 +222,7 @@ class SuggestedPackagesReporterTest extends TestCase */ public function testOutputSkipInstalledPackages() { - $repository = $this->getMockBuilder('Composer\Repository\RepositoryInterface')->getMock(); + $repository = $this->getMockBuilder('Composer\Repository\InstalledRepository')->disableOriginalConstructor()->getMock(); $package1 = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); $package2 = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock(); @@ -260,7 +260,7 @@ class SuggestedPackagesReporterTest extends TestCase */ public function testOutputNotGettingInstalledPackagesWhenNoSuggestions() { - $repository = $this->getMockBuilder('Composer\Repository\RepositoryInterface')->getMock(); + $repository = $this->getMockBuilder('Composer\Repository\InstalledRepository')->disableOriginalConstructor()->getMock(); $repository->expects($this->exactly(0)) ->method('getPackages'); From 6529fabb245f678379d4030d27996a3a8f10a30e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 10:52:33 +0200 Subject: [PATCH 053/193] Add isFresh to InstalledRepositoryInterface and make sure local repo is always an InstalledRepositoryInterface --- src/Composer/Repository/FilesystemRepository.php | 2 +- src/Composer/Repository/InstalledArrayRepository.php | 10 ++++++++++ .../Repository/InstalledFilesystemRepository.php | 8 ++++++++ .../Repository/InstalledRepositoryInterface.php | 4 ++++ src/Composer/Repository/RepositoryManager.php | 6 +++--- 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index dcd94f7b3..4bf4cb6ca 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -28,7 +28,7 @@ use Composer\Util\Filesystem; */ class FilesystemRepository extends WritableArrayRepository { - private $file; + protected $file; private $dumpVersions; private $rootPackage; diff --git a/src/Composer/Repository/InstalledArrayRepository.php b/src/Composer/Repository/InstalledArrayRepository.php index de1dd67d8..e8a5f8194 100644 --- a/src/Composer/Repository/InstalledArrayRepository.php +++ b/src/Composer/Repository/InstalledArrayRepository.php @@ -25,4 +25,14 @@ class InstalledArrayRepository extends WritableArrayRepository implements Instal { return 'installed '.parent::getRepoName(); } + + /** + * {@inheritDoc} + */ + public function isFresh() + { + // this is not a completely correct implementation but there is no way to + // distinguish an empty repo and a newly created one given this is all in-memory + return $this->count() === 0; + } } diff --git a/src/Composer/Repository/InstalledFilesystemRepository.php b/src/Composer/Repository/InstalledFilesystemRepository.php index bf81734d4..f526f31c2 100644 --- a/src/Composer/Repository/InstalledFilesystemRepository.php +++ b/src/Composer/Repository/InstalledFilesystemRepository.php @@ -23,4 +23,12 @@ class InstalledFilesystemRepository extends FilesystemRepository implements Inst { return 'installed '.parent::getRepoName(); } + + /** + * {@inheritDoc} + */ + public function isFresh() + { + return !$this->file->exists(); + } } diff --git a/src/Composer/Repository/InstalledRepositoryInterface.php b/src/Composer/Repository/InstalledRepositoryInterface.php index 19b095b2a..b5d8a264e 100644 --- a/src/Composer/Repository/InstalledRepositoryInterface.php +++ b/src/Composer/Repository/InstalledRepositoryInterface.php @@ -21,4 +21,8 @@ namespace Composer\Repository; */ interface InstalledRepositoryInterface extends WritableRepositoryInterface { + /** + * @return bool true if packages were never installed in this repository + */ + public function isFresh(); } diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index c5da49cdb..264115c10 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -163,9 +163,9 @@ class RepositoryManager /** * Sets local repository for the project. * - * @param WritableRepositoryInterface $repository repository instance + * @param InstalledRepositoryInterface $repository repository instance */ - public function setLocalRepository(WritableRepositoryInterface $repository) + public function setLocalRepository(InstalledRepositoryInterface $repository) { $this->localRepository = $repository; } @@ -173,7 +173,7 @@ class RepositoryManager /** * Returns local repository for the project. * - * @return WritableRepositoryInterface + * @return InstalledRepositoryInterface */ public function getLocalRepository() { From 9b8694bc0b3a227a336b119e45cf4cc55974c8e3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 10:53:11 +0200 Subject: [PATCH 054/193] Show suggestions from root package in suggest, and lists them in count only on first install of a project, fixes #8805 --- src/Composer/Command/SuggestsCommand.php | 4 +++- src/Composer/Installer.php | 5 +++++ src/Composer/Installer/SuggestedPackagesReporter.php | 7 ++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/SuggestsCommand.php b/src/Composer/Command/SuggestsCommand.php index 2ae4f4414..d874d1d19 100644 --- a/src/Composer/Command/SuggestsCommand.php +++ b/src/Composer/Command/SuggestsCommand.php @@ -71,7 +71,9 @@ EOT $reporter = new SuggestedPackagesReporter($this->getIO()); $filter = $input->getArgument('packages'); - foreach ($installedRepo->getPackages() as $package) { + $packages = $installedRepo->getPackages(); + $packages[] = $composer->getPackage(); + foreach ($packages as $package) { if (!empty($filter) && !in_array($package->getName(), $filter)) { continue; } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index bf33693b3..1a9d4fc4f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -203,6 +203,8 @@ class Installer throw new \RuntimeException("The installer options updateMirrors and updateAllowList are mutually exclusive."); } + $isFreshInstall = $this->repositoryManager->getLocalRepository()->isFresh(); + // Force update if there is no lock file present if (!$this->update && !$this->locker->isLocked()) { $this->io->writeError('No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.'); @@ -264,6 +266,9 @@ class Installer $this->createPlatformRepo(false), new RootPackageRepository(clone $this->package), )); + if ($isFreshInstall) { + $this->suggestedPackagesReporter->addSuggestionsFromPackage($this->package); + } $this->suggestedPackagesReporter->outputMinimalistic($installedRepo); } diff --git a/src/Composer/Installer/SuggestedPackagesReporter.php b/src/Composer/Installer/SuggestedPackagesReporter.php index 804873883..4d8de6086 100644 --- a/src/Composer/Installer/SuggestedPackagesReporter.php +++ b/src/Composer/Installer/SuggestedPackagesReporter.php @@ -100,7 +100,7 @@ class SuggestedPackagesReporter * * @param int $mode One of the MODE_* constants from this class * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped - * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package will be shown + * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown * @return SuggestedPackagesReporter */ public function output($mode, InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) @@ -168,7 +168,7 @@ class SuggestedPackagesReporter * Output number of new suggested packages and a hint to use suggest command. * * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped - * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package will be shown + * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown * @return SuggestedPackagesReporter */ public function outputMinimalistic(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) @@ -183,7 +183,7 @@ class SuggestedPackagesReporter /** * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped - * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package will be shown + * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown * @return array[] */ private function getFilteredSuggestions(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) @@ -204,6 +204,7 @@ class SuggestedPackagesReporter $sourceFilter = array_map(function ($link) { return $link->getTarget(); }, array_merge($onlyDependentsOf->getRequires(), $onlyDependentsOf->getDevRequires())); + $sourceFilter[] = $onlyDependentsOf->getName(); } $suggestions = array(); From cdc6a87e8d308c650ba844f93b8f987f2910fb0c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 11:01:44 +0200 Subject: [PATCH 055/193] Fix docblock --- src/Composer/Installer/SuggestedPackagesReporter.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Installer/SuggestedPackagesReporter.php b/src/Composer/Installer/SuggestedPackagesReporter.php index 4d8de6086..56fe4599f 100644 --- a/src/Composer/Installer/SuggestedPackagesReporter.php +++ b/src/Composer/Installer/SuggestedPackagesReporter.php @@ -99,8 +99,8 @@ class SuggestedPackagesReporter * Do not list the ones already installed if installed repository provided. * * @param int $mode One of the MODE_* constants from this class - * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped - * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown + * @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown * @return SuggestedPackagesReporter */ public function output($mode, InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) @@ -167,8 +167,8 @@ class SuggestedPackagesReporter /** * Output number of new suggested packages and a hint to use suggest command. * - * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped - * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown + * @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown * @return SuggestedPackagesReporter */ public function outputMinimalistic(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) @@ -182,8 +182,8 @@ class SuggestedPackagesReporter } /** - * @param InstalledRepository $installedRepo If passed in, suggested packages which are installed already will be skipped - * @param PackageInterface $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown + * @param InstalledRepository|null $installedRepo If passed in, suggested packages which are installed already will be skipped + * @param PackageInterface|null $onlyDependentsOf If passed in, only the suggestions from direct dependents of that package, or from the package itself, will be shown * @return array[] */ private function getFilteredSuggestions(InstalledRepository $installedRepo = null, PackageInterface $onlyDependentsOf = null) From 8f8840bfda959d1d2d58ba61f8eb9147c4c20a0d Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Thu, 23 Apr 2020 11:02:50 +0200 Subject: [PATCH 056/193] Update 04-schema.md to reflect package naming in 2.0 (#8777) --- UPGRADE-2.0.md | 2 +- doc/04-schema.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 3a5928efc..ae2e9b55f 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -4,7 +4,7 @@ - If a packages exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. - Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10 -- Package names now must comply to our naming guidelines or Composer will abort, as per the warnings introduced in 1.8.1 +- Package names now must comply to our [naming guidelines](doc/04-schema.md#name) or Composer will abort, as per the warnings introduced in 1.8.1 - Deprecated --no-suggest flag as it is not needed anymore - `update` now lists changes to the lock file first, and then the changes applied when installing the lock file to the vendor dir - `HTTPS_PROXY_REQUEST_FULLURI` if not specified will now default to false as this seems to work better in most environments diff --git a/doc/04-schema.md b/doc/04-schema.md index 7b67f4114..130394e7a 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -34,12 +34,12 @@ separated by `/`. Examples: * monolog/monolog * igorw/event-source -The name can contain any character, including white spaces, and it's case -insensitive (`foo/bar` and `Foo/Bar` are considered the same package). In order -to simplify its installation, it's recommended to define a short and lowercase -name that doesn't include non-alphanumeric characters or white spaces. +The name must be lowercased and consist of words separated by `-`, `.` or `_`. +The complete name should match `^[a-z0-9]([_.-]?[a-z0-9]+)*/[a-z0-9](([_.]?|-{0,2})[a-z0-9]+)*$`. -Required for published packages (libraries). +The `name` property is required for published packages (libraries). + +> **Note:** Before Composer version 2.0, a name could contain any character, including white spaces. ### description From b67dd2e201aabffe8eab0066513705b7a112648a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 11:14:08 +0200 Subject: [PATCH 057/193] Add coc file to appease the github UI --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..c93e4a6e0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at contact@packagist.org. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From d8678fc8f21eb5fdce6970fcfc031e19e055778d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 11:14:42 +0200 Subject: [PATCH 058/193] Update CONTRIBUTING.md --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index eee01015b..8f525a97f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,7 @@ Contributing to Composer ======================== Please note that this project is released with a -[Contributor Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct/). +[Contributor Code of Conduct](https://github.com/composer/composer/blob/master/CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. Reporting Issues From 34801239f11a4472318a9981b27cb33395553677 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 13:17:22 +0200 Subject: [PATCH 059/193] Tweak problem wording slightly --- src/Composer/DependencyResolver/Problem.php | 2 +- tests/Composer/Test/DependencyResolver/SolverTest.php | 2 +- .../Fixtures/installer/root-alias-change-with-circular-dep.test | 2 +- tests/Composer/Test/Fixtures/installer/solver-problems.test | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 1754ef82c..9e8f02586 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -260,7 +260,7 @@ class Problem return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', it is ', 'satisfiable by '.self::getPackageList($nextRepoPackages, $isVerbose).' from '.$nextRepo->getRepoName().' but '.self::getPackageList($higherRepoPackages, $isVerbose).' from '.reset($higherRepoPackages)->getRepository()->getRepoName().' has higher repository priority. The packages with higher priority do not match your constraint and are therefore not installable. See https://getcomposer.org/repoprio for details and assistance.'); } - return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match your constraint.'); + return array("- Root composer.json requires $packageName".self::constraintToText($constraint) . ', ', 'found '.self::getPackageList($packages, $isVerbose).' but '.(self::hasMultipleNames($packages) ? 'these do' : 'it does').' not match the constraint.'); } if (!preg_match('{^[A-Za-z0-9_./-]+$}', $packageName)) { diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 423a4bf1b..b7d0e7bc1 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -684,7 +684,7 @@ class SolverTest extends TestCase $msg = "\n"; $msg .= " Problem 1\n"; $msg .= " - Root composer.json requires a -> satisfiable by A[1.0].\n"; - $msg .= " - A 1.0 requires b >= 2.0 -> found B[1.0] but it does not match your constraint.\n"; + $msg .= " - A 1.0 requires b >= 2.0 -> found B[1.0] but it does not match the constraint.\n"; $this->assertEquals($msg, $e->getPrettyString($this->repoSet, $this->request, $this->pool, false)); } } diff --git a/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test b/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test index 91fad5662..07c8cdfd8 100644 --- a/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test +++ b/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test @@ -60,7 +60,7 @@ Your lock file does not contain a compatible set of packages. Please run compose Problem 1 - b/requirer is locked to version 1.0.0 and an update of this package was not requested. - - b/requirer 1.0.0 requires root/pkg ^1 -> found root/pkg[2.x-dev] but it does not match your constraint. + - b/requirer 1.0.0 requires root/pkg ^1 -> found root/pkg[2.x-dev] but it does not match the constraint. Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions. diff --git a/tests/Composer/Test/Fixtures/installer/solver-problems.test b/tests/Composer/Test/Fixtures/installer/solver-problems.test index 9c3d0e534..06f4c1ba7 100644 --- a/tests/Composer/Test/Fixtures/installer/solver-problems.test +++ b/tests/Composer/Test/Fixtures/installer/solver-problems.test @@ -140,7 +140,7 @@ Your requirements could not be resolved to an installable set of packages. - package/found4 2.0.0 requires non-existent/pkg2 1.* -> could not be found in any version, there may be a typo in the package name. Problem 13 - Root composer.json requires package/found6 2.* -> satisfiable by package/found6[2.0.0]. - - package/found6 2.0.0 requires stable-requiree-excluded/pkg2 1.0.1 -> found stable-requiree-excluded/pkg2[1.0.0] but it does not match your constraint. + - package/found6 2.0.0 requires stable-requiree-excluded/pkg2 1.0.1 -> found stable-requiree-excluded/pkg2[1.0.0] but it does not match the constraint. Problem 14 - Root composer.json requires package/found7 2.* -> satisfiable by package/found7[2.0.0]. - package/found7 2.0.0 requires php-64bit 1.0.1 -> your php-64bit version (%s) does not satisfy that requirement. From 1d4185b844dd22718562fcfe77d3ad785aa2c8fa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 13:51:57 +0200 Subject: [PATCH 060/193] Recommend ocramius/package-versions fork if needed to solve dependencies --- src/Composer/DependencyResolver/SolverProblemsException.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 082b4c4f7..2b58b707a 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -66,6 +66,10 @@ class SolverProblemsException extends \RuntimeException $text .= "\nUse the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions."; } + if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match') && strpos($text, '- ocramius/package-versions')) { + $text .= "\nocramius/package-versions only provides support for Composer 2 in 1.8+, which requires PHP 7.4.\nIf you can not upgrade PHP you can require composer/package-versions-deprecated to resolve this with PHP 7.0+.\n"; + } + // TODO remove before 2.0 final if (!class_exists('PHPUnit\Framework\TestCase', false)) { if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) { From 46efcefefa744bc0712bda6011930778d9d4ed8b Mon Sep 17 00:00:00 2001 From: effulgentsia Date: Thu, 23 Apr 2020 05:27:14 -0700 Subject: [PATCH 061/193] Added --json and --merge options to ConfigCommand (#8779) --- doc/03-cli.md | 9 +++++++++ src/Composer/Command/ConfigCommand.php | 22 +++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 3a964c390..217d2ea0a 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -605,6 +605,8 @@ See the [Config](06-config.md) chapter for valid configuration options. that this cannot be used in conjunction with the `--global` option. * **--absolute:** Returns absolute paths when fetching *-dir config values instead of relative. +* **--json:** JSON decode the setting value, to be used with `extra.*` keys. +* **--merge:** Merge the setting value with the current value, to be used with `extra.*` keys in combination with `--json`. ### Modifying Repositories @@ -633,6 +635,13 @@ php composer.phar config extra.foo.bar value The dots indicate array nesting, a max depth of 3 levels is allowed though. The above would set `"extra": { "foo": { "bar": "value" } }`. +If you have a complex value to add/modify, you can use the `--json` and `--merge` flags +to edit extra fields as json: + +```sh +php composer.phar config --json extra.foo.bar '{"baz": true, "qux": []}' +``` + ## create-project You can use Composer to create new projects from an existing package. This is diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 8c186e407..099e5c872 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -73,6 +73,8 @@ class ConfigCommand extends BaseCommand new InputOption('list', 'l', InputOption::VALUE_NONE, 'List configuration settings'), new InputOption('file', 'f', InputOption::VALUE_REQUIRED, 'If you want to choose a different composer.json or config.json'), new InputOption('absolute', null, InputOption::VALUE_NONE, 'Returns absolute paths when fetching *-dir config values instead of relative'), + new InputOption('json', 'j', InputOption::VALUE_NONE, 'JSON decode the setting value, to be used with extra.* keys'), + new InputOption('merge', 'm', InputOption::VALUE_NONE, 'Merge the setting value with the current value, to be used with extra.* keys in combination with --json'), new InputArgument('setting-key', null, 'Setting key'), new InputArgument('setting-value', InputArgument::IS_ARRAY, 'Setting value'), )) @@ -119,6 +121,10 @@ To add or edit extra properties you can use: %command.full_name% extra.property value +Or to add a complex value you can use json with: + + %command.full_name% extra.property --json '{"foo":true, "bar": []}' + To edit the file in an external editor: %command.full_name% --editor @@ -622,7 +628,21 @@ EOT return 0; } - $this->configSource->addProperty($settingKey, $values[0]); + $value = $values[0]; + if ($input->getOption('json')) { + $value = JsonFile::parseJson($value); + if ($input->getOption('merge')) { + $currentValue = $this->configFile->read(); + $bits = explode('.', $settingKey); + foreach ($bits as $bit) { + $currentValue = isset($currentValue[$bit]) ? $currentValue[$bit] : null; + } + if (is_array($currentValue)) { + $value = array_merge($currentValue, $value); + } + } + } + $this->configSource->addProperty($settingKey, $value); return 0; } From 3a7ea25289c4a73b02df2ed484bafce570dfaba2 Mon Sep 17 00:00:00 2001 From: PirxDanford Date: Wed, 4 Mar 2020 18:30:50 +0100 Subject: [PATCH 062/193] Introduce --no-install option for the update, require and remove commands Closes #8669, fixes #8551 Co-authored-by: Jordi Boggiano --- doc/03-cli.md | 7 +++++-- src/Composer/Command/InstallCommand.php | 7 +++++++ src/Composer/Command/RemoveCommand.php | 4 +++- src/Composer/Command/RequireCommand.php | 4 +++- src/Composer/Command/UpdateCommand.php | 2 ++ src/Composer/Installer.php | 27 ++++++++++++++++++++----- 6 files changed, 42 insertions(+), 9 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 217d2ea0a..37a3f5723 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -149,6 +149,7 @@ php composer.phar update "vendor/*" * **--dry-run:** Simulate the command without actually doing anything. * **--dev:** Install packages listed in `require-dev` (this is the default behavior). * **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules. +* **--no-install:** Does not run the install step after updating the composer.lock file. * **--lock:** Only updates the lock file hash to suppress warning about the lock file being out of date. * **--no-autoloader:** Skips autoloader generation. @@ -201,7 +202,8 @@ If you do not specify a package, composer will prompt you to search for a packag * **--prefer-dist:** Install packages from `dist` when available. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-update:** Disables the automatic update of the dependencies. +* **--no-update:** Disables the automatic update of the dependencies (implies --no-install). +* **--no-install:** Does not run the install step after updating the composer.lock file. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the `--no-dev` option. * **--update-with-dependencies:** Also update dependencies of the newly required packages, except those that are root requirements. @@ -237,7 +239,8 @@ uninstalled. * **--dry-run:** Simulate the command without actually doing anything. * **--no-progress:** Removes the progress display that can mess with some terminals or scripts which don't handle backspace characters. -* **--no-update:** Disables the automatic update of the dependencies. +* **--no-update:** Disables the automatic update of the dependencies (implies --no-install). +* **--no-install:** Does not run the install step after updating the composer.lock file. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the --no-dev option. * **--update-with-dependencies:** Also update dependencies of the removed packages. diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 033f32195..5f5565248 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -44,6 +44,7 @@ class InstallCommand extends BaseCommand new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Do not use, only defined here to catch misuse of the install command.'), new InputOption('verbose', 'v|vv|vvv', InputOption::VALUE_NONE, 'Shows more details including new commits pulled in when updating packages.'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), @@ -82,6 +83,12 @@ EOT return 1; } + if ($input->getOption('no-install')) { + $io->writeError('Invalid option "--no-install". Use "composer update --no-install" instead if you are trying to update the composer.lock file.'); + + return 1; + } + $composer = $this->getComposer(true, $input->getOption('no-plugins')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output); diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 452b30585..7e4c7ed49 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -41,7 +41,8 @@ class RemoveCommand extends BaseCommand new InputOption('dev', null, InputOption::VALUE_NONE, 'Removes a package from the require-dev section.'), new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies (implies --no-install).'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated with explicit dependencies. (Deprecrated, is now default behavior)'), @@ -237,6 +238,7 @@ EOT ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) ->setUpdate(true) + ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 67188504d..7e290aca6 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -61,7 +61,8 @@ class RequireCommand extends InitCommand new InputOption('fixed', null, InputOption::VALUE_NONE, 'Write fixed version to the composer.json.'), new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'), new InputOption('no-progress', null, InputOption::VALUE_NONE, 'Do not output download progress.'), - new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies.'), + new InputOption('no-update', null, InputOption::VALUE_NONE, 'Disables the automatic update of the dependencies (implies --no-install).'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('update-no-dev', null, InputOption::VALUE_NONE, 'Run the dependency update with the --no-dev option.'), new InputOption('update-with-dependencies', null, InputOption::VALUE_NONE, 'Allows inherited dependencies to be updated, except those that are root requirements.'), @@ -284,6 +285,7 @@ EOT ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) ->setUpdate(true) + ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setPreferStable($input->getOption('prefer-stable')) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 9c875a57d..4bb15fc9b 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -45,6 +45,7 @@ class UpdateCommand extends BaseCommand new InputOption('dev', null, InputOption::VALUE_NONE, 'DEPRECATED: Enables installation of require-dev packages (enabled by default, only present for BC).'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables installation of require-dev packages.'), new InputOption('lock', null, InputOption::VALUE_NONE, 'Only updates the lock file hash to suppress warning about the lock file being out of date.'), + new InputOption('no-install', null, InputOption::VALUE_NONE, 'Skip the install step after updating the composer.lock file.'), new InputOption('no-autoloader', null, InputOption::VALUE_NONE, 'Skips autoloader generation'), new InputOption('no-scripts', null, InputOption::VALUE_NONE, 'Skips the execution of all scripts defined in composer.json file.'), new InputOption('no-suggest', null, InputOption::VALUE_NONE, 'DEPRECATED: This flag does not exist anymore.'), @@ -163,6 +164,7 @@ EOT ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) ->setUpdate(true) + ->setInstall(!$input->getOption('no-install')) ->setUpdateMirrors($updateMirrors) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 1a9d4fc4f..2bca0618b 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -129,6 +129,7 @@ class Installer protected $dryRun = false; protected $verbose = false; protected $update = false; + protected $install = true; protected $dumpAutoloader = true; protected $runScripts = true; protected $ignorePlatformReqs = false; @@ -220,6 +221,10 @@ class Installer $this->mockLocalRepositories($this->repositoryManager); } + if ($this->update && !$this->install) { + $this->dumpAutoloader = false; + } + if ($this->runScripts) { $_SERVER['COMPOSER_DEV_MODE'] = (int) $this->devMode; putenv('COMPOSER_DEV_MODE='.$_SERVER['COMPOSER_DEV_MODE']); @@ -241,8 +246,7 @@ class Installer try { if ($this->update) { - // TODO introduce option to set doInstall to false (update lock file without vendor install) - $res = $this->doUpdate($localRepo, true); + $res = $this->doUpdate($localRepo, $this->install); } else { $res = $this->doInstall($localRepo); } @@ -250,13 +254,13 @@ class Installer return $res; } } catch (\Exception $e) { - if ($this->executeOperations && $this->config->get('notify-on-install')) { + if ($this->executeOperations && $this->install && $this->config->get('notify-on-install')) { $this->installationManager->notifyInstalls($this->io); } throw $e; } - if ($this->executeOperations && $this->config->get('notify-on-install')) { + if ($this->executeOperations && $this->install && $this->config->get('notify-on-install')) { $this->installationManager->notifyInstalls($this->io); } @@ -307,7 +311,7 @@ class Installer $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); } - if ($this->executeOperations) { + if ($this->install && $this->executeOperations) { // force binaries re-generation in case they are missing foreach ($localRepo->getPackages() as $package) { $this->installationManager->ensureBinariesPresence($package); @@ -1018,6 +1022,19 @@ class Installer return $this; } + /** + * Allows disabling the install step after an update + * + * @param bool $install + * @return Installer + */ + public function setInstall($install = true) + { + $this->install = (bool) $install; + + return $this; + } + /** * enables dev packages * From df0cada93f1c1f14e2503f1d943824cf27535acb Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 15:01:32 +0200 Subject: [PATCH 063/193] Add test for #8669 --- .../Fixtures/installer/update-no-install.test | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/update-no-install.test diff --git a/tests/Composer/Test/Fixtures/installer/update-no-install.test b/tests/Composer/Test/Fixtures/installer/update-no-install.test new file mode 100644 index 000000000..acf378675 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/update-no-install.test @@ -0,0 +1,64 @@ +--TEST-- +Updates without install updates the lock but not the installed state +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.0" }, + { "name": "a/a", "version": "1.0.1" }, + { "name": "a/a", "version": "1.1.0" }, + + { "name": "a/b", "version": "1.0.0" }, + { "name": "a/b", "version": "1.0.1" }, + { "name": "a/b", "version": "2.0.0" }, + + { "name": "a/c", "version": "1.0.0" }, + { "name": "a/c", "version": "2.0.0" } + ] + } + ], + "require": { + "a/a": "1.0.*", + "a/c": "1.*" + }, + "require-dev": { + "a/b": "*" + } +} +--INSTALLED-- +[ + { "name": "a/a", "version": "1.0.0" }, + { "name": "a/c", "version": "1.0.0" }, + { "name": "a/b", "version": "1.0.0" } +] +--RUN-- +update --no-install + +--EXPECT-LOCK-- +{ + "packages": [ + { "name": "a/a", "version": "1.0.1", "type": "library" }, + { "name": "a/c", "version": "1.0.0", "type": "library" } + ], + "packages-dev": [ + { "name": "a/b", "version": "2.0.0", "type": "library" } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} + +--EXPECT-INSTALLED-- +[ + { "name": "a/a", "version": "1.0.0" }, + { "name": "a/c", "version": "1.0.0" }, + { "name": "a/b", "version": "1.0.0" } +] + +--EXPECT-- From 755a73baa96b8530908e1d1e2ad84b7eecb29b2c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 23 Apr 2020 20:44:14 +0200 Subject: [PATCH 064/193] Only check for pcntl on cli SAPI, fixes #8828 --- src/Composer/Autoload/AutoloadGenerator.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 44279bc04..c3cffa839 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -588,7 +588,11 @@ EOF; if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { $extension = var_export($match[1], true); - $requiredExtensions[$extension] = "extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; + if ($match[1] === 'pcntl') { + $requiredExtensions[$extension] = "PHP_SAPI !== 'cli' || extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; + } else { + $requiredExtensions[$extension] = "extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; + } } } } From d89342dc434d52c88e0e06ce3982da739a467f13 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Apr 2020 09:07:32 +0200 Subject: [PATCH 065/193] Fix tests --- .../Composer/Test/Fixtures/installer/update-no-install.test | 6 +++--- tests/Composer/Test/InstallerTest.php | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/Composer/Test/Fixtures/installer/update-no-install.test b/tests/Composer/Test/Fixtures/installer/update-no-install.test index acf378675..e1b6f4954 100644 --- a/tests/Composer/Test/Fixtures/installer/update-no-install.test +++ b/tests/Composer/Test/Fixtures/installer/update-no-install.test @@ -56,9 +56,9 @@ update --no-install --EXPECT-INSTALLED-- [ - { "name": "a/a", "version": "1.0.0" }, - { "name": "a/c", "version": "1.0.0" }, - { "name": "a/b", "version": "1.0.0" } + { "name": "a/a", "version": "1.0.0", "type": "library" }, + { "name": "a/b", "version": "1.0.0", "type": "library" }, + { "name": "a/c", "version": "1.0.0", "type": "library" } ] --EXPECT-- diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index f9e1b1793..78c20a1be 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -290,6 +290,7 @@ class InstallerTest extends TestCase $installer ->setDevMode(!$input->getOption('no-dev')) ->setUpdate(true) + ->setInstall(!$input->getOption('no-install')) ->setDryRun($input->getOption('dry-run')) ->setUpdateMirrors($updateMirrors) ->setUpdateAllowList($packages) From f34ec132532050c2c5b092ab44813c4ef3eb1837 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Apr 2020 11:11:32 +0200 Subject: [PATCH 066/193] Upadte changelog/upgrade to latest master state --- CHANGELOG.md | 10 +++++++++- UPGRADE-2.0.md | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47211a0a6..13cb7866e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,26 @@ * The update command is now much more deterministic as it does not take the already installed packages into account * Package installation now performs all network operations first before doing any changes on disk, to reduce the chances of ending up with a partially updated vendor dir * Partial updates and require/remove are now much faster as they only load the metadata required for the updated packages - * Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present + * Added a platform-check step when vendor/autoload.php gets initialized which checks the current PHP version/extensions match what is expected and fails hard otherwise. Can be disabled with the platform-check config option + * Added a [`Composer\InstalledVersions`](https://github.com/composer/composer/blob/d89342dc434d52c88e0e06ce3982da739a467f13/src/Composer/InstalledVersions.php) class which is autoloaded in every project and lets you check which packages/versions are present at runtime + * Added a `composer-runtime-api` virtual package which you can require (as e.g. `^2.0`) to ensure things like the InstalledVersions class above are present. It will effectively force people to use Composer 2.x to install your project + * Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present and we thus strongly recommend enabling curl * Added much clearer dependency resolution error reporting for common error cases * Added support for TTY mode on Linux/OSX/WSL so that script handlers now run in interactive mode * Added `only`, `exclude` and `canonical` options to all repositories, see [repository priorities](https://getcomposer.org/repoprio) for details * Added support for lib-zip platform package * Added `pre-operations-exec` event to be fired before the packages get installed/upgraded/removed * Added `pre-pool-create` event to be fired before the package pool for the dependency solver is created, which lets you modify the list of packages going in + * Added `post-file-download` event to be fired after package dist files are downloaded, which lets you do additional checks on the files + * Added --unused flag to `remove` command to make sure any packages which are not needed anymore get removed * Added --dry-run flag to `require` and `remove` commands + * Added --no-install flag to `update`, `require` and `remove` commands to disable the install step and only do the update step (composer.lock file update) * Added --with-dependencies and --with-all-dependencies flag aliases to `require` and `remove` commands for consistency with `update` * Added more info to `vendor/composer/installed.json`, a dev key stores whether dev requirements were installed, and every package now has an install-path key with its install location * Added COMPOSER_DISABLE_NETWORK which if set makes Composer do its best to run offline. This can be useful when you have poor connectivity or to do benchmarking without network jitter + * Added --json and --merge flags to `config` command to allow editing complex `extra.*` values by using json as input * Added confirmation prompt when running Composer as superuser in interactive mode + * Added --no-check-version to `validate` command to remove the warning in case the version is defined * Fixed suggest output being very spammy, it now is only one line long and shows more rarely * Fixed conflict rules like e.g. >=5 from matching dev-master, as it is not normalized to 9999999-dev internally anymore diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index ae2e9b55f..c8bcedf70 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -2,11 +2,13 @@ ## For composer CLI users +- The new platform-check feature means that Composer checks the runtime PHP version and available extensions to ensure they match the project dependencies. If a mismatch is found, it exits with error details to make sure problems are not overlooked. To avoid issues when deploying to production it is recommended to run `composer check-platform-reqs` with the production PHP process as part of your build or deployment process. - If a packages exists in a higher priority repository, it will now be entirely ignored in lower priority repositories. See [repository priorities](https://getcomposer.org/repoprio) for details. - Invalid PSR-0 / PSR-4 class configurations will not autoload anymore in optimized-autoloader mode, as per the warnings introduced in 1.10 - Package names now must comply to our [naming guidelines](doc/04-schema.md#name) or Composer will abort, as per the warnings introduced in 1.8.1 - Deprecated --no-suggest flag as it is not needed anymore -- `update` now lists changes to the lock file first, and then the changes applied when installing the lock file to the vendor dir +- PEAR support (repository, downloader, etc.) has been removed +- `update` now lists changes to the lock file first (update step), and then the changes applied when installing the lock file to the vendor dir (install step) - `HTTPS_PROXY_REQUEST_FULLURI` if not specified will now default to false as this seems to work better in most environments ## For integrators and plugin authors From d494df61ff2bbb4bc516d2ef7224bb48f42f4a2f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Apr 2020 13:25:13 +0200 Subject: [PATCH 067/193] Make sure platform-check returns a non-0 exit code if it fails --- src/Composer/Autoload/AutoloadGenerator.php | 3 ++- .../Test/Autoload/Fixtures/platform/no_extensions_required.php | 3 ++- .../Test/Autoload/Fixtures/platform/no_php_lower_bound.php | 3 ++- .../Test/Autoload/Fixtures/platform/no_php_required.php | 3 ++- .../Test/Autoload/Fixtures/platform/no_php_upper_bound.php | 3 ++- .../Test/Autoload/Fixtures/platform/specific_php_release.php | 3 ++- tests/Composer/Test/Autoload/Fixtures/platform/typical.php | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index c3cffa839..c0b71c566 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -660,7 +660,8 @@ if (!(PHP_VERSION_ID $lowestOperator $lowestPhpVersionId && PHP_VERSION_ID $high } $requiredExtensions if (\$issues) { - die('Composer detected issues in your platform:' . "\\n\\n" . implode("\\n", \$issues)); + echo 'Composer detected issues in your platform:' . "\\n\\n" . implode("\\n", \$issues); + exit(104); } PLATFORM_CHECK; diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php index ccb09bcde..1dbfcd455 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php @@ -9,5 +9,6 @@ if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 80000)) { } if ($issues) { - die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php index 5d1c368ad..1339d2b76 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php @@ -9,5 +9,6 @@ if (!(PHP_VERSION_ID >= 0 && PHP_VERSION_ID < 80000)) { } if ($issues) { - die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php index ed2ecf25e..0cd09927a 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php @@ -17,5 +17,6 @@ if ($missingExtensions) { } if ($issues) { - die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php index f11188b31..838cbbbba 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php @@ -9,5 +9,6 @@ if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 99999)) { } if ($issues) { - die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php b/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php index 3ba4d2cc4..d1300a62d 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/specific_php_release.php @@ -9,5 +9,6 @@ if (!(PHP_VERSION_ID >= 70208 && PHP_VERSION_ID < 80000)) { } if ($issues) { - die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php index 45d85c8c2..728e68b63 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php @@ -17,5 +17,6 @@ if ($missingExtensions) { } if ($issues) { - die('Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues)); + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); } From 6a6ea6057f0cb8e7ed4b8e4ce6f45a4831e61083 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Apr 2020 13:38:50 +0200 Subject: [PATCH 068/193] Handle provider/replacer packages and avoid checking extensions which are provided by packages, refs #8546 --- src/Composer/Autoload/AutoloadGenerator.php | 20 +++++++++++++ .../Test/Autoload/AutoloadGeneratorTest.php | 30 +++++++++++++++++-- .../platform/replaced_provided_exts.php | 21 +++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index c0b71c566..bc334c927 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -20,6 +20,7 @@ use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; use Composer\Semver\Constraint\Bound; +use Composer\Semver\Constraint\EmptyConstraint; use Composer\Util\Filesystem; use Composer\Script\ScriptEvents; use Composer\Util\PackageSorter; @@ -573,6 +574,16 @@ EOF; $lowestPhpVersion = Bound::zero(); $highestPhpVersion = Bound::positiveInfinity(); $requiredExtensions = array(); + $extensionProviders = array(); + + foreach ($packageMap as $item) { + list($package, $installPath) = $item; + foreach (array_merge($package->getReplaces(), $package->getProvides()) as $link) { + if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { + $extensionProviders[$match[1]][] = $link->getConstraint() ?: new EmptyConstraint(); + } + } + } foreach ($packageMap as $item) { list($package, $installPath) = $item; @@ -587,6 +598,15 @@ EOF; } if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { + // skip extension checks if they have a valid provider/replacer + if (isset($extensionProviders[$match[1]])) { + foreach ($extensionProviders[$match[1]] as $provided) { + if (!$link->getConstraint() || $provided->matches($link->getConstraint())) { + continue 2; + } + } + } + $extension = var_export($match[1], true); if ($match[1] === 'pcntl') { $requiredExtensions[$extension] = "PHP_SAPI !== 'cli' || extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 4159cf003..298801b41 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1648,11 +1648,19 @@ EOF; /** * @dataProvider platformCheckProvider */ - public function testGeneratesPlatformCheck(array $requires, $expectedFixture) + public function testGeneratesPlatformCheck(array $requires, $expectedFixture, array $provides = array(), array $replaces = array()) { $package = new Package('a', '1.0', '1.0'); $package->setRequires($requires); + if ($provides) { + $package->setProvides($provides); + } + + if ($replaces) { + $package->setReplaces($replaces); + } + $this->repository->expects($this->once()) ->method('getCanonicalPackages') ->will($this->returnValue(array())); @@ -1705,7 +1713,25 @@ EOF; new Link('a', 'php', $versionParser->parseConstraints('^7.2')), ), 'no_extensions_required' - ) + ), + 'Replaced/provided extensions are not checked for + checking case insensitivity' => array( + array( + new Link('a', 'ext-xml', $versionParser->parseConstraints('^7.2')), + new Link('a', 'ext-Pdo', $versionParser->parseConstraints('^7.2')), + new Link('a', 'ext-bcMath', $versionParser->parseConstraints('^7.2')), + ), + 'replaced_provided_exts', + array( + // constraint does not satisfy all the ^7.2 requirement so we do not accept it as being replaced + new Link('a', 'ext-PDO', $versionParser->parseConstraints('7.1.*')), + // valid replace of bcmath so no need to check for it + new Link('a', 'ext-BCMath', $versionParser->parseConstraints('^7.1')), + ), + array( + // valid provide of ext-xml so no need to check for it + new Link('a', 'ext-XML', $versionParser->parseConstraints('*')), + ), + ), ); } diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php b/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php new file mode 100644 index 000000000..d57294f0b --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php @@ -0,0 +1,21 @@ += 0 && PHP_VERSION_ID < 99999)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 99999". You are running ' . PHP_VERSION . '.'; +} + +$missingExtensions = array(); +extension_loaded('pdo') || $missingExtensions[] = 'pdo'; + +if ($missingExtensions) { + $issues[] = 'Your Composer dependencies require the following PHP extensions to be installed: ' . implode(', ', $missingExtensions); +} + +if ($issues) { + echo 'Composer detected issues in your platform:' . "\n\n" . implode("\n", $issues); + exit(104); +} From d173af2d7ac1408655df2cf6670ea0262e06d137 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 24 Apr 2020 13:46:18 +0200 Subject: [PATCH 069/193] Fix check-platform-reqs command to take into account provide/replace from the root package --- src/Composer/Command/CheckPlatformReqsCommand.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/CheckPlatformReqsCommand.php b/src/Composer/Command/CheckPlatformReqsCommand.php index 68b55932d..c80884444 100644 --- a/src/Composer/Command/CheckPlatformReqsCommand.php +++ b/src/Composer/Command/CheckPlatformReqsCommand.php @@ -20,6 +20,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Composer\Repository\PlatformRepository; +use Composer\Repository\RootPackageRepository; use Composer\Repository\InstalledRepository; class CheckPlatformReqsCommand extends BaseCommand @@ -47,7 +48,7 @@ EOT { $composer = $this->getComposer(); - $requires = $composer->getPackage()->getRequires(); + $requires = array(); if ($input->getOption('no-dev')) { $installedRepo = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev')); $dependencies = $installedRepo->getPackages(); @@ -63,7 +64,7 @@ EOT $requires[$require] = array($link); } - $installedRepo = new InstalledRepository(array($installedRepo)); + $installedRepo = new InstalledRepository(array($installedRepo, new RootPackageRepository($composer->getPackage()))); foreach ($installedRepo->getPackages() as $package) { foreach ($package->getRequires() as $require => $link) { $requires[$require][] = $link; From 994a5b41c2a779929a436e443413e50a83613a0d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 26 Apr 2020 09:15:15 +0200 Subject: [PATCH 070/193] Provide default impl for abstract method in interface for PHP < 5.3.9 (#8837) --- .../DependencyResolver/Operation/SolverOperation.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Composer/DependencyResolver/Operation/SolverOperation.php b/src/Composer/DependencyResolver/Operation/SolverOperation.php index bbc077c31..41e829539 100644 --- a/src/Composer/DependencyResolver/Operation/SolverOperation.php +++ b/src/Composer/DependencyResolver/Operation/SolverOperation.php @@ -45,7 +45,10 @@ abstract class SolverOperation implements OperationInterface /** * @param $lock bool Whether this is an operation on the lock file - * @return string - */ - abstract public function show($lock); + * @return string + */ + public function show($lock) + { + throw new \RuntimeException('abstract method needs to be implemented in subclass, not marked abstract for compatibility with PHP <= 5.3.8'); + } } From 077930807637f7bd3abcaa589bd311cdab510196 Mon Sep 17 00:00:00 2001 From: Kamil Bacia Date: Sat, 25 Apr 2020 19:29:32 +0200 Subject: [PATCH 071/193] close #8784, Added --locked option to composer show command. Displaying locked packages with --all option --- src/Composer/Command/ShowCommand.php | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 6cf1905f5..b3fac0f47 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -66,6 +66,7 @@ class ShowCommand extends BaseCommand new InputArgument('package', InputArgument::OPTIONAL, 'Package to inspect. Or a name including a wildcard (*) to filter lists of packages instead.'), new InputArgument('version', InputArgument::OPTIONAL, 'Version or version constraint to inspect'), new InputOption('all', null, InputOption::VALUE_NONE, 'List all packages'), + new InputOption('locked', null, InputOption::VALUE_NONE, 'List all locked packages'), new InputOption('installed', 'i', InputOption::VALUE_NONE, 'List installed packages only (enabled by default, only present for BC).'), new InputOption('platform', 'p', InputOption::VALUE_NONE, 'List platform packages only'), new InputOption('available', 'a', InputOption::VALUE_NONE, 'List available packages only'), @@ -149,6 +150,7 @@ EOT $platformOverrides = $composer->getConfig()->get('platform') ?: array(); } $platformRepo = new PlatformRepository(array(), $platformOverrides); + $lockedRepo = null; $phpVersion = $platformRepo->findPackage('php', '*')->getVersion(); if ($input->getOption('self')) { @@ -168,13 +170,27 @@ EOT } } elseif ($input->getOption('all') && $composer) { $localRepo = $composer->getRepositoryManager()->getLocalRepository(); - $installedRepo = new InstalledRepository(array($localRepo, $platformRepo)); + $locker = $composer->getLocker(); + if ($locker->isLocked()) { + $lockedRepo = $locker->getLockedRepository(); + $installedRepo = new InstalledRepository(array($lockedRepo, $localRepo, $platformRepo)); + } else { + $installedRepo = new InstalledRepository(array($localRepo, $platformRepo)); + } $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } elseif ($input->getOption('all')) { $defaultRepos = RepositoryFactory::defaultRepos($io); $io->writeError('No composer.json found in the current directory, showing available packages from ' . implode(', ', array_keys($defaultRepos))); $installedRepo = new InstalledRepository(array($platformRepo)); $repos = new CompositeRepository(array_merge(array($installedRepo), $defaultRepos)); + } elseif ($input->getOption('locked')) { + if (!$composer || !$composer->getLocker()->isLocked()) { + throw new \UnexpectedValueException('A valid composer.json and composer.lock files is required to run this command with --locked'); + } + $locker = $composer->getLocker(); + $lockedRepo = $locker->getLockedRepository(); + $installedRepo = new InstalledRepository(array($lockedRepo)); + $repos = new CompositeRepository(array_merge(array($installedRepo), $composer->getRepositoryManager()->getRepositories())); } else { $repos = $installedRepo = new InstalledRepository(array($this->getComposer()->getRepositoryManager()->getLocalRepository())); $rootPkg = $this->getComposer()->getPackage(); @@ -315,6 +331,8 @@ EOT foreach ($repos as $repo) { if ($repo === $platformRepo) { $type = 'platform'; + } elseif($lockedRepo !== null && $repo === $lockedRepo) { + $type = 'locked'; } elseif ($repo === $installedRepo || in_array($repo, $installedRepo->getRepositories(), true)) { $type = 'installed'; } else { @@ -351,7 +369,7 @@ EOT $exitCode = 0; $viewData = array(); $viewMetaData = array(); - foreach (array('platform' => true, 'available' => false, 'installed' => true) as $type => $showVersion) { + foreach (array('platform' => true, 'locked' => true, 'available' => false, 'installed' => true) as $type => $showVersion) { if (isset($packages[$type])) { ksort($packages[$type]); From 6e7d7097e892ecd5c8eecadc2184f0b270e5b0df Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 28 Apr 2020 08:53:23 +0200 Subject: [PATCH 072/193] Add flag to docs --- doc/03-cli.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 3a964c390..43ce287cc 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -357,6 +357,7 @@ php composer.phar show monolog/monolog 1.0.2 * **--all :** List all packages available in all your repositories. * **--installed (-i):** List the packages that are installed (this is enabled by default, and deprecated). +* **--locked:** List the locked packages from composer.lock. * **--platform (-p):** List only platform packages (php & extensions). * **--available (-a):** List available packages only. * **--self (-s):** List the root package info. From b9be78b68919d11329aaa7d8792a858d96af2f91 Mon Sep 17 00:00:00 2001 From: Michael Chekin Date: Tue, 28 Apr 2020 11:45:30 +0200 Subject: [PATCH 073/193] Additional Util\Bitbucket class test coverage (#8835) --- tests/Composer/Test/Util/BitbucketTest.php | 182 +++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/tests/Composer/Test/Util/BitbucketTest.php b/tests/Composer/Test/Util/BitbucketTest.php index 5837d1996..04841f65f 100644 --- a/tests/Composer/Test/Util/BitbucketTest.php +++ b/tests/Composer/Test/Util/BitbucketTest.php @@ -124,6 +124,8 @@ class BitbucketTest extends TestCase $this->token, $this->bitbucket->requestToken($this->origin, $this->consumer_key, $this->consumer_secret) ); + + return $this->bitbucket; } public function testRequestAccessTokenWithValidOAuthConsumerAndExpiredAccessToken() @@ -223,6 +225,74 @@ class BitbucketTest extends TestCase $this->assertEquals('', $this->bitbucket->requestToken($this->origin, $this->username, $this->password)); } + public function testRequestAccessTokenWithUsernameAndPasswordWithUnauthorizedResponse() + { + $this->config->expects($this->once()) + ->method('get') + ->with('bitbucket-oauth') + ->willReturn(null); + + $this->io->expects($this->once()) + ->method('setAuthentication') + ->with($this->origin, $this->username, $this->password); + + $this->io->expects($this->any()) + ->method('writeError') + ->withConsecutive( + array('Invalid OAuth consumer provided.'), + array( + 'You can also add it manually later by using "composer config --global --auth bitbucket-oauth.bitbucket.org "') + ); + + $this->httpDownloader->expects($this->once()) + ->method('get') + ->with( + Bitbucket::OAUTH2_ACCESS_TOKEN_URL, + array( + 'retry-auth-failure' => false, + 'http' => array( + 'method' => 'POST', + 'content' => 'grant_type=client_credentials', + ), + ) + ) + ->willThrowException(new \Composer\Downloader\TransportException('HTTP/1.1 401 UNAUTHORIZED',401)); + + $this->assertEquals('', $this->bitbucket->requestToken($this->origin, $this->username, $this->password)); + } + + /** + * @expectedException \Composer\Downloader\TransportException + */ + public function testRequestAccessTokenWithUsernameAndPasswordWithNotFoundResponse() + { + $this->config->expects($this->once()) + ->method('get') + ->with('bitbucket-oauth') + ->willReturn(null); + + $this->io->expects($this->once()) + ->method('setAuthentication') + ->with($this->origin, $this->username, $this->password); + + $exception = new \Composer\Downloader\TransportException('HTTP/1.1 404 NOT FOUND',404); + $this->httpDownloader->expects($this->once()) + ->method('get') + ->with( + Bitbucket::OAUTH2_ACCESS_TOKEN_URL, + array( + 'retry-auth-failure' => false, + 'http' => array( + 'method' => 'POST', + 'content' => 'grant_type=client_credentials', + ), + ) + ) + ->willThrowException($exception); + + $this->bitbucket->requestToken($this->origin, $this->username, $this->password); + } + public function testUsernamePasswordAuthenticationFlow() { $this->io @@ -264,6 +334,74 @@ class BitbucketTest extends TestCase $this->assertTrue($this->bitbucket->authorizeOAuthInteractively($this->origin, $this->message)); } + public function testAuthorizeOAuthInteractivelyWithEmptyUsername() + { + $authConfigSourceMock = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock(); + $this->config->expects($this->atLeastOnce()) + ->method('getAuthConfigSource') + ->willReturn($authConfigSourceMock); + + $this->io->expects($this->once()) + ->method('askAndHideAnswer') + ->with('Consumer Key (hidden): ') + ->willReturnOnConsecutiveCalls(null); + + $this->assertFalse($this->bitbucket->authorizeOAuthInteractively($this->origin, $this->message)); + } + + public function testAuthorizeOAuthInteractivelyWithEmptyPassword() + { + $authConfigSourceMock = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock(); + $this->config->expects($this->atLeastOnce()) + ->method('getAuthConfigSource') + ->willReturn($authConfigSourceMock); + + $this->io->expects($this->exactly(2)) + ->method('askAndHideAnswer') + ->withConsecutive( + array('Consumer Key (hidden): '), + array('Consumer Secret (hidden): ') + ) + ->willReturnOnConsecutiveCalls($this->consumer_key, null); + + $this->assertFalse($this->bitbucket->authorizeOAuthInteractively($this->origin, $this->message)); + } + + public function testAuthorizeOAuthInteractivelyWithRequestAccessTokenFailure() + { + $authConfigSourceMock = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock(); + $this->config->expects($this->atLeastOnce()) + ->method('getAuthConfigSource') + ->willReturn($authConfigSourceMock); + + $this->io->expects($this->exactly(2)) + ->method('askAndHideAnswer') + ->withConsecutive( + array('Consumer Key (hidden): '), + array('Consumer Secret (hidden): ') + ) + ->willReturnOnConsecutiveCalls($this->consumer_key, $this->consumer_secret); + + $this->httpDownloader + ->expects($this->once()) + ->method('get') + ->with( + $this->equalTo($url = sprintf('https://%s/site/oauth2/access_token', $this->origin)), + $this->anything() + ) + ->willThrowException( + new \Composer\Downloader\TransportException( + sprintf( + 'The \'%s\' URL could not be accessed: HTTP/1.1 400 BAD REQUEST', + Bitbucket::OAUTH2_ACCESS_TOKEN_URL + ), + 400 + ) + ); + + $this->assertFalse($this->bitbucket->authorizeOAuthInteractively($this->origin, $this->message)); + } + private function setExpectationsForStoringAccessToken($removeBasicAuth = false) { $configSourceMock = $this->getMockBuilder('Composer\Config\ConfigSourceInterface')->getMock(); @@ -298,4 +436,48 @@ class BitbucketTest extends TestCase ->with('http-basic.' . $this->origin); } } + + public function testGetTokenWithoutAccessToken() + { + $this->assertSame('', $this->bitbucket->getToken()); + } + + /** + * @depends testRequestAccessTokenWithValidOAuthConsumerAndValidStoredAccessToken + * + * @param Bitbucket $bitbucket + */ + public function testGetTokenWithAccessToken(Bitbucket $bitbucket) + { + $this->assertSame($this->token, $bitbucket->getToken()); + } + + public function testAuthorizeOAuthWithWrongOriginUrl() + { + $this->assertFalse($this->bitbucket->authorizeOAuth('non-' . $this->origin)); + } + + public function testAuthorizeOAuthWithoutAvailableGitConfigToken() + { + $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock(); + $process->expects($this->once()) + ->method('execute') + ->willReturn(-1); + + $bitbucket = new Bitbucket($this->io, $this->config, $process, $this->httpDownloader, $this->time); + + $this->assertFalse($bitbucket->authorizeOAuth($this->origin)); + } + + public function testAuthorizeOAuthWithAvailableGitConfigToken() + { + $process = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock(); + $process->expects($this->once()) + ->method('execute') + ->willReturn(0); + + $bitbucket = new Bitbucket($this->io, $this->config, $process, $this->httpDownloader, $this->time); + + $this->assertTrue($bitbucket->authorizeOAuth($this->origin)); + } } From 56edd53046fd697d32b2fd2fbaf45af5d7951671 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 28 Apr 2020 11:56:26 +0200 Subject: [PATCH 074/193] Remove unnecessary implementation of interface method in abstract class --- .../DependencyResolver/Operation/SolverOperation.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Composer/DependencyResolver/Operation/SolverOperation.php b/src/Composer/DependencyResolver/Operation/SolverOperation.php index 41e829539..5839cbd64 100644 --- a/src/Composer/DependencyResolver/Operation/SolverOperation.php +++ b/src/Composer/DependencyResolver/Operation/SolverOperation.php @@ -42,13 +42,4 @@ abstract class SolverOperation implements OperationInterface { return $this->reason; } - - /** - * @param $lock bool Whether this is an operation on the lock file - * @return string - */ - public function show($lock) - { - throw new \RuntimeException('abstract method needs to be implemented in subclass, not marked abstract for compatibility with PHP <= 5.3.8'); - } } From 270c7c3262b928835b78c2c809cb51aaabfe4fed Mon Sep 17 00:00:00 2001 From: Alessandro Lai Date: Tue, 28 Apr 2020 16:04:00 +0200 Subject: [PATCH 075/193] Backport validation support for composer-runtime-api (#8842) Fixes #8841 --- src/Composer/Repository/PlatformRepository.php | 2 +- .../Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 265cdbf4a..1426d4a16 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -27,7 +27,7 @@ use Symfony\Component\Process\ExecutableFinder; */ class PlatformRepository extends ArrayRepository { - const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-plugin-api)$}iD'; + const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD'; private $versionParser; diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 9dabb23b7..e1ce9264a 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -86,6 +86,7 @@ class ValidatingArrayLoaderTest extends TestCase 'a/b' => '1.*', 'b/c' => '~2', 'example' => '>2.0-dev,<2.4-dev', + 'composer-runtime-api' => '*', ), 'require-dev' => array( 'a/b' => '1.*', From 6140897d088408ed56b3ae661c8d525d0346a685 Mon Sep 17 00:00:00 2001 From: Basil Peace Date: Wed, 29 Apr 2020 00:38:08 +0300 Subject: [PATCH 076/193] feat: add archive.name config option --- doc/04-schema.md | 15 +++++++ res/composer-schema.json | 4 ++ src/Composer/Package/AliasPackage.php | 5 +++ .../Package/Archiver/ArchiveManager.php | 39 ++++++++++++------- src/Composer/Package/Dumper/ArrayDumper.php | 3 ++ src/Composer/Package/Loader/ArrayLoader.php | 3 ++ src/Composer/Package/Package.php | 19 +++++++++ src/Composer/Package/PackageInterface.php | 7 ++++ 8 files changed, 80 insertions(+), 15 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index ecff3d996..57debee9c 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -883,6 +883,21 @@ A set of options for creating package archives. The following options are supported: +* **name:** Allows configuring base name for archive. + By default (if not configured, and `--file` is not passed as command-line argument), + `preg_replace('#[^a-z0-9-_]#i', '-', name)` is used. + +Example: + +```json +{ + "name": "org/strangeName", + "archive": { + "name": "Strange_name" + } +} +``` + * **exclude:** Allows configuring a list of patterns for excluded paths. The pattern syntax matches .gitignore files. A leading exclamation mark (!) will result in any matching files to be included even if a previous pattern diff --git a/res/composer-schema.json b/res/composer-schema.json index bcb712882..565454ac8 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -354,6 +354,10 @@ "type": ["object"], "description": "Options for creating package archives for distribution.", "properties": { + "name": { + "type": "string", + "description": "A base name for archive." + }, "exclude": { "type": "array", "description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark." diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index ee93ec497..34fbd8713 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -387,6 +387,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface return $this->aliasOf->getNotificationUrl(); } + public function getArchiveName() + { + return $this->aliasOf->getArchiveName(); + } + public function getArchiveExcludes() { return $this->aliasOf->getArchiveExcludes(); diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 6f8fa8a01..a523800c7 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -72,7 +72,12 @@ class ArchiveManager */ public function getPackageFilename(PackageInterface $package) { - $nameParts = array(preg_replace('#[^a-z0-9-_]#i', '-', $package->getName())); + if ($package->getArchiveName()) { + $baseName = $package->getArchiveName(); + } else { + $baseName = preg_replace('#[^a-z0-9-_]#i', '-', $package->getName()); + } + $nameParts = array($baseName); if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) { array_push($nameParts, $package->getDistReference(), $package->getDistType()); @@ -125,20 +130,6 @@ class ArchiveManager } $filesystem = new Filesystem(); - if (null === $fileName) { - $packageName = $this->getPackageFilename($package); - } else { - $packageName = $fileName; - } - - // Archive filename - $filesystem->ensureDirectoryExists($targetDir); - $target = realpath($targetDir).'/'.$packageName.'.'.$format; - $filesystem->ensureDirectoryExists(dirname($target)); - - if (!$this->overwriteFiles && file_exists($target)) { - return $target; - } if ($package instanceof RootPackageInterface) { $sourcePath = realpath('.'); @@ -159,12 +150,30 @@ class ArchiveManager if (file_exists($composerJsonPath = $sourcePath.'/composer.json')) { $jsonFile = new JsonFile($composerJsonPath); $jsonData = $jsonFile->read(); + if (!empty($jsonData['archive']['name'])) { + $package->setArchiveName($jsonData['archive']['name']); + } if (!empty($jsonData['archive']['exclude'])) { $package->setArchiveExcludes($jsonData['archive']['exclude']); } } } + if (null === $fileName) { + $packageName = $this->getPackageFilename($package); + } else { + $packageName = $fileName; + } + + // Archive filename + $filesystem->ensureDirectoryExists($targetDir); + $target = realpath($targetDir).'/'.$packageName.'.'.$format; + $filesystem->ensureDirectoryExists(dirname($target)); + + if (!$this->overwriteFiles && file_exists($target)) { + return $target; + } + // Create the archive $tempTarget = sys_get_temp_dir().'/composer_archive'.uniqid().'.'.$format; $filesystem->ensureDirectoryExists(dirname($tempTarget)); diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index dece598f1..8999666eb 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -70,6 +70,9 @@ class ArrayDumper } } + if ($package->getArchiveName()) { + $data['archive']['name'] = $package->getArchiveName(); + } if ($package->getArchiveExcludes()) { $data['archive']['exclude'] = $package->getArchiveExcludes(); } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 228632b42..cb9058941 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -159,6 +159,9 @@ class ArrayLoader implements LoaderInterface $package->setNotificationUrl($config['notification-url']); } + if (!empty($config['archive']['name'])) { + $package->setArchiveName($config['archive']['name']); + } if (!empty($config['archive']['exclude'])) { $package->setArchiveExcludes($config['archive']['exclude']); } diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 6c7b426e7..e5b39b896 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -57,6 +57,7 @@ class Package extends BasePackage protected $autoload = array(); protected $devAutoload = array(); protected $includePaths = array(); + protected $archiveName; protected $archiveExcludes = array(); /** @@ -551,6 +552,24 @@ class Package extends BasePackage return $this->notificationUrl; } + /** + * Sets default base filename for archive + * + * @param string $name + */ + public function setArchiveName($name) + { + $this->archiveName = $name; + } + + /** + * {@inheritDoc} + */ + public function getArchiveName() + { + return $this->archiveName; + } + /** * Sets a list of patterns to be excluded from archives * diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index cb16efa7e..adbc3b189 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -345,6 +345,13 @@ interface PackageInterface */ public function getPrettyString(); + /** + * Returns default base filename for archive + * + * @return array + */ + public function getArchiveName(); + /** * Returns a list of patterns to exclude from package archives * From ff05150c4e1514c452be03b86974fbc891942156 Mon Sep 17 00:00:00 2001 From: Alessandro Lai Date: Wed, 29 Apr 2020 08:55:58 +0200 Subject: [PATCH 077/193] Add composer-runtime-api version constant --- src/Composer/Composer.php | 11 +++++++++++ src/Composer/Repository/PlatformRepository.php | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 99f4756b0..a879a0dae 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -55,6 +55,17 @@ class Composer const RELEASE_DATE = '@release_date@'; const SOURCE_VERSION = '1.10-dev+source'; + /** + * Version number of the internal composer-runtime-api package + * + * This is used to version features available to projects at runtime + * like the platform-check file, the Composer\InstalledVersions class + * and possibly others in the future. + * + * @var string + */ + const RUNTIME_API_VERSION = '1.0.0'; + public static function getVersion() { // no replacement done, this must be a source checkout diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 1426d4a16..ecd4ca256 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -12,6 +12,7 @@ namespace Composer\Repository; +use Composer\Composer; use Composer\Package\CompletePackage; use Composer\Package\PackageInterface; use Composer\Package\Version\VersionParser; @@ -74,6 +75,12 @@ class PlatformRepository extends ArrayRepository $composerPluginApi->setDescription('The Composer Plugin API'); $this->addPackage($composerPluginApi); + $prettyVersion = Composer::RUNTIME_API_VERSION; + $version = $this->versionParser->normalize($prettyVersion); + $composerRuntimeApi = new CompletePackage('composer-runtime-api', $version, $prettyVersion); + $composerRuntimeApi->setDescription('The Composer Runtime API'); + $this->addPackage($composerRuntimeApi); + try { $prettyVersion = PHP_VERSION; $version = $this->versionParser->normalize($prettyVersion); From 3c1757575e8c2345100be76003c184750822799e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Thu, 30 Apr 2020 09:44:22 +0200 Subject: [PATCH 078/193] Annotate phpstan errors in pull requests Utilizing https://github.com/staabm/annotate-pull-request-from-checkstyle --- .github/workflows/phpstan.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index e2cb477f1..967f79dcd 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -34,6 +34,7 @@ jobs: extensions: "intl" ini-values: "memory_limit=-1" php-version: "${{ matrix.php-version }}" + tools: "cs2pr" - name: "Determine composer cache directory" id: "determine-composer-cache-directory" @@ -52,4 +53,4 @@ jobs: - name: Run PHPStan run: | bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --with-all-dependencies - vendor/bin/phpstan analyse --configuration=phpstan/config.neon + vendor/bin/phpstan analyse --configuration=phpstan/config.neon --error-format=checkstyle | cs2pr From a5f86a494efc17bcdaadd10f0993cfc0b81c6d5c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 30 Apr 2020 15:19:18 +0200 Subject: [PATCH 079/193] Add a conflict on latest phpunit-bridge --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8331bc8a1..a4606d71a 100644 --- a/composer.json +++ b/composer.json @@ -38,7 +38,8 @@ "react/promise": "^1.2 || ^2.7" }, "conflict": { - "symfony/console": "2.8.38" + "symfony/console": "2.8.38", + "symfony/phpunit-bridge": "3.4.40" }, "require-dev": { "symfony/phpunit-bridge": "^3.4", From 11cd3e0a284347d1f90dc46981ce625bfcb09ee8 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 30 Apr 2020 15:21:55 +0200 Subject: [PATCH 080/193] Update lock file --- composer.lock | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 7096a1a36..914dd0798 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0a043906e568eec0b2608c6684018480", + "content-hash": "ded9d158cb184fe1c92fa05610786bb5", "packages": [ { "name": "composer/ca-bundle", @@ -142,6 +142,16 @@ "issues": "https://github.com/composer/semver/issues", "source": "https://github.com/composer/semver/tree/2.0.0" }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], "time": "2020-04-21T13:19:12+00:00" }, { @@ -419,7 +429,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com" + } + ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/1.0" + }, "time": "2016-03-07T13:46:50+00:00" }, { @@ -1453,6 +1473,20 @@ "support": { "source": "https://github.com/symfony/phpunit-bridge/tree/v3.4.38" }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-02-21T08:01:47+00:00" } ], From 1cb6bbe5504a8618c5200e671c5d544217a53f43 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 30 Apr 2020 19:50:53 +0200 Subject: [PATCH 081/193] Clarify composer update mirrors/nothing/lock Addresses https://github.com/composer/composer/issues/7459 by listing these special arguments in the documentation. --- doc/03-cli.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/03-cli.md b/doc/03-cli.md index 29eeb398c..0c41e9ef7 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -174,6 +174,8 @@ php composer.phar update "vendor/*" * **--interactive:** Interactive interface with autocompletion to select the packages to update. * **--root-reqs:** Restricts the update to your first degree dependencies. +Specifying one of the words `mirrors`, `lock`, or `nothing` as an argument has the same effect as specifying the option `--lock`, for example `composer update mirrors` is exactly the same as `composer update --lock`. + ## require The `require` command adds new packages to the `composer.json` file from From 81bf47ffa2ce317ec75aa165de51cde158feb8e9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 30 Apr 2020 10:36:17 +0200 Subject: [PATCH 082/193] Use fully qualified calls in hot classes --- src/Composer/DependencyResolver/Decisions.php | 12 +++---- .../DependencyResolver/DefaultPolicy.php | 2 +- .../DependencyResolver/GenericRule.php | 2 +- .../DependencyResolver/MultiConflictRule.php | 2 +- src/Composer/DependencyResolver/Pool.php | 4 +-- .../DependencyResolver/PoolBuilder.php | 6 ++-- src/Composer/DependencyResolver/Rule.php | 2 +- .../DependencyResolver/Rule2Literals.php | 2 +- src/Composer/DependencyResolver/RuleSet.php | 8 ++--- .../DependencyResolver/RuleSetGenerator.php | 4 +-- .../DependencyResolver/RuleSetIterator.php | 6 ++-- .../DependencyResolver/RuleWatchNode.php | 4 +-- src/Composer/DependencyResolver/Solver.php | 32 +++++++++---------- src/Composer/Package/AliasPackage.php | 2 +- src/Composer/Package/BasePackage.php | 4 +-- src/Composer/Package/CompletePackage.php | 2 +- src/Composer/Package/Dumper/ArrayDumper.php | 6 ++-- src/Composer/Package/Loader/ArrayLoader.php | 26 +++++++-------- src/Composer/Package/Package.php | 2 +- src/Composer/Util/Filesystem.php | 30 ++++++++--------- src/Composer/Util/Platform.php | 8 ++--- 21 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/Composer/DependencyResolver/Decisions.php b/src/Composer/DependencyResolver/Decisions.php index e2773501f..bbf774ba9 100644 --- a/src/Composer/DependencyResolver/Decisions.php +++ b/src/Composer/DependencyResolver/Decisions.php @@ -108,17 +108,17 @@ class Decisions implements \Iterator, \Countable public function validOffset($queueOffset) { - return $queueOffset >= 0 && $queueOffset < count($this->decisionQueue); + return $queueOffset >= 0 && $queueOffset < \count($this->decisionQueue); } public function lastReason() { - return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_REASON]; + return $this->decisionQueue[\count($this->decisionQueue) - 1][self::DECISION_REASON]; } public function lastLiteral() { - return $this->decisionQueue[count($this->decisionQueue) - 1][self::DECISION_LITERAL]; + return $this->decisionQueue[\count($this->decisionQueue) - 1][self::DECISION_LITERAL]; } public function reset() @@ -130,7 +130,7 @@ class Decisions implements \Iterator, \Countable public function resetToOffset($offset) { - while (count($this->decisionQueue) > $offset + 1) { + while (\count($this->decisionQueue) > $offset + 1) { $decision = array_pop($this->decisionQueue); $this->decisionMap[abs($decision[self::DECISION_LITERAL])] = 0; } @@ -144,7 +144,7 @@ class Decisions implements \Iterator, \Countable public function count() { - return count($this->decisionQueue); + return \count($this->decisionQueue); } public function rewind() @@ -174,7 +174,7 @@ class Decisions implements \Iterator, \Countable public function isEmpty() { - return count($this->decisionQueue) === 0; + return \count($this->decisionQueue) === 0; } protected function addDecision($literal, $level) diff --git a/src/Composer/DependencyResolver/DefaultPolicy.php b/src/Composer/DependencyResolver/DefaultPolicy.php index 3fdb4438b..235987aa3 100644 --- a/src/Composer/DependencyResolver/DefaultPolicy.php +++ b/src/Composer/DependencyResolver/DefaultPolicy.php @@ -60,7 +60,7 @@ class DefaultPolicy implements PolicyInterface $sortedLiterals = $this->pruneRemoteAliases($pool, $sortedLiterals); } - $selected = call_user_func_array('array_merge', $packages); + $selected = \call_user_func_array('array_merge', $packages); // now sort the result across all packages to respect replaces across packages usort($selected, function ($a, $b) use ($policy, $pool, $requiredPackage) { diff --git a/src/Composer/DependencyResolver/GenericRule.php b/src/Composer/DependencyResolver/GenericRule.php index a07883872..2d2cec7b1 100644 --- a/src/Composer/DependencyResolver/GenericRule.php +++ b/src/Composer/DependencyResolver/GenericRule.php @@ -64,7 +64,7 @@ class GenericRule extends Rule public function isAssertion() { - return 1 === count($this->literals); + return 1 === \count($this->literals); } /** diff --git a/src/Composer/DependencyResolver/MultiConflictRule.php b/src/Composer/DependencyResolver/MultiConflictRule.php index 8de77a41b..8200eef0a 100644 --- a/src/Composer/DependencyResolver/MultiConflictRule.php +++ b/src/Composer/DependencyResolver/MultiConflictRule.php @@ -33,7 +33,7 @@ class MultiConflictRule extends Rule { parent::__construct($reason, $reasonData); - if (count($literals) < 3) { + if (\count($literals) < 3) { throw new \RuntimeException("multi conflict rule requires at least 3 literals"); } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index abf3d70bf..b848ae37e 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -71,7 +71,7 @@ class Pool implements \Countable */ public function count() { - return count($this->packages); + return \count($this->packages); } /** @@ -189,6 +189,6 @@ class Pool implements \Countable public function isUnacceptableFixedPackage(PackageInterface $package) { - return in_array($package, $this->unacceptableFixedPackages, true); + return \in_array($package, $this->unacceptableFixedPackages, true); } } diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index f03a6bf24..b8ad9b281 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -161,7 +161,7 @@ class PoolBuilder // filter packages according to all the require statements collected for each package $nameConstraints = array(); foreach ($this->nameConstraints as $name => $constraints) { - if (is_array($constraints)) { + if (\is_array($constraints)) { $nameConstraints[$name] = MultiConstraint::create(array_values(array_unique($constraints)), false); } } @@ -275,9 +275,9 @@ class PoolBuilder $linkConstraint = $link->getConstraint(); if ($linkConstraint && !($linkConstraint instanceof EmptyConstraint)) { - if (!array_key_exists($require, $this->nameConstraints)) { + if (!\array_key_exists($require, $this->nameConstraints)) { $this->nameConstraints[$require] = array($linkConstraint); - } elseif (is_array($this->nameConstraints[$require])) { + } elseif (\is_array($this->nameConstraints[$require])) { $this->nameConstraints[$require][] = $linkConstraint; } // else it is null and should stay null diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 07550d8d0..4dc483d5f 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -264,7 +264,7 @@ abstract class Rule { $prepared = array(); foreach ($packages as $index => $package) { - if (!is_object($package)) { + if (!\is_object($package)) { $packages[$index] = $pool->literalToPackage($package); } } diff --git a/src/Composer/DependencyResolver/Rule2Literals.php b/src/Composer/DependencyResolver/Rule2Literals.php index 2df95e09d..b67843c96 100644 --- a/src/Composer/DependencyResolver/Rule2Literals.php +++ b/src/Composer/DependencyResolver/Rule2Literals.php @@ -76,7 +76,7 @@ class Rule2Literals extends Rule } $literals = $rule->getLiterals(); - if (2 != count($literals)) { + if (2 != \count($literals)) { return false; } diff --git a/src/Composer/DependencyResolver/RuleSet.php b/src/Composer/DependencyResolver/RuleSet.php index 8058f9f68..5de29bb51 100644 --- a/src/Composer/DependencyResolver/RuleSet.php +++ b/src/Composer/DependencyResolver/RuleSet.php @@ -65,7 +65,7 @@ class RuleSet implements \IteratorAggregate, \Countable // Do not add if rule already exists if (isset($this->rulesByHash[$hash])) { $potentialDuplicates = $this->rulesByHash[$hash]; - if (is_array($potentialDuplicates)) { + if (\is_array($potentialDuplicates)) { foreach ($potentialDuplicates as $potentialDuplicate) { if ($rule->equals($potentialDuplicate)) { return; @@ -90,7 +90,7 @@ class RuleSet implements \IteratorAggregate, \Countable if (!isset($this->rulesByHash[$hash])) { $this->rulesByHash[$hash] = $rule; - } elseif (is_array($this->rulesByHash[$hash])) { + } elseif (\is_array($this->rulesByHash[$hash])) { $this->rulesByHash[$hash][] = $rule; } else { $originalRule = $this->rulesByHash[$hash]; @@ -120,7 +120,7 @@ class RuleSet implements \IteratorAggregate, \Countable public function getIteratorFor($types) { - if (!is_array($types)) { + if (!\is_array($types)) { $types = array($types); } @@ -136,7 +136,7 @@ class RuleSet implements \IteratorAggregate, \Countable public function getIteratorWithout($types) { - if (!is_array($types)) { + if (!\is_array($types)) { $types = array($types); } diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index e6ac7ac31..02c3fd598 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -120,7 +120,7 @@ class RuleSetGenerator $literals[] = -$package->id; } - if (count($literals) == 2) { + if (\count($literals) == 2) { return new Rule2Literals($literals[0], $literals[1], $reason, $reasonData); } @@ -207,7 +207,7 @@ class RuleSetGenerator } foreach ($this->addedPackagesByNames as $name => $packages) { - if (count($packages) > 1) { + if (\count($packages) > 1) { $reason = Rule::RULE_PACKAGE_SAME_NAME; $this->addRule(RuleSet::TYPE_PACKAGE, $this->createMultiConflictRule($packages, $reason, $name)); } diff --git a/src/Composer/DependencyResolver/RuleSetIterator.php b/src/Composer/DependencyResolver/RuleSetIterator.php index 8c048624f..1c95218da 100644 --- a/src/Composer/DependencyResolver/RuleSetIterator.php +++ b/src/Composer/DependencyResolver/RuleSetIterator.php @@ -51,7 +51,7 @@ class RuleSetIterator implements \Iterator return; } - if ($this->currentOffset >= count($this->rules[$this->currentType])) { + if ($this->currentOffset >= \count($this->rules[$this->currentType])) { $this->currentOffset = 0; do { @@ -63,7 +63,7 @@ class RuleSetIterator implements \Iterator } $this->currentType = $this->types[$this->currentTypeOffset]; - } while (isset($this->types[$this->currentTypeOffset]) && !count($this->rules[$this->currentType])); + } while (isset($this->types[$this->currentTypeOffset]) && !\count($this->rules[$this->currentType])); } } @@ -83,7 +83,7 @@ class RuleSetIterator implements \Iterator } $this->currentType = $this->types[$this->currentTypeOffset]; - } while (isset($this->types[$this->currentTypeOffset]) && !count($this->rules[$this->currentType])); + } while (isset($this->types[$this->currentTypeOffset]) && !\count($this->rules[$this->currentType])); } public function valid() diff --git a/src/Composer/DependencyResolver/RuleWatchNode.php b/src/Composer/DependencyResolver/RuleWatchNode.php index 926c144b4..0986b08b1 100644 --- a/src/Composer/DependencyResolver/RuleWatchNode.php +++ b/src/Composer/DependencyResolver/RuleWatchNode.php @@ -37,7 +37,7 @@ class RuleWatchNode $literals = $rule->getLiterals(); - $literalCount = count($literals); + $literalCount = \count($literals); $this->watch1 = $literalCount > 0 ? $literals[0] : 0; $this->watch2 = $literalCount > 1 ? $literals[1] : 0; } @@ -55,7 +55,7 @@ class RuleWatchNode $literals = $this->rule->getLiterals(); // if there are only 2 elements, both are being watched anyway - if (count($literals) < 3 || $this->rule instanceof MultiConflictRule) { + if (\count($literals) < 3 || $this->rule instanceof MultiConflictRule) { return; } diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 73a17b112..d0f90dc59 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -75,7 +75,7 @@ class Solver */ public function getRuleSetSize() { - return count($this->rules); + return \count($this->rules); } public function getPool() @@ -87,9 +87,9 @@ class Solver private function makeAssertionRuleDecisions() { - $decisionStart = count($this->decisions) - 1; + $decisionStart = \count($this->decisions) - 1; - $rulesCount = count($this->rules); + $rulesCount = \count($this->rules); for ($ruleIndex = 0; $ruleIndex < $rulesCount; $ruleIndex++) { $rule = $this->rules->ruleById[$ruleIndex]; @@ -269,10 +269,10 @@ class Solver } $this->decisions->revertLast(); - $this->propagateIndex = count($this->decisions); + $this->propagateIndex = \count($this->decisions); } - while (!empty($this->branches) && $this->branches[count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) { + while (!empty($this->branches) && $this->branches[\count($this->branches) - 1][self::BRANCH_LEVEL] >= $level) { array_pop($this->branches); } } @@ -357,7 +357,7 @@ class Solver $selectedLiteral = array_shift($literals); // if there are multiple candidates, then branch - if (count($literals)) { + if (\count($literals)) { $this->branches[] = array($literals, $level); } @@ -378,12 +378,12 @@ class Solver $seen = array(); $learnedLiterals = array(null); - $decisionId = count($this->decisions); + $decisionId = \count($this->decisions); $this->learnedPool[] = array(); while (true) { - $this->learnedPool[count($this->learnedPool) - 1][] = $rule; + $this->learnedPool[\count($this->learnedPool) - 1][] = $rule; foreach ($rule->getLiterals() as $literal) { // skip the one true literal @@ -466,7 +466,7 @@ class Solver $rule = $decision[Decisions::DECISION_REASON]; } - $why = count($this->learnedPool) - 1; + $why = \count($this->learnedPool) - 1; if (!$learnedLiterals[0]) { throw new SolverBugException( @@ -647,7 +647,7 @@ class Solver } } - if ($noneSatisfied && count($decisionQueue)) { + if ($noneSatisfied && \count($decisionQueue)) { // if any of the options in the decision queue are fixed, only use those $prunedQueue = array(); foreach ($decisionQueue as $literal) { @@ -660,7 +660,7 @@ class Solver } } - if ($noneSatisfied && count($decisionQueue)) { + if ($noneSatisfied && \count($decisionQueue)) { $oLevel = $level; $level = $this->selectAndInstall($level, $decisionQueue, $rule); @@ -687,7 +687,7 @@ class Solver $systemLevel = $level; } - $rulesCount = count($this->rules); + $rulesCount = \count($this->rules); $pass = 1; $this->io->writeError('Looking at all rules.', true, IOInterface::DEBUG); @@ -734,7 +734,7 @@ class Solver } // need to have at least 2 item to pick from - if (count($decisionQueue) < 2) { + if (\count($decisionQueue) < 2) { continue; } @@ -745,7 +745,7 @@ class Solver } // something changed, so look at all rules again - $rulesCount = count($this->rules); + $rulesCount = \count($this->rules); $n = -1; } @@ -754,13 +754,13 @@ class Solver } // minimization step - if (count($this->branches)) { + if (\count($this->branches)) { $lastLiteral = null; $lastLevel = null; $lastBranchIndex = 0; $lastBranchOffset = 0; - for ($i = count($this->branches) - 1; $i >= 0; $i--) { + for ($i = \count($this->branches) - 1; $i >= 0; $i--) { list($literals, $l) = $this->branches[$i]; foreach ($literals as $offset => $literal) { diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 6f380d7d2..54e26979d 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -179,7 +179,7 @@ class AliasPackage extends BasePackage implements CompletePackageInterface $prettyVersion = $this->aliasOf->getPrettyVersion(); } - if (in_array($linkType, array('conflicts', 'provides', 'replaces'), true)) { + if (\in_array($linkType, array('conflicts', 'provides', 'replaces'), true)) { $newLinks = array(); foreach ($links as $link) { // link is self.version, but must be replacing also the replaced version diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php index 480b5ee62..baf3a2292 100644 --- a/src/Composer/Package/BasePackage.php +++ b/src/Composer/Package/BasePackage.php @@ -215,7 +215,7 @@ abstract class BasePackage implements PackageInterface public function getFullPrettyVersion($truncate = true, $displayMode = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV) { if ($displayMode === PackageInterface::DISPLAY_SOURCE_REF_IF_DEV && - (!$this->isDev() || !in_array($this->getSourceType(), array('hg', 'git'))) + (!$this->isDev() || !\in_array($this->getSourceType(), array('hg', 'git'))) ) { return $this->getPrettyVersion(); } @@ -233,7 +233,7 @@ abstract class BasePackage implements PackageInterface } // if source reference is a sha1 hash -- truncate - if ($truncate && strlen($reference) === 40) { + if ($truncate && \strlen($reference) === 40) { return $this->getPrettyVersion() . ' ' . substr($reference, 0, 7); } diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php index 785d5817c..85056405b 100644 --- a/src/Composer/Package/CompletePackage.php +++ b/src/Composer/Package/CompletePackage.php @@ -213,6 +213,6 @@ class CompletePackage extends Package implements CompletePackageInterface */ public function getReplacementPackage() { - return is_string($this->abandoned) ? $this->abandoned : null; + return \is_string($this->abandoned) ? $this->abandoned : null; } } diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index dece598f1..3ebd44f26 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -109,7 +109,7 @@ class ArrayDumper $data = $this->dumpValues($package, $keys, $data); - if (isset($data['keywords']) && is_array($data['keywords'])) { + if (isset($data['keywords']) && \is_array($data['keywords'])) { sort($data['keywords']); } @@ -125,7 +125,7 @@ class ArrayDumper } } - if (count($package->getTransportOptions()) > 0) { + if (\count($package->getTransportOptions()) > 0) { $data['transport-options'] = $package->getTransportOptions(); } @@ -142,7 +142,7 @@ class ArrayDumper $getter = 'get'.ucfirst($method); $value = $package->$getter(); - if (null !== $value && !(is_array($value) && 0 === count($value))) { + if (null !== $value && !(\is_array($value) && 0 === \count($value))) { $data[$key] = $value; } } diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 53270e601..adf8fb039 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -109,7 +109,7 @@ class ArrayLoader implements LoaderInterface $package->setTargetDir($config['target-dir']); } - if (isset($config['extra']) && is_array($config['extra'])) { + if (isset($config['extra']) && \is_array($config['extra'])) { $package->setExtra($config['extra']); } @@ -159,7 +159,7 @@ class ArrayLoader implements LoaderInterface } } - if (isset($config['suggest']) && is_array($config['suggest'])) { + if (isset($config['suggest']) && \is_array($config['suggest'])) { foreach ($config['suggest'] as $target => $reason) { if ('self.version' === trim($reason)) { $config['suggest'][$target] = $package->getPrettyVersion(); @@ -199,7 +199,7 @@ class ArrayLoader implements LoaderInterface } if ($package instanceof Package\CompletePackageInterface) { - if (isset($config['scripts']) && is_array($config['scripts'])) { + if (isset($config['scripts']) && \is_array($config['scripts'])) { foreach ($config['scripts'] as $event => $listeners) { $config['scripts'][$event] = (array) $listeners; } @@ -209,23 +209,23 @@ class ArrayLoader implements LoaderInterface $package->setScripts($config['scripts']); } - if (!empty($config['description']) && is_string($config['description'])) { + if (!empty($config['description']) && \is_string($config['description'])) { $package->setDescription($config['description']); } - if (!empty($config['homepage']) && is_string($config['homepage'])) { + if (!empty($config['homepage']) && \is_string($config['homepage'])) { $package->setHomepage($config['homepage']); } - if (!empty($config['keywords']) && is_array($config['keywords'])) { + if (!empty($config['keywords']) && \is_array($config['keywords'])) { $package->setKeywords($config['keywords']); } if (!empty($config['license'])) { - $package->setLicense(is_array($config['license']) ? $config['license'] : array($config['license'])); + $package->setLicense(\is_array($config['license']) ? $config['license'] : array($config['license'])); } - if (!empty($config['authors']) && is_array($config['authors'])) { + if (!empty($config['authors']) && \is_array($config['authors'])) { $package->setAuthors($config['authors']); } @@ -233,7 +233,7 @@ class ArrayLoader implements LoaderInterface $package->setSupport($config['support']); } - if (!empty($config['funding']) && is_array($config['funding'])) { + if (!empty($config['funding']) && \is_array($config['funding'])) { $package->setFunding($config['funding']); } @@ -307,8 +307,8 @@ class ArrayLoader implements LoaderInterface private function createLink($source, $sourceVersion, $description, $target, $prettyConstraint) { - if (!is_string($prettyConstraint)) { - throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.gettype($prettyConstraint) . ' (' . var_export($prettyConstraint, true) . ')'); + if (!\is_string($prettyConstraint)) { + throw new \UnexpectedValueException('Link constraint in '.$source.' '.$description.' > '.$target.' should be a string, got '.\gettype($prettyConstraint) . ' (' . var_export($prettyConstraint, true) . ')'); } if ('self.version' === $prettyConstraint) { $parsedConstraint = $this->versionParser->parseConstraints($sourceVersion); @@ -331,7 +331,7 @@ class ArrayLoader implements LoaderInterface return; } - if (isset($config['extra']['branch-alias']) && is_array($config['extra']['branch-alias'])) { + if (isset($config['extra']['branch-alias']) && \is_array($config['extra']['branch-alias'])) { foreach ($config['extra']['branch-alias'] as $sourceBranch => $targetBranch) { // ensure it is an alias to a -dev package if ('-dev' !== substr($targetBranch, -4)) { @@ -361,7 +361,7 @@ class ArrayLoader implements LoaderInterface } } - if (in_array($config['version'], array('dev-master', 'dev-default', 'dev-trunk'), true)) { + if (\in_array($config['version'], array('dev-master', 'dev-default', 'dev-trunk'), true)) { return VersionParser::DEV_MASTER_ALIAS; } } diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 6fdba9b42..3c2e74219 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -619,7 +619,7 @@ class Package extends BasePackage } else { continue; } - if (!in_array($mirrorUrl, $urls)) { + if (!\in_array($mirrorUrl, $urls)) { $func = $mirror['preferred'] ? 'array_unshift' : 'array_push'; $func($urls, $mirrorUrl); } diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 0e516ef29..fd7ded57e 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -58,7 +58,7 @@ class Filesystem ->depth(0) ->in($dir); - return count($finder) === 0; + return \count($finder) === 0; } public function emptyDirectory($dir, $ensureDirectoryExists = true) @@ -116,7 +116,7 @@ class Filesystem throw new \RuntimeException('Aborting an attempted deletion of '.$directory.', this was probably not intended, if it is a real use case please report it.'); } - if (!function_exists('proc_open')) { + if (!\function_exists('proc_open')) { return $this->removeDirectoryPhp($directory); } @@ -311,7 +311,7 @@ class Filesystem return; } - if (!function_exists('proc_open')) { + if (!\function_exists('proc_open')) { $this->copyThenRemove($source, $target); return; @@ -369,13 +369,13 @@ class Filesystem $from = rtrim($from, '/') . '/dummy_file'; } - if (dirname($from) === dirname($to)) { + if (\dirname($from) === \dirname($to)) { return './'.basename($to); } $commonPath = $to; while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath)) { - $commonPath = strtr(dirname($commonPath), '\\', '/'); + $commonPath = strtr(\dirname($commonPath), '\\', '/'); } if (0 !== strpos($from, $commonPath) || '/' === $commonPath) { @@ -383,10 +383,10 @@ class Filesystem } $commonPath = rtrim($commonPath, '/') . '/'; - $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/'); + $sourcePathDepth = substr_count(substr($from, \strlen($commonPath)), '/'); $commonPathCode = str_repeat('../', $sourcePathDepth); - return ($commonPathCode . substr($to, strlen($commonPath))) ?: './'; + return ($commonPathCode . substr($to, \strlen($commonPath))) ?: './'; } /** @@ -414,7 +414,7 @@ class Filesystem $commonPath = $to; while (strpos($from.'/', $commonPath.'/') !== 0 && '/' !== $commonPath && !preg_match('{^[a-z]:/?$}i', $commonPath) && '.' !== $commonPath) { - $commonPath = strtr(dirname($commonPath), '\\', '/'); + $commonPath = strtr(\dirname($commonPath), '\\', '/'); } if (0 !== strpos($from, $commonPath) || '/' === $commonPath || '.' === $commonPath) { @@ -423,17 +423,17 @@ class Filesystem $commonPath = rtrim($commonPath, '/') . '/'; if (strpos($to, $from.'/') === 0) { - return '__DIR__ . '.var_export(substr($to, strlen($from)), true); + return '__DIR__ . '.var_export(substr($to, \strlen($from)), true); } - $sourcePathDepth = substr_count(substr($from, strlen($commonPath)), '/') + $directories; + $sourcePathDepth = substr_count(substr($from, \strlen($commonPath)), '/') + $directories; if ($staticCode) { $commonPathCode = "__DIR__ . '".str_repeat('/..', $sourcePathDepth)."'"; } else { $commonPathCode = str_repeat('dirname(', $sourcePathDepth).'__DIR__'.str_repeat(')', $sourcePathDepth); } - $relTarget = substr($to, strlen($commonPath)); + $relTarget = substr($to, \strlen($commonPath)); - return $commonPathCode . (strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : ''); + return $commonPathCode . (\strlen($relTarget) ? '.' . var_export('/' . $relTarget, true) : ''); } /** @@ -484,7 +484,7 @@ class Filesystem // extract a prefix being a protocol://, protocol:, protocol://drive: or simply drive: if (preg_match('{^( [0-9a-z]{2,}+: (?: // (?: [a-z]: )? )? | [a-z]: )}ix', $path, $match)) { $prefix = $match[1]; - $path = substr($path, strlen($prefix)); + $path = substr($path, \strlen($prefix)); } if (substr($path, 0, 1) === '/') { @@ -579,7 +579,7 @@ class Filesystem $cwd = getcwd(); $relativePath = $this->findShortestPath($link, $target); - chdir(dirname($link)); + chdir(\dirname($link)); $result = @symlink($relativePath, $link); chdir($cwd); @@ -632,7 +632,7 @@ class Filesystem $resolved = rtrim($pathname, '/'); - if (!strlen($resolved)) { + if (!\strlen($resolved)) { return $pathname; } diff --git a/src/Composer/Util/Platform.php b/src/Composer/Util/Platform.php index 60bf9efa9..4dc9af07b 100644 --- a/src/Composer/Util/Platform.php +++ b/src/Composer/Util/Platform.php @@ -55,7 +55,7 @@ class Platform return $home; } - if (function_exists('posix_getuid') && function_exists('posix_getpwuid')) { + if (\function_exists('posix_getuid') && \function_exists('posix_getpwuid')) { $info = posix_getpwuid(posix_getuid()); return $info['dir']; @@ -69,7 +69,7 @@ class Platform */ public static function isWindows() { - return defined('PHP_WINDOWS_VERSION_BUILD'); + return \defined('PHP_WINDOWS_VERSION_BUILD'); } /** @@ -80,13 +80,13 @@ class Platform { static $useMbString = null; if (null === $useMbString) { - $useMbString = function_exists('mb_strlen') && ini_get('mbstring.func_overload'); + $useMbString = \function_exists('mb_strlen') && ini_get('mbstring.func_overload'); } if ($useMbString) { return mb_strlen($str, '8bit'); } - return strlen($str); + return \strlen($str); } } From 3c593b0d129c01032615281eaf6222729ac254f5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 30 Apr 2020 21:36:17 +0200 Subject: [PATCH 083/193] Remove duplicate use statement --- src/Composer/Repository/PlatformRepository.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Repository/PlatformRepository.php b/src/Composer/Repository/PlatformRepository.php index 9b0df0fb6..e003976a1 100644 --- a/src/Composer/Repository/PlatformRepository.php +++ b/src/Composer/Repository/PlatformRepository.php @@ -21,7 +21,6 @@ use Composer\Util\ProcessExecutor; use Composer\Util\Silencer; use Composer\Util\Platform; use Composer\XdebugHandler\XdebugHandler; -use Composer\Composer; use Symfony\Component\Process\ExecutableFinder; /** From 557fb873ee10c413bbd86d8c820c063c2b871a43 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 09:54:41 +0200 Subject: [PATCH 084/193] Add a way to retrieve the Loop instance from Composer to be able to wait on promises to integrate things --- src/Composer/Composer.php | 52 ++++++++++++++++++++++++++++----------- src/Composer/Factory.php | 1 + 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index c7ab40039..57cfdffe1 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -14,6 +14,7 @@ namespace Composer; use Composer\Package\RootPackageInterface; use Composer\Package\Locker; +use Composer\Util\Loop; use Composer\Repository\RepositoryManager; use Composer\Installer\InstallationManager; use Composer\Plugin\PluginManager; @@ -82,7 +83,7 @@ class Composer } /** - * @var Package\RootPackageInterface + * @var RootPackageInterface */ private $package; @@ -91,6 +92,11 @@ class Composer */ private $locker; + /** + * @var Loop + */ + private $loop; + /** * @var Repository\RepositoryManager */ @@ -132,7 +138,7 @@ class Composer private $archiveManager; /** - * @param Package\RootPackageInterface $package + * @param RootPackageInterface $package * @return void */ public function setPackage(RootPackageInterface $package) @@ -141,7 +147,7 @@ class Composer } /** - * @return Package\RootPackageInterface + * @return RootPackageInterface */ public function getPackage() { @@ -165,7 +171,7 @@ class Composer } /** - * @param Package\Locker $locker + * @param Locker $locker */ public function setLocker(Locker $locker) { @@ -173,7 +179,7 @@ class Composer } /** - * @return Package\Locker + * @return Locker */ public function getLocker() { @@ -181,7 +187,23 @@ class Composer } /** - * @param Repository\RepositoryManager $manager + * @param Loop $loop + */ + public function setLoop(loop $loop) + { + $this->loop = $loop; + } + + /** + * @return Loop + */ + public function getLoop() + { + return $this->loop; + } + + /** + * @param RepositoryManager $manager */ public function setRepositoryManager(RepositoryManager $manager) { @@ -189,7 +211,7 @@ class Composer } /** - * @return Repository\RepositoryManager + * @return RepositoryManager */ public function getRepositoryManager() { @@ -197,7 +219,7 @@ class Composer } /** - * @param Downloader\DownloadManager $manager + * @param DownloadManager $manager */ public function setDownloadManager(DownloadManager $manager) { @@ -205,7 +227,7 @@ class Composer } /** - * @return Downloader\DownloadManager + * @return DownloadManager */ public function getDownloadManager() { @@ -229,7 +251,7 @@ class Composer } /** - * @param Installer\InstallationManager $manager + * @param InstallationManager $manager */ public function setInstallationManager(InstallationManager $manager) { @@ -237,7 +259,7 @@ class Composer } /** - * @return Installer\InstallationManager + * @return InstallationManager */ public function getInstallationManager() { @@ -245,7 +267,7 @@ class Composer } /** - * @param Plugin\PluginManager $manager + * @param PluginManager $manager */ public function setPluginManager(PluginManager $manager) { @@ -253,7 +275,7 @@ class Composer } /** - * @return Plugin\PluginManager + * @return PluginManager */ public function getPluginManager() { @@ -277,7 +299,7 @@ class Composer } /** - * @param Autoload\AutoloadGenerator $autoloadGenerator + * @param AutoloadGenerator $autoloadGenerator */ public function setAutoloadGenerator(AutoloadGenerator $autoloadGenerator) { @@ -285,7 +307,7 @@ class Composer } /** - * @return Autoload\AutoloadGenerator + * @return AutoloadGenerator */ public function getAutoloadGenerator() { diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index dd1a7b308..9160ce2f5 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -336,6 +336,7 @@ class Factory $httpDownloader = self::createHttpDownloader($io, $config); $loop = new Loop($httpDownloader); + $composer->setLoop($loop); // initialize event dispatcher $dispatcher = new EventDispatcher($composer, $io); From 0071bc1ec0c97420f322a96a65f1fa96b0480bbc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 10:18:54 +0200 Subject: [PATCH 085/193] Add docs about new runtime features --- CHANGELOG.md | 4 +- doc/04-schema.md | 2 +- doc/06-config.md | 2 +- doc/07-runtime.md | 95 ++++++++++++++++++++++++ doc/{07-community.md => 08-community.md} | 2 +- doc/articles/autoloader-optimization.md | 2 +- 6 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 doc/07-runtime.md rename doc/{07-community.md => 08-community.md} (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13cb7866e..a3d3d84f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,8 @@ * The update command is now much more deterministic as it does not take the already installed packages into account * Package installation now performs all network operations first before doing any changes on disk, to reduce the chances of ending up with a partially updated vendor dir * Partial updates and require/remove are now much faster as they only load the metadata required for the updated packages - * Added a platform-check step when vendor/autoload.php gets initialized which checks the current PHP version/extensions match what is expected and fails hard otherwise. Can be disabled with the platform-check config option - * Added a [`Composer\InstalledVersions`](https://github.com/composer/composer/blob/d89342dc434d52c88e0e06ce3982da739a467f13/src/Composer/InstalledVersions.php) class which is autoloaded in every project and lets you check which packages/versions are present at runtime + * Added a [platform-check step](doc/07-runtime.md#platform-check) when vendor/autoload.php gets initialized which checks the current PHP version/extensions match what is expected and fails hard otherwise. Can be disabled with the platform-check config option + * Added a [`Composer\InstalledVersions`](doc/07-runtime.md#installed-versions) class which is autoloaded in every project and lets you check which packages/versions are present at runtime * Added a `composer-runtime-api` virtual package which you can require (as e.g. `^2.0`) to ensure things like the InstalledVersions class above are present. It will effectively force people to use Composer 2.x to install your project * Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present and we thus strongly recommend enabling curl * Added much clearer dependency resolution error reporting for common error cases diff --git a/doc/04-schema.md b/doc/04-schema.md index 130394e7a..d237282e3 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -1,4 +1,4 @@ -# The composer.json Schema +# The composer.json schema This chapter will explain all of the fields available in `composer.json`. diff --git a/doc/06-config.md b/doc/06-config.md index a79f3fe5d..1b0a8e648 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -312,4 +312,4 @@ file. Defaults to `true`. If set to `false`, Composer will not create and require a `platform_check.php` file as part of the autoloader bootstrap. -← [Repositories](05-repositories.md) | [Community](07-community.md) → +← [Repositories](05-repositories.md) | [Runtime](07-runtime.md) → diff --git a/doc/07-runtime.md b/doc/07-runtime.md new file mode 100644 index 000000000..f1e2cb52e --- /dev/null +++ b/doc/07-runtime.md @@ -0,0 +1,95 @@ +# Runtime Composer utilities + +While Composer is mostly used around your project to install its dependencies, +there are a few things which are made available to you at runtime. + +If you need to rely on some of these in a specific version, you can require +the `composer-runtime-api` package. + +## Autoload + +The autoloader is the most used one, and is already covered in our +[basic usage guide](#01-basic-usage.md#autoloading). It is available in all +Composer versions. + +## Installed versions + +composer-runtime-api 2.0 introduced a new `Composer\InstalledVersions` class which offers +a few static methods to inspect which versions are currently installed. This is +automatically available to your code as long as you include the Composer autoloader. + +The main use cases for this class are the following: + +### Knowing whether package X (or virtual package) is present + +```php +\Composer\InstalledVersions::isInstalled('vendor/package'); // returns bool +\Composer\InstalledVersions::isInstalled('psr/log-implementation'); // returns bool +``` + +Note that this can not be used to check whether platform packages are installed. + +### Knowing whether package X is installed in version Y + +> **Note:** To use this, your package must require `"composer/semver": "^2.0"`. + +```php +use Composer\Semver\VersionParser; + +\Composer\InstalledVersions::satisfies(new VersionParser, 'vendor/package', '2.0.*'); +\Composer\InstalledVersions::satisfies(new VersionParser, 'psr/log-implementation', '^1.0'); +``` + +This will return true if e.g. vendor/package is installed in a version matching +`2.0.*`, but also if the given package name is replaced or provided by some other +package. + +### Knowing the version of package X + +> **Note:** This will return `null` if the package name you ask for is not itself installed +> but merely provided or replaced by another package. We therefore recommend using satisfies() +> in library code at least. In application code you have a bit more control and it is less +> important. + +```php +// returns a normalized version (e.g. 1.2.3.0) if vendor/package is installed, +// or null if it is provided/replaced, +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getVersion('vendor/package'); +``` + +```php +// returns the original version (e.g. v1.2.3) if vendor/package is installed, +// or null if it is provided/replaced, +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getPrettyVersion('vendor/package'); +``` + +```php +// returns the package dist or source reference (e.g. a git commit hash) if vendor/package is installed, +// or null if it is provided/replaced, +// or throws OutOfBoundsException if the package is not installed at all +\Composer\InstalledVersions::getReference('vendor/package'); +``` + +A few other methods are available for more complex usages, please refer to the +source/docblocks of the class itself. + +## Platform check + +composer-runtime-api 2.0 introduced a new `vendor/composer/platform_check.php` file, which +is included automatically when you include the Composer autoloader. + +It verifies that platform requirements (i.e. php and php extensions) are fulfilled +by the PHP process currently running. If the requirements are not met, the script +prints a warning with the missing requirements and exits with code 104. + +To avoid an unexpected white page of death with some obscure PHP extension warning in +production, you can run `composer check-platform-reqs` as part of your deployment/build +and if that returns a non-0 code you should abort. + +If you for some reason do not want to use this safety check, and would rather +risk runtime errors when your code executes, you can disable this by setting the +[`platform-check`](06-config.md#platform-check) config option to `false`. + +← [Config](06-config.md) | [Community](08-community.md) → diff --git a/doc/07-community.md b/doc/08-community.md similarity index 97% rename from doc/07-community.md rename to doc/08-community.md index 0a45827ec..fc140e0c7 100644 --- a/doc/07-community.md +++ b/doc/08-community.md @@ -32,4 +32,4 @@ for users and [#composer-dev](irc://irc.freenode.org/composer-dev) for developme Stack Overflow has a growing collection of [Composer related questions](https://stackoverflow.com/questions/tagged/composer-php). -← [Config](06-config.md) +← [Config](07-runtime.md) diff --git a/doc/articles/autoloader-optimization.md b/doc/articles/autoloader-optimization.md index 41bd4a6cb..11faac8a7 100644 --- a/doc/articles/autoloader-optimization.md +++ b/doc/articles/autoloader-optimization.md @@ -2,7 +2,7 @@ tagline: How to reduce the performance impact of the autoloader --> -# Autoloader Optimization +# Autoloader optimization By default, the Composer autoloader runs relatively fast. However, due to the way PSR-4 and PSR-0 autoloading rules are set up, it needs to check the filesystem From 56811b4c8fec9c2eda5bd47a2c079e121c668eb4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 13:10:50 +0200 Subject: [PATCH 086/193] Exclude platform require/provides from InstalledVersions as concrete ones are also not listed --- src/Composer/Repository/FilesystemRepository.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index 4bf4cb6ca..fe19b420b 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -155,6 +155,10 @@ class FilesystemRepository extends WritableArrayRepository // add provided/replaced packages foreach ($packages as $package) { foreach ($package->getReplaces() as $replace) { + // exclude platform replaces as when they are really there we can not check for their presence + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $replace->getTarget())) { + continue; + } $replaced = $replace->getPrettyConstraint(); if ($replaced === 'self.version') { $replaced = $package->getPrettyVersion(); @@ -164,6 +168,10 @@ class FilesystemRepository extends WritableArrayRepository } } foreach ($package->getProvides() as $provide) { + // exclude platform provides as when they are really there we can not check for their presence + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $provide->getTarget())) { + continue; + } $provided = $provide->getPrettyConstraint(); if ($provided === 'self.version') { $provided = $package->getPrettyVersion(); From fc8be2bed873b99af6c8fdbb9e7e403395312332 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 14:50:55 +0200 Subject: [PATCH 087/193] Add --no-dev to recommended check-platform-reqs command for prod deploy --- doc/07-runtime.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/07-runtime.md b/doc/07-runtime.md index f1e2cb52e..35d4276fb 100644 --- a/doc/07-runtime.md +++ b/doc/07-runtime.md @@ -85,8 +85,8 @@ by the PHP process currently running. If the requirements are not met, the scrip prints a warning with the missing requirements and exits with code 104. To avoid an unexpected white page of death with some obscure PHP extension warning in -production, you can run `composer check-platform-reqs` as part of your deployment/build -and if that returns a non-0 code you should abort. +production, you can run `composer check-platform-reqs --no-dev` as part of your +deployment/build and if that returns a non-0 code you should abort. If you for some reason do not want to use this safety check, and would rather risk runtime errors when your code executes, you can disable this by setting the From 56a11b9c2c0a498d76ead8d53839347db4342397 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 15:41:54 +0200 Subject: [PATCH 088/193] Deduplicate require/conflict rules which are the same but for different versions of the same package, fixes #8851 --- src/Composer/DependencyResolver/Problem.php | 84 ++++++++++++++----- .../deduplicate-solver-problems.test | 48 +++++++++++ 2 files changed, 111 insertions(+), 21 deletions(-) create mode 100644 tests/Composer/Test/Fixtures/installer/deduplicate-solver-problems.test diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 9e8f02586..3ec6a72cc 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -95,11 +95,41 @@ class Problem } $messages = array(); + $templates = array(); + $parser = new VersionParser; + $deduplicatableRuleTypes = array(Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT); foreach ($reasons as $rule) { - $messages[] = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); + $message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); + if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && preg_match('{^(?P\S+) (?P\S+) (?Prequires|conflicts)}', $message, $m)) { + $template = preg_replace('{^\S+ \S+ }', '%s%s ', $message); + $messages[] = $template; + $templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2]; + } else { + $messages[] = $message; + } } - return "\n - ".implode("\n - ", array_unique($messages)); + $result = array(); + foreach (array_unique($messages) as $message) { + if (isset($templates[$message])) { + foreach ($templates[$message] as $package => $versions) { + if (!$isVerbose) { + $versions = self::condenseVersionList($versions, 1); + } + if (count($versions) > 1) { + // remove the s from requires/conflicts to correct grammar + $message = preg_replace('{^(%s%s (?:require|conflict))s}', '$1', $message); + $result[] = sprintf($message, $package, '['.implode(', ', $versions).']'); + } else { + $result[] = sprintf($message, $package, ' '.reset($versions)); + } + } + } else { + $result[] = $message; + } + } + + return "\n - ".implode("\n - ", $result); } public function isCausedByLock() @@ -299,32 +329,44 @@ class Problem if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) { unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]); } - if (!$isVerbose && count($package['versions']) > 4) { - uksort($package['versions'], 'version_compare'); - $filtered = array(); - $byMajor = array(); - foreach ($package['versions'] as $version => $pretty) { - $byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty; - } - foreach ($byMajor as $versions) { - if (count($versions) > 4) { - $filtered[] = $versions[0]; - $filtered[] = '...'; - $filtered[] = $versions[count($versions) - 1]; - } else { - $filtered = array_merge($filtered, $versions); - } - } - - $package['versions'] = $filtered; + if (!$isVerbose) { + $package['versions'] = self::condenseVersionList($package['versions'], 4); } - $prepared[$name] = $package['name'].'['.implode(', ', $package['versions']).']'; } return implode(', ', $prepared); } + /** + * @param string[] $versions an array of pretty versions, with normalized versions as keys + * @return list a list of pretty versions and '...' where versions were removed + */ + private static function condenseVersionList(array $versions, $max) + { + if (count($versions) <= $max) { + return $versions; + } + + uksort($versions, 'version_compare'); + $filtered = array(); + $byMajor = array(); + foreach ($versions as $version => $pretty) { + $byMajor[preg_replace('{^(\d+)\..*}', '$1', $version)][] = $pretty; + } + foreach ($byMajor as $versionsForMajor) { + if (count($versionsForMajor) > $max) { + $filtered[] = $versionsForMajor[0]; + $filtered[] = '...'; + $filtered[] = $versionsForMajor[count($versionsForMajor) - 1]; + } else { + $filtered = array_merge($filtered, $versionsForMajor); + } + } + + return $filtered; + } + private static function hasMultipleNames(array $packages) { $name = null; diff --git a/tests/Composer/Test/Fixtures/installer/deduplicate-solver-problems.test b/tests/Composer/Test/Fixtures/installer/deduplicate-solver-problems.test new file mode 100644 index 000000000..bbb245914 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/deduplicate-solver-problems.test @@ -0,0 +1,48 @@ +--TEST-- +Test the error output of solver problems is deduplicated. +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "package/a", "version": "2.0.0", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.0.1", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.0.2", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.0.3", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.1.0", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.2.0", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.3.1", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.3.2", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.3.3", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.3.4", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.3.5", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.4.0", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.5.0", "require": { "missing/dep": "^1.0" } }, + { "name": "package/a", "version": "2.6.0", "require": { "missing/dep": "^1.0" } }, + { "name": "missing/dep", "version": "2.0.0" } + ] + } + ], + "require": { + "package/a": "*" + } +} + +--RUN-- +update + +--EXPECT-EXIT-CODE-- +2 + +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies +Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - package/a[2.0.0, ..., 2.6.0] require missing/dep ^1.0 -> found missing/dep[2.0.0] but it does not match the constraint. + - Root composer.json requires package/a * -> satisfiable by package/a[2.0.0, ..., 2.6.0]. + +--EXPECT-- + From 3a64acb53e470e4cee7448aff565eea85cb2ee0e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 15:47:11 +0200 Subject: [PATCH 089/193] Make sure versions are ordered also in the satisfied by package lists --- src/Composer/DependencyResolver/Problem.php | 5 ++++- .../Test/Fixtures/installer/alias-solver-problems.test | 2 +- .../Test/Fixtures/installer/provider-conflicts3.test | 8 ++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 3ec6a72cc..112c13174 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -113,6 +113,7 @@ class Problem foreach (array_unique($messages) as $message) { if (isset($templates[$message])) { foreach ($templates[$message] as $package => $versions) { + uksort($versions, 'version_compare'); if (!$isVerbose) { $versions = self::condenseVersionList($versions, 1); } @@ -329,6 +330,9 @@ class Problem if (isset($package['versions'][VersionParser::DEV_MASTER_ALIAS]) && isset($package['versions']['dev-master'])) { unset($package['versions'][VersionParser::DEV_MASTER_ALIAS]); } + + uksort($package['versions'], 'version_compare'); + if (!$isVerbose) { $package['versions'] = self::condenseVersionList($package['versions'], 4); } @@ -348,7 +352,6 @@ class Problem return $versions; } - uksort($versions, 'version_compare'); $filtered = array(); $byMajor = array(); foreach ($versions as $version => $pretty) { diff --git a/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test b/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test index ce1aea7bb..613bc4159 100644 --- a/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test +++ b/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test @@ -47,7 +47,7 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master]. - a/a dev-master requires d/d 1.0.0 -> satisfiable by d/d[1.0.0]. - - You can only install one version of a package, so only one of these can be installed: d/d[2.0.0, 1.0.0]. + - You can only install one version of a package, so only one of these can be installed: d/d[1.0.0, 2.0.0]. - Conclusion: install d/d 2.0.0, learned rules: - Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master]. - b/b dev-master requires d/d 2.0.0 -> satisfiable by d/d[2.0.0]. diff --git a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test index 1c2ea0ceb..53d11ecf0 100644 --- a/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test +++ b/tests/Composer/Test/Fixtures/installer/provider-conflicts3.test @@ -39,14 +39,14 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Conclusion: don't install regular/pkg 1.0.3, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Conclusion: don't install regular/pkg 1.0.2, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Conclusion: don't install regular/pkg 1.0.1, learned rules: - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - - Only one of these can be installed: regular/pkg[1.0.3, 1.0.2, 1.0.1, 1.0.0], replacer/pkg[2.0.3, 2.0.2, 2.0.1, 2.0.0]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. + - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3]. - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. From 7f308d986e96f17430927ed7e16dc19c2c333a4e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 16:56:17 +0200 Subject: [PATCH 090/193] Add --with to update command to allow downgrading to a specific version/applying custom temporary constraints, fixes #8756 --- doc/03-cli.md | 21 ++++++++ src/Composer/Command/BaseCommand.php | 22 ++++++++ src/Composer/Command/InitCommand.php | 18 ------- src/Composer/Command/UpdateCommand.php | 69 ++++++++++++++++++++++++-- 4 files changed, 107 insertions(+), 23 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 14b6e44a2..7e8f8a742 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -142,6 +142,26 @@ You can also use wildcards to update a bunch of packages at once: php composer.phar update "vendor/*" ``` + +If you want to downgrade a package to a specific version without changing your +composer.json you can use `--with` and provide a custom version constraint: + +```sh +php composer.phar update --with vendor/package:2.0.1 +``` + +The custom constraint has to be a subset of the existing constraint you have, +and this feature is only available for your root package dependencies. + +If you only want to update the package(s) for which you provide custom constraints +using `--with`, you can skip `--with` and just use constraints with the partial +update syntax: + +```sh +php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.* +``` + + ### Options * **--prefer-source:** Install packages from `source` when available. @@ -152,6 +172,7 @@ php composer.phar update "vendor/*" * **--no-install:** Does not run the install step after updating the composer.lock file. * **--lock:** Only updates the lock file hash to suppress warning about the lock file being out of date. +* **--with:** Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0 * **--no-autoloader:** Skips autoloader generation. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--no-progress:** Removes the progress display that can mess with some diff --git a/src/Composer/Command/BaseCommand.php b/src/Composer/Command/BaseCommand.php index 56ee9f7f4..8c45899cf 100644 --- a/src/Composer/Command/BaseCommand.php +++ b/src/Composer/Command/BaseCommand.php @@ -19,6 +19,7 @@ use Composer\Factory; use Composer\IO\IOInterface; use Composer\IO\NullIO; use Composer\Plugin\PreCommandRunEvent; +use Composer\Package\Version\VersionParser; use Composer\Plugin\PluginEvents; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -180,4 +181,25 @@ abstract class BaseCommand extends Command return array($preferSource, $preferDist); } + + protected function formatRequirements(array $requirements) + { + $requires = array(); + $requirements = $this->normalizeRequirements($requirements); + foreach ($requirements as $requirement) { + if (!isset($requirement['version'])) { + throw new \UnexpectedValueException('Option '.$requirement['name'] .' is missing a version constraint, use e.g. '.$requirement['name'].':^1.0'); + } + $requires[$requirement['name']] = $requirement['version']; + } + + return $requires; + } + + protected function normalizeRequirements(array $requirements) + { + $parser = new VersionParser(); + + return $parser->parseNameVersionPairs($requirements); + } } diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 8f101515c..b8401f201 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -577,17 +577,6 @@ EOT return array($this->parseAuthorString($author)); } - protected function formatRequirements(array $requirements) - { - $requires = array(); - $requirements = $this->normalizeRequirements($requirements); - foreach ($requirements as $requirement) { - $requires[$requirement['name']] = $requirement['version']; - } - - return $requires; - } - protected function getGitConfig() { if (null !== $this->gitConfig) { @@ -652,13 +641,6 @@ EOT return false; } - protected function normalizeRequirements(array $requirements) - { - $parser = new VersionParser(); - - return $parser->parseNameVersionPairs($requirements); - } - protected function addVendorIgnore($ignoreFile, $vendor = '/vendor/') { $contents = ""; diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 4bb15fc9b..3ca704620 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -18,6 +18,9 @@ use Composer\Installer; use Composer\IO\IOInterface; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Package\Version\VersionParser; +use Composer\Semver\Constraint\MultiConstraint; +use Composer\Package\Link; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -39,6 +42,7 @@ class UpdateCommand extends BaseCommand ->setDescription('Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.') ->setDefinition(array( new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Packages that should be updated, if not provided all packages are.'), + new InputOption('with', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Temporary version constraint to add, e.g. foo/bar:1.0.0 or foo/bar=1.0.0'), 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('dry-run', null, InputOption::VALUE_NONE, 'Outputs the operations but will not execute anything (implicitly enables --verbose).'), @@ -80,6 +84,14 @@ from a specific vendor: php composer.phar update vendor/package1 foo/* [...] +To run an update with more restrictive constraints you can use: + +php composer.phar update --with vendor/package:1.0.* + +To run a partial update with more restrictive constraints you can use the shorthand: + +php composer.phar update vendor/package:1.0.* + To select packages names interactively with auto-completion use -i. Read more at https://getcomposer.org/doc/03-cli.md#update-u @@ -101,22 +113,54 @@ EOT $composer = $this->getComposer(true, $input->getOption('no-plugins')); $packages = $input->getArgument('packages'); + $reqs = $this->formatRequirements($input->getOption('with')); + + // extract --with shorthands from the allowlist + if ($packages) { + $allowlistPackagesWithRequirements = array_filter($packages, function ($pkg) { + return preg_match('{\S+[ =:]\S+}', $pkg) > 0; + }); + foreach ($this->formatRequirements($allowlistPackagesWithRequirements) as $package => $constraint) { + var_Dump($package, $constraint); + $reqs[$package] = $constraint; + } + + // replace the foo/bar:req by foo/bar in the allowlist + foreach ($allowlistPackagesWithRequirements as $package) { + $packageName = preg_replace('{^([^ =:]+)[ =:].*$}', '$1', $package); + $index = array_search($package, $packages); + $packages[$index] = $packageName; + } + } + + $rootRequires = $composer->getPackage()->getRequires(); + $rootDevRequires = $composer->getPackage()->getDevRequires(); + foreach ($reqs as $package => $constraint) { + if (isset($rootRequires[$package])) { + $rootRequires[$package] = $this->appendConstraintToLink($rootRequires[$package], $constraint); + } elseif (isset($rootDevRequires[$package])) { + $rootDevRequires[$package] = $this->appendConstraintToLink($rootDevRequires[$package], $constraint); + } else { + throw new \UnexpectedValueException('Only root package requirements can receive temporary constraints and '.$package.' is not one'); + } + } + $composer->getPackage()->setRequires($rootRequires); + $composer->getPackage()->setDevRequires($rootDevRequires); if ($input->getOption('interactive')) { $packages = $this->getPackagesInteractively($io, $input, $output, $composer, $packages); } if ($input->getOption('root-reqs')) { - $require = array_keys($composer->getPackage()->getRequires()); + $requires = array_keys($rootRequires); if (!$input->getOption('no-dev')) { - $requireDev = array_keys($composer->getPackage()->getDevRequires()); - $require = array_merge($require, $requireDev); + $requires = array_merge($requires, array_keys($rootDevRequires)); } if (!empty($packages)) { - $packages = array_intersect($packages, $require); + $packages = array_intersect($packages, $requires); } else { - $packages = $require; + $packages = $requires; } } @@ -242,4 +286,19 @@ EOT throw new \RuntimeException('Installation aborted.'); } + + private function appendConstraintToLink(Link $link, $constraint) + { + $parser = new VersionParser; + $oldPrettyString = $link->getConstraint()->getPrettyString(); + $newConstraint = MultiConstraint::create(array($link->getConstraint(), $parser->parseConstraints($constraint))); + $newConstraint->setPrettyString($oldPrettyString.' && '.$constraint); + return new Link( + $link->getSource(), + $link->getTarget(), + $newConstraint, + $link->getDescription(), + $link->getPrettyConstraint() . ' && ' . $constraint + ); + } } From 4ce3836f512d7d0b86cd563e15f5d0d0c607159f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 17:23:32 +0200 Subject: [PATCH 091/193] Fix creation of a php: dir when running create-project, fixes #8849 --- src/Composer/Json/JsonFile.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index d1d189287..1e30690c1 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -112,11 +112,17 @@ class JsonFile */ public function write(array $hash, $options = 448) { + if ($this->path === 'php://memory') { + file_put_contents($this->path, static::encode($hash, $options)); + + return; + } + $dir = dirname($this->path); if (!is_dir($dir)) { if (file_exists($dir)) { throw new \UnexpectedValueException( - $dir.' exists and is not a directory.' + realpath($dir).' exists and is not a directory.' ); } if (!@mkdir($dir, 0777, true)) { From bfe71fb9520c0862f3465e4e92af43d178b4955c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 1 May 2020 18:03:22 +0200 Subject: [PATCH 092/193] Fix support for --self --name-only, fixes #8844 --- src/Composer/Command/ShowCommand.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index b515cc6a9..40d4f5981 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -152,6 +152,10 @@ EOT if ($input->getOption('self')) { $package = $this->getComposer()->getPackage(); + if ($input->getOption('name-only')) { + $io->write($package->getName()); + return 0; + } $repos = $installedRepo = new ArrayRepository(array($package)); } elseif ($input->getOption('platform')) { $repos = $installedRepo = $platformRepo; From 7c891701e6ca13a79e0131f06c9eddfd9a315c9b Mon Sep 17 00:00:00 2001 From: Helmut Hummel Date: Tue, 31 Mar 2020 13:29:22 +0200 Subject: [PATCH 093/193] Fix package sorting PackageSorter weighs the importance of a package by counting how many times it is required by other packages. This works by calculating the weight for each package name. However currently the package index of the package array is currently passed the weigh function, which basically disables package sorting. The reason for that is, that a package repository previously returned the package list as associative array with package name as keys, but currently just as an array with integer keys. Therefore we must extract the package name from the package before passing it to the weigh function. --- src/Composer/Util/PackageSorter.php | 10 +- .../Composer/Test/Util/PackageSorterTest.php | 129 ++++++++++++++++++ 2 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 tests/Composer/Test/Util/PackageSorterTest.php diff --git a/src/Composer/Util/PackageSorter.php b/src/Composer/Util/PackageSorter.php index 8d8c9a06c..204a35b51 100644 --- a/src/Composer/Util/PackageSorter.php +++ b/src/Composer/Util/PackageSorter.php @@ -55,9 +55,9 @@ class PackageSorter $weightList = array(); - foreach ($packages as $name => $package) { - $weight = $computeImportance($name); - $weightList[$name] = $weight; + foreach ($packages as $index => $package) { + $weight = $computeImportance($package->getName()); + $weightList[$index] = $weight; } $stable_sort = function (&$array) { @@ -84,8 +84,8 @@ class PackageSorter $sortedPackages = array(); - foreach (array_keys($weightList) as $name) { - $sortedPackages[] = $packages[$name]; + foreach (array_keys($weightList) as $index) { + $sortedPackages[] = $packages[$index]; } return $sortedPackages; } diff --git a/tests/Composer/Test/Util/PackageSorterTest.php b/tests/Composer/Test/Util/PackageSorterTest.php new file mode 100644 index 000000000..e93503436 --- /dev/null +++ b/tests/Composer/Test/Util/PackageSorterTest.php @@ -0,0 +1,129 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Util; + +use Composer\Package\Link; +use Composer\Package\Package; +use Composer\Test\TestCase; +use Composer\Util\PackageSorter; + +class PackageSorterTest extends TestCase +{ + public function testSortingDoesNothingWithNoDependencies() + { + $packages[] = $this->createPackage('foo/bar1', array()); + $packages[] = $this->createPackage('foo/bar2', array()); + $packages[] = $this->createPackage('foo/bar3', array()); + $packages[] = $this->createPackage('foo/bar4', array()); + + $sortedPackages = PackageSorter::sortPackages($packages); + + self::assertSame($packages, $sortedPackages); + } + + public function sortingOrdersDependenciesHigherThanPackageDataProvider() + { + return array( + 'one package is dep' => array( + array( + $this->createPackage('foo/bar1', array('foo/bar4')), + $this->createPackage('foo/bar2', array('foo/bar4')), + $this->createPackage('foo/bar3', array('foo/bar4')), + $this->createPackage('foo/bar4', array()), + ), + array( + 'foo/bar4', + 'foo/bar1', + 'foo/bar2', + 'foo/bar3', + ), + ), + 'one package has more deps' => array( + array( + $this->createPackage('foo/bar1', array('foo/bar2')), + $this->createPackage('foo/bar2', array('foo/bar4')), + $this->createPackage('foo/bar3', array('foo/bar4')), + $this->createPackage('foo/bar4', array()), + ), + array( + 'foo/bar4', + 'foo/bar2', + 'foo/bar1', + 'foo/bar3', + ), + ), + 'package is required by many, but requires one other' => array( + array( + $this->createPackage('foo/bar1', array('foo/bar3')), + $this->createPackage('foo/bar2', array('foo/bar3')), + $this->createPackage('foo/bar3', array('foo/bar4')), + $this->createPackage('foo/bar4', array()), + $this->createPackage('foo/bar5', array('foo/bar3')), + $this->createPackage('foo/bar6', array('foo/bar3')), + ), + array( + 'foo/bar4', + 'foo/bar3', + 'foo/bar1', + 'foo/bar2', + 'foo/bar5', + 'foo/bar6', + ), + ), + 'one package has many requires' => array( + array( + $this->createPackage('foo/bar1', array('foo/bar2')), + $this->createPackage('foo/bar2', array()), + $this->createPackage('foo/bar3', array('foo/bar4')), + $this->createPackage('foo/bar4', array()), + $this->createPackage('foo/bar5', array('foo/bar2')), + $this->createPackage('foo/bar6', array('foo/bar2')), + ), + array( + 'foo/bar2', + 'foo/bar4', + 'foo/bar1', + 'foo/bar3', + 'foo/bar5', + 'foo/bar6', + ), + ), + ); + } + + /** + * @dataProvider sortingOrdersDependenciesHigherThanPackageDataProvider + * @param array $packages + * @param array $expectedOrderedList + */ + public function testSortingOrdersDependenciesHigherThanPackage($packages, $expectedOrderedList) + { + $sortedPackages = PackageSorter::sortPackages($packages); + $sortedPackageNames = array_map(function ($package) { return $package->getName(); }, $sortedPackages); + + self::assertSame($expectedOrderedList, $sortedPackageNames); + } + + private function createPackage($name, $requires) + { + $package = new Package($name, '1.0.0.0', '1.0.0'); + + $links = array(); + foreach ($requires as $requireName) { + $links[] = new Link($package->getName(), $requireName); + } + $package->setRequires($links); + + return $package; + } +} From d4c8478df52f9eb5335108d558d363b668d12f8d Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Mon, 4 May 2020 00:38:31 +0700 Subject: [PATCH 094/193] Improve regex in \Composer\Repository\ComposerRepository::fetchFile() by removing unnecessary greedy operator --- src/Composer/Repository/ComposerRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index f9d3155c1..5f68de635 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -997,7 +997,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } // url-encode $ signs in URLs as bad proxies choke on them - if (($pos = strpos($filename, '$')) && preg_match('{^https?://.*}i', $filename)) { + if (($pos = strpos($filename, '$')) && preg_match('{^https?://}i', $filename)) { $filename = substr($filename, 0, $pos) . '%24' . substr($filename, $pos + 1); } From aa2041399079a82280a96ed5515d1f51557c4eaf Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Mon, 4 May 2020 00:50:53 +0700 Subject: [PATCH 095/193] Improve regex in \Composer\Downloader\SvnDownloader::getCommitLogs() by removing unnecessary greedy operator --- src/Composer/Downloader/SvnDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 0281862b5..634c4a7d5 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -178,7 +178,7 @@ class SvnDownloader extends VcsDownloader */ protected function getCommitLogs($fromReference, $toReference, $path) { - if (preg_match('{.*@(\d+)$}', $fromReference) && preg_match('{.*@(\d+)$}', $toReference)) { + if (preg_match('{@(\d+)$}', $fromReference) && preg_match('{@(\d+)$}', $toReference)) { // retrieve the svn base url from the checkout folder $command = sprintf('svn info --non-interactive --xml %s', ProcessExecutor::escape($path)); if (0 !== $this->process->execute($command, $output, $path)) { From c23670c3ecb32e433beceab9df79c10d9d39f54d Mon Sep 17 00:00:00 2001 From: Michael Chekin Date: Mon, 4 May 2020 09:48:23 +0200 Subject: [PATCH 096/193] Add Util\AuthHelper unit test coverage (#8863) * Add AuthHelper::addAuthenticationHeader() test on missing authentication credentials. * Add AuthHelper::addAuthenticationHeader() test on bearer password. * Add AuthHelper::addAuthenticationHeader() test on Github token. * Add AuthHelper::addAuthenticationHeader() test on Gitlab Oauth token. * Add $authenticationDisplayMessage write expectation to AuthHelper::addAuthenticationHeader() tests. * Add AuthHelper::addAuthenticationHeader() test on Gitlab private token. * Add AuthHelper::addAuthenticationHeader() test on Bitbucket Oauth token. * Add AuthHelper::addAuthenticationHeader() test on Bitbucket public urls. * Add AuthHelper::addAuthenticationHeader() test on Basic Http Authentication. * Add AuthHelper::isPublicBitBucketDownload() tests. * Rename AuthHelperTest $credentials variable to $auth. * Add AuthHelper::storeAuth() test for auto-store option. * Add AuthHelper::storeAuth() test for user prompt and y(es) answer. * Add AuthHelper::storeAuth() test for user prompt and n(o) answer. * Add AuthHelper::storeAuth() test for user prompt with invalid answer. * Add AuthHelper::promptAuthIfNeeded() test for Github authentication failure. - add GitHub hard dependency mock (new GitHub(...) mock) * Run AuthHelper::promptAuthIfNeeded() tests only with PHP > 5.3 * Run AuthHelper::promptAuthIfNeeded() tests only with PHP >= 5.4 * Run AuthHelper::promptAuthIfNeeded() tests only with PHP 5.4 * Exclude PHPStan analyses of '../tests/Composer/Test/Util/Mocks/*' * Exclude AuthHelper::promptAuthIfNeeded() tests from current pull request. * Extract repetitive AuthHelperTest authentication expectation into a method. --- tests/Composer/Test/Util/AuthHelperTest.php | 525 ++++++++++++++++++++ 1 file changed, 525 insertions(+) create mode 100644 tests/Composer/Test/Util/AuthHelperTest.php diff --git a/tests/Composer/Test/Util/AuthHelperTest.php b/tests/Composer/Test/Util/AuthHelperTest.php new file mode 100644 index 000000000..567299345 --- /dev/null +++ b/tests/Composer/Test/Util/AuthHelperTest.php @@ -0,0 +1,525 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Util; + +use Composer\IO\IOInterface; +use Composer\Test\TestCase; +use Composer\Util\AuthHelper; +use Composer\Util\Bitbucket; +use RuntimeException; + +/** + * @author Michael Chekin + */ +class AuthHelperTest extends TestCase +{ + /** @type \Composer\IO\IOInterface|\PHPUnit_Framework_MockObject_MockObject */ + private $io; + + /** @type \Composer\Config|\PHPUnit_Framework_MockObject_MockObject */ + private $config; + + /** @type AuthHelper */ + private $authHelper; + + protected function setUp() + { + $this->io = $this + ->getMockBuilder('Composer\IO\IOInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->config = $this->getMockBuilder('Composer\Config')->getMock(); + + $this->authHelper = new AuthHelper($this->io, $this->config); + } + + public function testAddAuthenticationHeaderWithoutAuthCredentials() + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'http://example.org'; + $url = 'file://' . __FILE__; + + $this->io->expects($this->once()) + ->method('hasAuthentication') + ->with($origin) + ->willReturn(false); + + $this->assertSame( + $headers, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function testAddAuthenticationHeaderWithBearerPassword() + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'http://example.org'; + $url = 'file://' . __FILE__; + $auth = array( + 'username' => 'my_username', + 'password' => 'bearer' + ); + + $this->expectsAuthentication($origin, $auth); + + $expectedHeaders = array_merge($headers, array('Authorization: Bearer ' . $auth['username'])); + + $this->assertSame( + $expectedHeaders, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function testAddAuthenticationHeaderWithGithubToken() + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'github.com'; + $url = 'https://api.github.com/'; + $auth = array( + 'username' => 'my_username', + 'password' => 'x-oauth-basic' + ); + + $this->expectsAuthentication($origin, $auth); + + $this->io->expects($this->once()) + ->method('writeError') + ->with('Using GitHub token authentication', true, IOInterface::DEBUG); + + $expectedHeaders = array_merge($headers, array('Authorization: token ' . $auth['username'])); + + $this->assertSame( + $expectedHeaders, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function testAddAuthenticationHeaderWithGitlabOathToken() + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'gitlab.com'; + $url = 'https://api.gitlab.com/'; + $auth = array( + 'username' => 'my_username', + 'password' => 'oauth2' + ); + + $this->expectsAuthentication($origin, $auth); + + $this->config->expects($this->once()) + ->method('get') + ->with('gitlab-domains') + ->willReturn(array($origin)); + + $this->io->expects($this->once()) + ->method('writeError') + ->with('Using GitLab OAuth token authentication', true, IOInterface::DEBUG); + + $expectedHeaders = array_merge($headers, array('Authorization: Bearer ' . $auth['username'])); + + $this->assertSame( + $expectedHeaders, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function gitlabPrivateTokenProvider() + { + return array( + array('private-token'), + array('gitlab-ci-token'), + ); + } + + /** + * @dataProvider gitlabPrivateTokenProvider + * + * @param string $password + */ + public function testAddAuthenticationHeaderWithGitlabPrivateToken($password) + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'gitlab.com'; + $url = 'https://api.gitlab.com/'; + $auth = array( + 'username' => 'my_username', + 'password' => $password + ); + + $this->expectsAuthentication($origin, $auth); + + $this->config->expects($this->once()) + ->method('get') + ->with('gitlab-domains') + ->willReturn(array($origin)); + + $this->io->expects($this->once()) + ->method('writeError') + ->with('Using GitLab private token authentication', true, IOInterface::DEBUG); + + $expectedHeaders = array_merge($headers, array('PRIVATE-TOKEN: ' . $auth['username'])); + + $this->assertSame( + $expectedHeaders, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function testAddAuthenticationHeaderWithBitbucketOathToken() + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'bitbucket.org'; + $url = 'https://bitbucket.org/site/oauth2/authorize'; + $auth = array( + 'username' => 'x-token-auth', + 'password' => 'my_password' + ); + + $this->expectsAuthentication($origin, $auth); + + $this->config->expects($this->once()) + ->method('get') + ->with('gitlab-domains') + ->willReturn(array()); + + $this->io->expects($this->once()) + ->method('writeError') + ->with('Using Bitbucket OAuth token authentication', true, IOInterface::DEBUG); + + $expectedHeaders = array_merge($headers, array('Authorization: Bearer ' . $auth['password'])); + + $this->assertSame( + $expectedHeaders, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function bitbucketPublicUrlProvider() + { + return array( + array('https://bitbucket.org/user/repo/downloads/whatever'), + array('https://bbuseruploads.s3.amazonaws.com/9421ee72-638e-43a9-82ea-39cfaae2bfaa/downloads/b87c59d9-54f3-4922-b711-d89059ec3bcf'), + ); + } + + /** + * @dataProvider bitbucketPublicUrlProvider + * + * @param string $url + */ + public function testAddAuthenticationHeaderWithBitbucketPublicUrl($url) + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + $origin = 'bitbucket.org'; + $auth = array( + 'username' => 'x-token-auth', + 'password' => 'my_password' + ); + + $this->expectsAuthentication($origin, $auth); + + $this->config->expects($this->once()) + ->method('get') + ->with('gitlab-domains') + ->willReturn(array()); + + $this->assertSame( + $headers, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + public function basicHttpAuthenticationProvider() + { + return array( + array( + Bitbucket::OAUTH2_ACCESS_TOKEN_URL, + 'bitbucket.org', + array( + 'username' => 'x-token-auth', + 'password' => 'my_password' + ) + ), + array( + 'https://some-api.url.com', + 'some-api.url.com', + array( + 'username' => 'my_username', + 'password' => 'my_password' + ) + ), + ); + } + + /** + * @dataProvider basicHttpAuthenticationProvider + * + * @param string $url + * @param string $origin + * @param array $auth + */ + public function testAddAuthenticationHeaderWithBasicHttpAuthentication($url, $origin, $auth) + { + $headers = array( + 'Accept-Encoding: gzip', + 'Connection: close' + ); + + $this->expectsAuthentication($origin, $auth); + + $this->config->expects($this->once()) + ->method('get') + ->with('gitlab-domains') + ->willReturn(array()); + + $this->io->expects($this->once()) + ->method('writeError') + ->with( + 'Using HTTP basic authentication with username "' . $auth['username'] . '"', + true, + IOInterface::DEBUG + ); + + $expectedHeaders = array_merge( + $headers, + array('Authorization: Basic ' . base64_encode($auth['username'] . ':' . $auth['password'])) + ); + + $this->assertSame( + $expectedHeaders, + $this->authHelper->addAuthenticationHeader($headers, $origin, $url) + ); + } + + /** + * @dataProvider bitbucketPublicUrlProvider + * + * @param string $url + */ + public function testIsPublicBitBucketDownloadWithBitbucketPublicUrl($url) + { + $this->assertTrue($this->authHelper->isPublicBitBucketDownload($url)); + } + + public function testIsPublicBitBucketDownloadWithNonBitbucketPublicUrl() + { + $this->assertFalse($this->authHelper->isPublicBitBucketDownload( + 'https://bitbucket.org/site/oauth2/authorize') + ); + } + + public function testStoreAuthAutomatically() + { + $origin = 'github.com'; + $storeAuth = true; + $auth = array( + 'username' => 'my_username', + 'password' => 'my_password' + ); + + /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */ + $configSource = $this + ->getMockBuilder('Composer\Config\ConfigSourceInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->config->expects($this->once()) + ->method('getAuthConfigSource') + ->willReturn($configSource); + + $this->io->expects($this->once()) + ->method('getAuthentication') + ->with($origin) + ->willReturn($auth); + + $configSource->expects($this->once()) + ->method('addConfigSetting') + ->with('http-basic.'.$origin, $auth) + ->willReturn($configSource); + + $this->authHelper->storeAuth($origin, $storeAuth); + } + + public function testStoreAuthWithPromptYesAnswer() + { + $origin = 'github.com'; + $storeAuth = 'prompt'; + $auth = array( + 'username' => 'my_username', + 'password' => 'my_password' + ); + $answer = 'y'; + $configSourceName = 'https://api.gitlab.com/source'; + + /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */ + $configSource = $this + ->getMockBuilder('Composer\Config\ConfigSourceInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->config->expects($this->once()) + ->method('getAuthConfigSource') + ->willReturn($configSource); + + $configSource->expects($this->once()) + ->method('getName') + ->willReturn($configSourceName); + + $this->io->expects($this->once()) + ->method('askAndValidate') + ->with( + 'Do you want to store credentials for '.$origin.' in '.$configSourceName.' ? [Yn] ', + $this->anything(), + null, + 'y' + ) + ->willReturnCallback(function ($question, $validator, $attempts, $default) use ($answer) { + + $validator($answer); + + return $answer; + }); + + $this->io->expects($this->once()) + ->method('getAuthentication') + ->with($origin) + ->willReturn($auth); + + $configSource->expects($this->once()) + ->method('addConfigSetting') + ->with('http-basic.'.$origin, $auth) + ->willReturn($configSource); + + $this->authHelper->storeAuth($origin, $storeAuth); + } + + public function testStoreAuthWithPromptNoAnswer() + { + $origin = 'github.com'; + $storeAuth = 'prompt'; + $answer = 'n'; + $configSourceName = 'https://api.gitlab.com/source'; + + /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */ + $configSource = $this + ->getMockBuilder('Composer\Config\ConfigSourceInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->config->expects($this->once()) + ->method('getAuthConfigSource') + ->willReturn($configSource); + + $configSource->expects($this->once()) + ->method('getName') + ->willReturn($configSourceName); + + $this->io->expects($this->once()) + ->method('askAndValidate') + ->with( + 'Do you want to store credentials for '.$origin.' in '.$configSourceName.' ? [Yn] ', + $this->anything(), + null, + 'y' + ) + ->willReturnCallback(function ($question, $validator, $attempts, $default) use ($answer) { + + $validator($answer); + + return $answer; + }); + + $this->authHelper->storeAuth($origin, $storeAuth); + } + + /** + * @expectedException RuntimeException + */ + public function testStoreAuthWithPromptInvalidAnswer() + { + $origin = 'github.com'; + $storeAuth = 'prompt'; + $answer = 'invalid'; + $configSourceName = 'https://api.gitlab.com/source'; + + /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */ + $configSource = $this + ->getMockBuilder('Composer\Config\ConfigSourceInterface') + ->disableOriginalConstructor() + ->getMock(); + + $this->config->expects($this->once()) + ->method('getAuthConfigSource') + ->willReturn($configSource); + + $configSource->expects($this->once()) + ->method('getName') + ->willReturn($configSourceName); + + $this->io->expects($this->once()) + ->method('askAndValidate') + ->with( + 'Do you want to store credentials for '.$origin.' in '.$configSourceName.' ? [Yn] ', + $this->anything(), + null, + 'y' + ) + ->willReturnCallback(function ($question, $validator, $attempts, $default) use ($answer) { + + $validator($answer); + + return $answer; + }); + + $this->authHelper->storeAuth($origin, $storeAuth); + } + + /** + * @param $origin + * @param $auth + */ + private function expectsAuthentication($origin, $auth) + { + $this->io->expects($this->once()) + ->method('hasAuthentication') + ->with($origin) + ->willReturn(true); + + $this->io->expects($this->once()) + ->method('getAuthentication') + ->with($origin) + ->willReturn($auth); + } +} From d15447c4547c02aa96d0f4baf423659bca0d4eea Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 4 May 2020 10:28:17 +0200 Subject: [PATCH 097/193] Add test for replace version overlap --- .../replace-range-require-single-version.test | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/replace-range-require-single-version.test diff --git a/tests/Composer/Test/Fixtures/installer/replace-range-require-single-version.test b/tests/Composer/Test/Fixtures/installer/replace-range-require-single-version.test new file mode 100644 index 000000000..00b780c4c --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/replace-range-require-single-version.test @@ -0,0 +1,30 @@ +--TEST-- +Verify replacing an unbound range and requiring a single version works as well as vice versa. + +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "a/a", "version": "1.0.1", "replace": {"c/c": ">2.0" }}, + { "name": "b/b", "version": "1.0.2", "require": {"c/c": "2.1.2" }}, + { "name": "d/d", "version": "1.0.3", "replace": {"f/f": "2.1.2" }}, + { "name": "e/e", "version": "1.0.4", "require": {"f/f": ">2.0" }} + ] + } + ], + "require": { + "a/a": "1.0.1", + "b/b": "1.0.2", + "d/d": "1.0.3", + "e/e": "1.0.4" + } +} +--RUN-- +update +--EXPECT-- +Installing a/a (1.0.1) +Installing b/b (1.0.2) +Installing d/d (1.0.3) +Installing e/e (1.0.4) From 11930001fba920367be95fc934363462f3f81f74 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 4 May 2020 13:34:05 +0200 Subject: [PATCH 098/193] Added phpdoc types for PoolBuilder (#8862) --- .../DependencyResolver/PoolBuilder.php | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index b8ad9b281..9d2d3ade9 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -21,6 +21,7 @@ use Composer\Package\Version\StabilityFilter; use Composer\Repository\PlatformRepository; use Composer\Repository\RootPackageRepository; use Composer\Semver\Constraint\Constraint; +use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\EmptyConstraint; use Composer\Semver\Constraint\MultiConstraint; use Composer\EventDispatcher\EventDispatcher; @@ -32,23 +33,53 @@ use Composer\Plugin\PluginEvents; */ class PoolBuilder { + /** + * @var int[] + */ private $acceptableStabilities; + /** + * @var int[] + */ private $stabilityFlags; /** * @psalm-var array> */ private $rootAliases; + /** + * @psalm-var array + */ private $rootReferences; + /** + * @var EventDispatcher + */ private $eventDispatcher; + /** + * @var IOInterface + */ private $io; + /** + * @psalm-var array + */ private $aliasMap = array(); + /** + * @psalm-var array + */ private $nameConstraints = array(); private $loadedNames = array(); + /** + * @psalm-var Package[] + */ private $packages = array(); + /** + * @psalm-var list + */ private $unacceptableFixedPackages = array(); private $updateAllowList = array(); private $skippedLoad = array(); + /** + * @psalm-var array + */ private $updateAllowWarned = array(); /** From 92aed041e4b06db5ad348f4043dff17c11938201 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 13:51:34 +0200 Subject: [PATCH 099/193] Make the VersionSelector take Composer API/Runtime versions into account, refs getsentry/sentry-php#1008 --- src/Composer/Package/Version/VersionSelector.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 8e225d803..200f56e0f 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -15,6 +15,8 @@ namespace Composer\Package\Version; use Composer\DependencyResolver\Pool; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; +use Composer\Plugin\PluginInterface; +use Composer\Composer; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; use Composer\Semver\Constraint\Constraint; @@ -53,10 +55,14 @@ class VersionSelector if ($targetPhpVersion) { $phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion)); - $candidates = array_filter($candidates, function ($pkg) use ($phpConstraint) { + $composerRuntimeConstraint = new Constraint('==', $this->getParser()->normalize(Composer::RUNTIME_API_VERSION)); + $composerPluginConstraint = new Constraint('==', $this->getParser()->normalize(PluginInterface::PLUGIN_API_VERSION)); + $candidates = array_filter($candidates, function ($pkg) use ($phpConstraint, $composerPluginConstraint, $composerRuntimeConstraint) { $reqs = $pkg->getRequires(); - return !isset($reqs['php']) || $reqs['php']->getConstraint()->matches($phpConstraint); + return (!isset($reqs['php']) || $reqs['php']->getConstraint()->matches($phpConstraint)) + && (!isset($reqs['composer-plugin-api']) || $reqs['composer-plugin-api']->getConstraint()->matches($composerPluginConstraint)) + && (!isset($reqs['composer-runtime-api']) || $reqs['composer-runtime-api']->getConstraint()->matches($composerRuntimeConstraint)); }); } From 21aeef13655c1d87a7970cf53b6f46a43c6e0c2a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 14:30:17 +0200 Subject: [PATCH 100/193] Allow php 8 tests to run, require latest semver dev --- .github/workflows/continuous-integration.yml | 6 ++- composer.json | 2 +- composer.lock | 44 +++++++++++++------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 97e2b6558..d1e1d1b1b 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -56,7 +56,7 @@ jobs: dependencies: locked experimental: false - php-version: 8.0 - dependencies: highest + dependencies: highest-ignore os: ubuntu-latest experimental: true @@ -87,6 +87,10 @@ jobs: if: "matrix.dependencies == 'highest'" run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }}" + - name: "Install highest dependencies from composer.json using composer binary provided by system" + if: "matrix.dependencies == 'highest-ignore'" + run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }} --ignore-platform-reqs" + - name: "Install lowest dependencies from composer.json using composer binary provided by system" if: "matrix.dependencies == 'lowest'" run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest" diff --git a/composer.json b/composer.json index a4606d71a..bd2e3e465 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require": { "php": "^5.3.2 || ^7.0", "composer/ca-bundle": "^1.0", - "composer/semver": "^2.0", + "composer/semver": "^2.0@dev", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", diff --git a/composer.lock b/composer.lock index 914dd0798..514ab24e0 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ded9d158cb184fe1c92fa05610786bb5", + "content-hash": "08293668540de913642a19e6af165dd0", "packages": [ { "name": "composer/ca-bundle", @@ -79,20 +79,20 @@ }, { "name": "composer/semver", - "version": "2.0.0", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "0ec124f57c7e23925c006cbad0de853e3aec3ba2" + "reference": "538b6696308ee4ebcdedbd41b1a68f4e67cc8191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/0ec124f57c7e23925c006cbad0de853e3aec3ba2", - "reference": "0ec124f57c7e23925c006cbad0de853e3aec3ba2", + "url": "https://api.github.com/repos/composer/semver/zipball/538b6696308ee4ebcdedbd41b1a68f4e67cc8191", + "reference": "538b6696308ee4ebcdedbd41b1a68f4e67cc8191", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { "phpstan/phpstan": "^0.12.19", @@ -140,7 +140,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/2.0.0" + "source": "https://github.com/composer/semver/tree/master" }, "funding": [ { @@ -152,7 +152,7 @@ "type": "tidelift" } ], - "time": "2020-04-21T13:19:12+00:00" + "time": "2020-05-01T15:06:13+00:00" }, { "name": "composer/spdx-licenses", @@ -444,20 +444,20 @@ }, { "name": "seld/jsonlint", - "version": "1.7.2", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", + "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" @@ -491,9 +491,19 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.7.2" + "source": "https://github.com/Seldaek/jsonlint/tree/master" }, - "time": "2019-10-24T14:27:39+00:00" + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2020-04-30T19:05:18+00:00" }, { "name": "seld/phar-utils", @@ -1492,7 +1502,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "composer/semver": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From e5a5a9175d5e520abbeca84fecde792d6b75a2e3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 20:16:56 +0200 Subject: [PATCH 101/193] Fix php8 build bootstrap --- .github/workflows/continuous-integration.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d1e1d1b1b..462dd7e5e 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -87,7 +87,7 @@ jobs: if: "matrix.dependencies == 'highest'" run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }}" - - name: "Install highest dependencies from composer.json using composer binary provided by system" + - name: "Install highest dependencies from composer.json using composer binary provided by system, ignoring platform requirements" if: "matrix.dependencies == 'highest-ignore'" run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }} --ignore-platform-reqs" @@ -100,6 +100,7 @@ jobs: run: "composer install ${{ env.COMPOSER_FLAGS }}" - name: "Run install again using composer binary from source" + if: "matrix.dependencies != 'highest-ignore'" run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" - name: "Validate composer.json" From 419567ba6db91e8cf15f78ba0690d5e4b6668c0e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 21:12:21 +0200 Subject: [PATCH 102/193] Update VersionSelector to take all platform requirements into account when selecting packages --- UPGRADE-2.0.md | 1 + .../Package/Version/VersionSelector.php | 47 ++++++++--- .../Package/Version/VersionSelectorTest.php | 80 ++++++++++++++++--- 3 files changed, 105 insertions(+), 23 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index c8bcedf70..22486cae0 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -27,6 +27,7 @@ - packages now contain an `"installed-path"` key which lists where they were installed - there is a top level `"dev"` key which stores whether dev requirements were installed or not - `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overridden by listeners anymore +- `VersionSelector::findBestCandidate`'s third argument (phpVersion) was removed in favor of passing in a complete PlatformRepository instance into the constructor - `IOInterface` now extends PSR-3's `LoggerInterface`, and has new `writeRaw` + `writeErrorRaw` methods - `RepositoryInterface` changes: - A new `loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags)` function was added for use during pool building diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 9f331dffe..82d60dce7 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -20,6 +20,7 @@ use Composer\Composer; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; use Composer\Repository\RepositorySet; +use Composer\Repository\PlatformRepository; use Composer\Semver\Constraint\Constraint; /** @@ -32,11 +33,22 @@ class VersionSelector { private $repositorySet; + private $platformConstraints; + private $parser; - public function __construct(RepositorySet $repositorySet) + /** + * @param PlatformRepository $platformRepo If passed in, the versions found will be filtered against their requirements to eliminate any not matching the current platform packages + */ + public function __construct(RepositorySet $repositorySet, PlatformRepository $platformRepo = null) { $this->repositorySet = $repositorySet; + if ($platformRepo) { + $this->platformConstraints = array(); + foreach ($platformRepo->getPackages() as $package) { + $this->platformConstraints[$package->getName()][] = new Constraint('==', $package->getVersion()); + } + } } /** @@ -45,25 +57,38 @@ class VersionSelector * * @param string $packageName * @param string $targetPackageVersion - * @param string $targetPhpVersion * @param string $preferredStability + * @param bool $ignorePlatformReqs * @return PackageInterface|false */ - public function findBestCandidate($packageName, $targetPackageVersion = null, $targetPhpVersion = null, $preferredStability = 'stable') + public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $ignorePlatformReqs = false) { + if (!isset(BasePackage::$stabilities[$preferredStability])) { + // If you get this, maybe you are still relying on the Composer 1.x signature where the 3rd arg was the php version + throw new \UnexpectedValueException('Expected a valid stability name as 3rd argument, got '.$preferredStability); + } + $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null; $candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint); - if ($targetPhpVersion) { - $phpConstraint = new Constraint('==', $this->getParser()->normalize($targetPhpVersion)); - $composerRuntimeConstraint = new Constraint('==', $this->getParser()->normalize(Composer::RUNTIME_API_VERSION)); - $composerPluginConstraint = new Constraint('==', $this->getParser()->normalize(PluginInterface::PLUGIN_API_VERSION)); - $candidates = array_filter($candidates, function ($pkg) use ($phpConstraint, $composerPluginConstraint, $composerRuntimeConstraint) { + if ($this->platformConstraints && !$ignorePlatformReqs) { + $platformConstraints = $this->platformConstraints; + $candidates = array_filter($candidates, function ($pkg) use ($platformConstraints) { $reqs = $pkg->getRequires(); - return (!isset($reqs['php']) || $reqs['php']->getConstraint()->matches($phpConstraint)) - && (!isset($reqs['composer-plugin-api']) || $reqs['composer-plugin-api']->getConstraint()->matches($composerPluginConstraint)) - && (!isset($reqs['composer-runtime-api']) || $reqs['composer-runtime-api']->getConstraint()->matches($composerRuntimeConstraint)); + foreach ($reqs as $name => $link) { + if (isset($platformConstraints[$name])) { + foreach ($platformConstraints[$name] as $constraint) { + if ($link->getConstraint()->matches($constraint)) { + continue 2; + } + } + + return false; + } + } + + return true; }); } diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index 66d07d267..14d00b438 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -15,6 +15,7 @@ namespace Composer\Test\Package\Version; use Composer\Package\Version\VersionSelector; use Composer\Package\Package; use Composer\Package\Link; +use Composer\Repository\PlatformRepository; use Composer\Semver\VersionParser; use Composer\Test\TestCase; @@ -46,27 +47,82 @@ class VersionSelectorTest extends TestCase $this->assertSame($package2, $best, 'Latest version should be 1.2.2'); } - public function testLatestVersionIsReturnedThatMatchesPhpRequirement() + public function testLatestVersionIsReturnedThatMatchesPhpRequirements() { $packageName = 'foobar'; + $platform = new PlatformRepository(array(), array('php' => '5.5.0')); + $repositorySet = $this->createMockRepositorySet(); + $versionSelector = new VersionSelector($repositorySet, $platform); + $parser = new VersionParser; $package1 = $this->createPackage('1.0.0'); - $package2 = $this->createPackage('2.0.0'); $package1->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.4'), 'requires', '>=5.4'))); + $package2 = $this->createPackage('2.0.0'); $package2->setRequires(array('php' => new Link($packageName, 'php', $parser->parseConstraints('>=5.6'), 'requires', '>=5.6'))); $packages = array($package1, $package2); - $repositorySet = $this->createMockRepositorySet(); - $repositorySet->expects($this->once()) + $repositorySet->expects($this->any()) ->method('findPackages') ->with($packageName, null) ->will($this->returnValue($packages)); - $versionSelector = new VersionSelector($repositorySet); - $best = $versionSelector->findBestCandidate($packageName, null, '5.5.0'); - + $best = $versionSelector->findBestCandidate($packageName); $this->assertSame($package1, $best, 'Latest version supporting php 5.5 should be returned (1.0.0)'); + $best = $versionSelector->findBestCandidate($packageName, null, 'stable', true); + $this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (2.0.0)'); + } + + public function testLatestVersionIsReturnedThatMatchesExtRequirements() + { + $packageName = 'foobar'; + + $platform = new PlatformRepository(array(), array('ext-zip' => '5.3.0')); + $repositorySet = $this->createMockRepositorySet(); + $versionSelector = new VersionSelector($repositorySet, $platform); + + $parser = new VersionParser; + $package1 = $this->createPackage('1.0.0'); + $package1->setRequires(array('ext-zip' => new Link($packageName, 'ext-zip', $parser->parseConstraints('^5.2'), 'requires', '^5.2'))); + $package2 = $this->createPackage('2.0.0'); + $package2->setRequires(array('ext-zip' => new Link($packageName, 'ext-zip', $parser->parseConstraints('^5.4'), 'requires', '^5.4'))); + $packages = array($package1, $package2); + + $repositorySet->expects($this->any()) + ->method('findPackages') + ->with($packageName, null) + ->will($this->returnValue($packages)); + + $best = $versionSelector->findBestCandidate($packageName); + $this->assertSame($package1, $best, 'Latest version supporting ext-zip 5.3.0 should be returned (1.0.0)'); + $best = $versionSelector->findBestCandidate($packageName, null, 'stable', true); + $this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (2.0.0)'); + } + + public function testLatestVersionIsReturnedThatMatchesComposerRequirements() + { + $packageName = 'foobar'; + + $platform = new PlatformRepository(array(), array('composer-runtime-api' => '1.0.0')); + $repositorySet = $this->createMockRepositorySet(); + $versionSelector = new VersionSelector($repositorySet, $platform); + + $parser = new VersionParser; + $package1 = $this->createPackage('1.0.0'); + $package1->setRequires(array('composer-runtime-api' => new Link($packageName, 'composer-runtime-api', $parser->parseConstraints('^1.0'), 'requires', '^1.0'))); + $package2 = $this->createPackage('1.1.0'); + $package2->setRequires(array('composer-runtime-api' => new Link($packageName, 'composer-runtime-api', $parser->parseConstraints('^2.0'), 'requires', '^2.0'))); + $packages = array($package1, $package2); + + $repositorySet->expects($this->any()) + ->method('findPackages') + ->with($packageName, null) + ->will($this->returnValue($packages)); + + $best = $versionSelector->findBestCandidate($packageName); + $this->assertSame($package1, $best, 'Latest version supporting composer 1 should be returned (1.0.0)'); + $best = $versionSelector->findBestCandidate($packageName, null, 'stable', true); + $this->assertSame($package2, $best, 'Latest version should be returned when ignoring platform reqs (1.1.0)'); } public function testMostStableVersionIsReturned() @@ -109,10 +165,10 @@ class VersionSelectorTest extends TestCase ->will($this->returnValue(array_reverse($packages))); $versionSelector = new VersionSelector($repositorySet); - $best = $versionSelector->findBestCandidate($packageName, null, null); + $best = $versionSelector->findBestCandidate($packageName); $this->assertSame($package2, $best, 'Expecting 2.0.0-beta3, cause beta is more stable than dev'); - $best = $versionSelector->findBestCandidate($packageName, null, null); + $best = $versionSelector->findBestCandidate($packageName); $this->assertSame($package2, $best, 'Expecting 2.0.0-beta3, cause beta is more stable than dev'); } @@ -131,7 +187,7 @@ class VersionSelectorTest extends TestCase ->will($this->returnValue($packages)); $versionSelector = new VersionSelector($repositorySet); - $best = $versionSelector->findBestCandidate($packageName, null, null, 'dev'); + $best = $versionSelector->findBestCandidate($packageName, null, 'dev'); $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)'); } @@ -152,7 +208,7 @@ class VersionSelectorTest extends TestCase ->will($this->returnValue($packages)); $versionSelector = new VersionSelector($repositorySet); - $best = $versionSelector->findBestCandidate($packageName, null, null, 'beta'); + $best = $versionSelector->findBestCandidate($packageName, null, 'beta'); $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)'); } @@ -172,7 +228,7 @@ class VersionSelectorTest extends TestCase ->will($this->returnValue($packages)); $versionSelector = new VersionSelector($repositorySet); - $best = $versionSelector->findBestCandidate($packageName, null, null, 'stable'); + $best = $versionSelector->findBestCandidate($packageName, null, 'stable'); $this->assertSame($package2, $best, 'Latest version should be returned (1.1.0-beta)'); } From 8a2dd1baefdc87cb6cab69eda6938cec748a825c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 21:36:10 +0200 Subject: [PATCH 103/193] Adjust all usages of VersionSelector to the new API --- UPGRADE-2.0.md | 1 + src/Composer/Command/CreateProjectCommand.php | 15 ++--- src/Composer/Command/InitCommand.php | 56 ++++++++++--------- src/Composer/Command/RequireCommand.php | 13 ++++- src/Composer/Command/ShowCommand.php | 19 +++---- 5 files changed, 56 insertions(+), 48 deletions(-) diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index 22486cae0..ba9fa3615 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -28,6 +28,7 @@ - there is a top level `"dev"` key which stores whether dev requirements were installed or not - `PreFileDownloadEvent` now receives an `HttpDownloader` instance instead of `RemoteFilesystem`, and that instance can not be overridden by listeners anymore - `VersionSelector::findBestCandidate`'s third argument (phpVersion) was removed in favor of passing in a complete PlatformRepository instance into the constructor +- `InitCommand::determineRequirements`'s fourth argument (phpVersion) should now receive a complete PlatformRepository instance or null if platform requirements are to be ignored - `IOInterface` now extends PSR-3's `LoggerInterface`, and has new `writeRaw` + `writeErrorRaw` methods - `RepositoryInterface` changes: - A new `loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags)` function was added for use during pool building diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 9263ba7ee..2b3c7b20d 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -336,25 +336,20 @@ EOT $repositorySet = new RepositorySet($stability); $repositorySet->addRepository($sourceRepo); - $phpVersion = null; - $prettyPhpVersion = null; + $platformRepo = null; if (!$ignorePlatformReqs) { $platformOverrides = $config->get('platform') ?: array(); - // initialize $this->repos as it is used by the parent InitCommand - $platform = new PlatformRepository(array(), $platformOverrides); - $phpPackage = $platform->findPackage('php', '*'); - $phpVersion = $phpPackage->getVersion(); - $prettyPhpVersion = $phpPackage->getPrettyVersion(); + $platformRepo = new PlatformRepository(array(), $platformOverrides); } // find the latest version if there are multiple - $versionSelector = new VersionSelector($repositorySet); + $versionSelector = new VersionSelector($repositorySet, $platformRepo); $package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability); if (!$package) { $errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability"); - if ($phpVersion && $versionSelector->findBestCandidate($name, $packageVersion, null, $stability)) { - throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version '.$prettyPhpVersion.'.'); + if ($platformRepo && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) { + throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version, PHP extensions and Composer version.'); } throw new \InvalidArgumentException($errorMessage .'.'); diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 8f101515c..ecbbbf1e0 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -345,13 +345,21 @@ EOT // prepare to resolve dependencies $repos = $this->getRepos(); $preferredStability = $minimumStability ?: 'stable'; - $phpVersion = $repos->findPackage('php', '*')->getPrettyVersion(); + $platformRepo = null; + if ($repos instanceof CompositeRepository) { + foreach ($repos->getRepositories() as $candidateRepo) { + if ($candidateRepo instanceof PlatformRepository) { + $platformRepo = $candidateRepo; + break; + } + } + } $question = 'Would you like to define your dependencies (require) interactively [yes]? '; $require = $input->getOption('require'); $requirements = array(); if ($require || $io->askConfirmation($question, true)) { - $requirements = $this->determineRequirements($input, $output, $require, $phpVersion, $preferredStability); + $requirements = $this->determineRequirements($input, $output, $require, $platformRepo, $preferredStability); } $input->setOption('require', $requirements); @@ -359,7 +367,7 @@ EOT $requireDev = $input->getOption('require-dev'); $devRequirements = array(); if ($requireDev || $io->askConfirmation($question, true)) { - $devRequirements = $this->determineRequirements($input, $output, $requireDev, $phpVersion, $preferredStability); + $devRequirements = $this->determineRequirements($input, $output, $requireDev, $platformRepo, $preferredStability); } $input->setOption('require-dev', $devRequirements); } @@ -403,7 +411,7 @@ EOT return $this->repos; } - final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false) + final protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), PlatformRepository $platformRepo = null, $preferredStability = 'stable', $checkProvidedVersions = true, $fixed = false) { if ($requires) { $requires = $this->normalizeRequirements($requires); @@ -413,7 +421,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, null, null, $fixed); + list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, null, null, $fixed); $requirement['version'] = $version; // replace package name from packagist.org @@ -426,7 +434,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', $fixed); + list($name, $version) = $this->findBestVersionAndNameForPackage($input, $requirement['name'], $platformRepo, $preferredStability, $checkProvidedVersions ? $requirement['version'] : null, 'dev', $fixed); // replace package name from packagist.org $requirement['name'] = $name; @@ -550,7 +558,7 @@ EOT ); if (false === $constraint) { - list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $phpVersion, $preferredStability); + list($name, $constraint) = $this->findBestVersionAndNameForPackage($input, $package, $platformRepo, $preferredStability); $io->writeError(sprintf( 'Using version %s for %s', @@ -723,7 +731,7 @@ EOT * * @param InputInterface $input * @param string $name - * @param string|null $phpVersion + * @param PlatformRepository|null $platformRepo * @param string $preferredStability * @param string|null $requiredVersion * @param string $minimumStability @@ -731,18 +739,18 @@ EOT * @throws \InvalidArgumentException * @return array name version */ - private function findBestVersionAndNameForPackage(InputInterface $input, $name, $phpVersion, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) + private function findBestVersionAndNameForPackage(InputInterface $input, $name, PlatformRepository $platformRepo = null, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) { - // find the latest version allowed in this repo set - $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability)); + // ignore platform repo if platform requirements are ignored $ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs'); - - // ignore phpVersion if platform requirements are ignored if ($ignorePlatformReqs) { - $phpVersion = null; + $platformRepo = null; } - $package = $versionSelector->findBestCandidate($name, $requiredVersion, $phpVersion, $preferredStability); + // find the latest version allowed in this repo set + $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo); + + $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability); if (!$package) { // platform packages can not be found in the pool in versions other than the local platform's has @@ -752,28 +760,26 @@ EOT } // Check whether the PHP version was the problem - if ($phpVersion && $versionSelector->findBestCandidate($name, $requiredVersion, null, $preferredStability)) { + if ($platformRepo && $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true)) { throw new \InvalidArgumentException(sprintf( - 'Package %s at version %s has a PHP requirement incompatible with your PHP version (%s)', + 'Package %s at version %s has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version', $name, - $requiredVersion, - $phpVersion + $requiredVersion )); } // Check whether the required version was the problem - if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $phpVersion, $preferredStability)) { + if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $preferredStability)) { throw new \InvalidArgumentException(sprintf( 'Could not find package %s in a version matching %s', $name, $requiredVersion )); } - // Check whether the PHP version was the problem - if ($phpVersion && $versionSelector->findBestCandidate($name)) { + // Check whether the PHP version was the problem for all versions + if ($platformRepo && $versionSelector->findBestCandidate($name, null, $preferredStability, true)) { throw new \InvalidArgumentException(sprintf( - 'Could not find package %s in any version matching your PHP version (%s)', - $name, - $phpVersion + 'Could not find package %s in any version matching your PHP version, PHP extensions and Composer version', + $name )); } diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 7e290aca6..4bc08905b 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -166,7 +166,7 @@ EOT $platformOverrides = $composer->getConfig()->get('platform') ?: array(); // initialize $this->repos as it is used by the parent InitCommand $this->repos = new CompositeRepository(array_merge( - array(new PlatformRepository(array(), $platformOverrides)), + array($platformRepo = new PlatformRepository(array(), $platformOverrides)), $repos )); @@ -176,9 +176,16 @@ EOT $preferredStability = $composer->getPackage()->getMinimumStability(); } - $phpVersion = $this->repos->findPackage('php', '*')->getPrettyVersion(); try { - $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed')); + $requirements = $this->determineRequirements( + $input, + $output, + $input->getArgument('packages'), + $input->getOption('ignore-platform-reqs') ? null : $platformRepo, + $preferredStability, + !$input->getOption('no-update'), + $input->getOption('fixed') + ); } 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); diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index b153c408c..b928765f9 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -151,7 +151,6 @@ EOT } $platformRepo = new PlatformRepository(array(), $platformOverrides); $lockedRepo = null; - $phpVersion = $platformRepo->findPackage('php', '*')->getVersion(); if ($input->getOption('self')) { $package = $this->getComposer()->getPackage(); @@ -249,7 +248,7 @@ EOT } else { $latestPackage = null; if ($input->getOption('latest')) { - $latestPackage = $this->findLatestPackage($package, $composer, $phpVersion); + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo); } if ($input->getOption('outdated') && $input->getOption('strict') && $latestPackage && $latestPackage->getFullPrettyVersion() !== $package->getFullPrettyVersion() && !$latestPackage->isAbandoned()) { $exitCode = 1; @@ -382,7 +381,7 @@ EOT if ($showLatest && $showVersion) { foreach ($packages[$type] as $package) { if (is_object($package)) { - $latestPackage = $this->findLatestPackage($package, $composer, $phpVersion, $showMinorOnly); + $latestPackage = $this->findLatestPackage($package, $composer, $platformRepo, $showMinorOnly); if ($latestPackage === false) { continue; } @@ -1180,18 +1179,18 @@ EOT /** * Given a package, this finds the latest package matching it * - * @param PackageInterface $package - * @param Composer $composer - * @param string $phpVersion - * @param bool $minorOnly + * @param PackageInterface $package + * @param Composer $composer + * @param PlatformRepository $platformRepo + * @param bool $minorOnly * * @return PackageInterface|false */ - private function findLatestPackage(PackageInterface $package, Composer $composer, $phpVersion, $minorOnly = false) + private function findLatestPackage(PackageInterface $package, Composer $composer, PlatformRepository $platformRepo, $minorOnly = false) { // find the latest version allowed in this repo set $name = $package->getName(); - $versionSelector = new VersionSelector($this->getRepositorySet($composer)); + $versionSelector = new VersionSelector($this->getRepositorySet($composer), $platformRepo); $stability = $composer->getPackage()->getMinimumStability(); $flags = $composer->getPackage()->getStabilityFlags(); if (isset($flags[$name])) { @@ -1212,7 +1211,7 @@ EOT $targetVersion = '^' . $package->getVersion(); } - return $versionSelector->findBestCandidate($name, $targetVersion, $phpVersion, $bestStability); + return $versionSelector->findBestCandidate($name, $targetVersion, $bestStability); } private function getRepositorySet(Composer $composer) From ef3797cdd686a854e8a6dea262aa795535843b0b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 21:58:33 +0200 Subject: [PATCH 104/193] Cache successful requests to make sure subsequent loadPackages calls do not do the same requests for nothing --- src/Composer/Repository/ComposerRepository.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 5f68de635..34584e2db 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -70,6 +70,14 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito private $hasPartialPackages; private $partialPackagesByName; + /** + * TODO v3 should make this private once we can drop PHP 5.3 support + * @private + * @var array list of package names which are fresh and can be loaded from the cache directly in case loadPackage is called several times + * useful for v2 metadata repositories with lazy providers + */ + public $freshMetadataUrls = array(); + /** * TODO v3 should make this private once we can drop PHP 5.3 support * @private @@ -1150,6 +1158,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return new Promise(function ($resolve, $reject) { $resolve(array('packages' => array())); }); } + if (isset($this->freshMetadataUrls[$filename]) && $lastModifiedTime) { + // make it look like we got a 304 response + return new Promise(function ($resolve, $reject) { $resolve(true); }); + } + $httpDownloader = $this->httpDownloader; if ($this->eventDispatcher) { $preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename); @@ -1173,6 +1186,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $json = $response->getBody(); if ($json === '' && $response->getStatusCode() === 304) { + $repo->freshMetadataUrls[$filename] = true; return true; } @@ -1186,6 +1200,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $json = JsonFile::encode($data, JsonFile::JSON_UNESCAPED_SLASHES | JsonFile::JSON_UNESCAPED_UNICODE); } $cache->write($cacheKey, $json); + $repo->freshMetadataUrls[$filename] = true; return $data; }; From 1d68e85433c50d84c166ec964b23679b049b5d0c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 4 May 2020 22:04:57 +0200 Subject: [PATCH 105/193] Fix usage --- src/Composer/Command/CreateProjectCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 2b3c7b20d..e79ed8a7d 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -344,7 +344,7 @@ EOT // find the latest version if there are multiple $versionSelector = new VersionSelector($repositorySet, $platformRepo); - $package = $versionSelector->findBestCandidate($name, $packageVersion, $phpVersion, $stability); + $package = $versionSelector->findBestCandidate($name, $packageVersion, $stability); if (!$package) { $errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability"); From ebefcdbd7e994748a155cbb40c5f954c5297e545 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 10:00:50 +0200 Subject: [PATCH 106/193] Update to latest semver, refs #8783 --- composer.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.lock b/composer.lock index 514ab24e0..c4c5a6c2f 100644 --- a/composer.lock +++ b/composer.lock @@ -83,12 +83,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "538b6696308ee4ebcdedbd41b1a68f4e67cc8191" + "reference": "1f88d7a8a0a3d71facc60599e87c6b3107893e90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/538b6696308ee4ebcdedbd41b1a68f4e67cc8191", - "reference": "538b6696308ee4ebcdedbd41b1a68f4e67cc8191", + "url": "https://api.github.com/repos/composer/semver/zipball/1f88d7a8a0a3d71facc60599e87c6b3107893e90", + "reference": "1f88d7a8a0a3d71facc60599e87c6b3107893e90", "shasum": "" }, "require": { @@ -152,7 +152,7 @@ "type": "tidelift" } ], - "time": "2020-05-01T15:06:13+00:00" + "time": "2020-05-06T07:58:50+00:00" }, { "name": "composer/spdx-licenses", From 4ce492d01d3e4c839d03a97319c5046077a4e7bf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 10:18:53 +0200 Subject: [PATCH 107/193] Fix detection of gitlab URLs, fixes #8402, closes #8868 --- src/Composer/Util/Git.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 27bfe766f..70f62bde2 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -165,7 +165,7 @@ class Git $errorMsg = $this->process->getErrorOutput(); } } elseif ( - preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) + preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?\.git)$}i', $url, $match) || preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}', $url, $match) ) { if ($match[1] === 'git') { From 73b9a3960f2942f60f12cffc55cfc17bd2673c60 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 10:21:58 +0200 Subject: [PATCH 108/193] Fix 5.3 build --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 97b271de8..e70058719 100644 --- a/composer.json +++ b/composer.json @@ -38,6 +38,7 @@ }, "conflict": { "symfony/console": "2.8.38" + "symfony/phpunit-bridge": "3.4.40" }, "require-dev": { "symfony/phpunit-bridge": "^3.4", From 7d87688032740a7081d41390b518a3183e84fd3e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 10:22:44 +0200 Subject: [PATCH 109/193] Fix 5.3 build --- composer.json | 2 +- composer.lock | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/composer.json b/composer.json index e70058719..e588cb71e 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "conflict": { - "symfony/console": "2.8.38" + "symfony/console": "2.8.38", "symfony/phpunit-bridge": "3.4.40" }, "require-dev": { diff --git a/composer.lock b/composer.lock index abe4aafea..aa67fc010 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cc6f9640996dfad00a5b03a8be01a571", + "content-hash": "cb76dc8e228d462d248c09f0051da364", "packages": [ { "name": "composer/ca-bundle", @@ -365,20 +365,20 @@ }, { "name": "seld/jsonlint", - "version": "1.7.2", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19" + "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19", - "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", + "reference": "ff2aa5420bfbc296cf6a0bc785fa5b35736de7c1", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0" + "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" @@ -412,9 +412,19 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.7.2" + "source": "https://github.com/Seldaek/jsonlint/tree/master" }, - "time": "2019-10-24T14:27:39+00:00" + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2020-04-30T19:05:18+00:00" }, { "name": "seld/phar-utils", @@ -946,6 +956,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/master" + }, "time": "2015-06-14T21:17:01+00:00" }, { @@ -995,6 +1009,10 @@ "email": "mike.vanriel@naenius.com" } ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x" + }, "time": "2016-01-25T08:17:30+00:00" }, { @@ -1126,6 +1144,10 @@ "compare", "equality" ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "source": "https://github.com/sebastianbergmann/comparator/tree/1.2" + }, "time": "2017-01-29T09:50:25+00:00" }, { @@ -1178,6 +1200,10 @@ "keywords": [ "diff" ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "source": "https://github.com/sebastianbergmann/diff/tree/1.4" + }, "time": "2017-05-22T07:24:03+00:00" }, { @@ -1245,6 +1271,10 @@ "export", "exporter" ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "source": "https://github.com/sebastianbergmann/exporter/tree/master" + }, "time": "2016-11-19T08:54:04+00:00" }, { @@ -1298,6 +1328,10 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/master" + }, "time": "2016-11-19T07:33:16+00:00" }, { From ff27fdf4eaafc1d0cd201f725bf70ced870bbbae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 10:25:25 +0200 Subject: [PATCH 110/193] Update changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d41be6a1..ab1d3bba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +### [1.10.6] 2020-05-06 + + * Fixed version guessing to take composer-runtime-api and composer-plugin-api requirements into account to avoid selecting packages which require Composer 2 + * Fixed package name validation to allow several dashes following each other + * Fixed post-status-cmd script not firing when there were no changes to be displayed + * Fixed composer-runtime-api support on Composer 1.x, the package is now present as 1.0.0 + * Fixed support for composer show --name-only --self + * Fixed detection of GitLab URLs when handling authentication in some cases + ### [1.10.5] 2020-04-10 * Fixed self-update on PHP <5.6, seriously please upgrade people, it's time @@ -846,6 +855,7 @@ * Initial release +[1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6 [1.10.5]: https://github.com/composer/composer/compare/1.10.4...1.10.5 [1.10.4]: https://github.com/composer/composer/compare/1.10.3...1.10.4 [1.10.3]: https://github.com/composer/composer/compare/1.10.2...1.10.3 From 592b05df117a39b9b574f1833ebf1bcc29a38692 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 11:12:25 +0200 Subject: [PATCH 111/193] Add one more use case to InstalledVersions docs --- doc/07-runtime.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/07-runtime.md b/doc/07-runtime.md index 35d4276fb..4faef5e07 100644 --- a/doc/07-runtime.md +++ b/doc/07-runtime.md @@ -72,6 +72,20 @@ package. \Composer\InstalledVersions::getReference('vendor/package'); ``` +### Knowing a package's own installed version + +If you are only interested in getting a package's own version, e.g. in the source of acme/foo you want +to know which version acme/foo is currently running to display that to the user, then it is +acceptable to use getVersion/getPrettyVersion/getReference. + +The warning in the section above does not apply in this case as you are sure the package is present +and not being replaced if your code is running. + +It is nonetheless a good idea to make sure you handle the `null` return value as gracefully as +possible for safety. + +---- + A few other methods are available for more complex usages, please refer to the source/docblocks of the class itself. From 9c0f7d4bcede7c12620063cd6c0c43a0b3e85cf5 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 11:25:57 +0200 Subject: [PATCH 112/193] Try fixing php8 build --- .github/workflows/continuous-integration.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 462dd7e5e..825574f84 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -114,4 +114,9 @@ jobs: run: "echo \"::set-env name=SYMFONY_PHPUNIT_VERSION::7.5\"" - name: "Run tests" + if: "matrix.php-version != '8.0'" run: "vendor/bin/simple-phpunit" + + - name: "Run tests for PHP 8" + if: "matrix.php-version == '8.0'" + run: "bin/composer remove --dev symfony/phpunit-bridge && bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs" From b7dd5b068019df3eb7213214cf06ff5dbb0e5130 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 11:38:48 +0200 Subject: [PATCH 113/193] PHP8 again --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 825574f84..122d09d63 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -119,4 +119,4 @@ jobs: - name: "Run tests for PHP 8" if: "matrix.php-version == '8.0'" - run: "bin/composer remove --dev symfony/phpunit-bridge && bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs" + run: "bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs && bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs" From 4b1e386fffe98d113a1e65b561dcb286dfaba239 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:23:38 +0200 Subject: [PATCH 114/193] Make sure php8 runs tests, and that 7.3 runs complete test suite --- .github/workflows/continuous-integration.yml | 8 ++++++-- tests/complete.phpunit.xml | 11 +++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 122d09d63..022fc7fa0 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -114,9 +114,13 @@ jobs: run: "echo \"::set-env name=SYMFONY_PHPUNIT_VERSION::7.5\"" - name: "Run tests" - if: "matrix.php-version != '8.0'" + if: "matrix.php-version != '8.0' && matrix.php-version != '7.3'" run: "vendor/bin/simple-phpunit" + - name: "Run Complete test suite" + if: "matrix.php-version == '7.3'" + run: "vendor/bin/simple-phpunit --configuration tests/complete.phpunit.xml" + - name: "Run tests for PHP 8" if: "matrix.php-version == '8.0'" - run: "bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs && bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs" + run: "bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs && bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs && vendor/bin/phpunit" diff --git a/tests/complete.phpunit.xml b/tests/complete.phpunit.xml index 0a3f29f06..30faf6837 100644 --- a/tests/complete.phpunit.xml +++ b/tests/complete.phpunit.xml @@ -1,16 +1,23 @@ - + + + ./Composer/ From 22622372bc5e43d8838be7b9c5f26ef4c2531c1d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:23:52 +0200 Subject: [PATCH 115/193] Fix complete test suite for v2 --- tests/Composer/Test/Repository/VcsRepositoryTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Composer/Test/Repository/VcsRepositoryTest.php b/tests/Composer/Test/Repository/VcsRepositoryTest.php index 65bf52409..0333ffd69 100644 --- a/tests/Composer/Test/Repository/VcsRepositoryTest.php +++ b/tests/Composer/Test/Repository/VcsRepositoryTest.php @@ -144,6 +144,7 @@ class VcsRepositoryTest extends TestCase 'dev-feature-b' => true, 'dev-feature/a-1.0-B' => true, 'dev-master' => true, + '9999999-dev' => true, // alias of dev-master ); $config = new Config(); From a8cd2941e5b27f99721a376937ca48cab6df7403 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:32:13 +0200 Subject: [PATCH 116/193] Fix phar compilation test --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 022fc7fa0..1fef80808 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -69,7 +69,7 @@ jobs: with: coverage: "none" extensions: "intl" - ini-values: "memory_limit=-1" + ini-values: "memory_limit=-1, phar.readonly=0" php-version: "${{ matrix.php-version }}" - name: "Determine composer cache directory" From cb2fca5d131c411a71fade8af7be04786953e625 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:34:06 +0200 Subject: [PATCH 117/193] Try to workaround react/promise php8 issue --- .github/workflows/continuous-integration.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 1fef80808..675c613e9 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -123,4 +123,8 @@ jobs: - name: "Run tests for PHP 8" if: "matrix.php-version == '8.0'" - run: "bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs && bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs && vendor/bin/phpunit" + run: | + rm -rf vendor/symfony/phpunit-bridge + bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs + bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs + vendor/bin/phpunit From 88ced66f2c4a91e5ec5901b021bc340e5f39ce7b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:38:05 +0200 Subject: [PATCH 118/193] Fix issue with PHPUnitBridge autoload --- .github/workflows/continuous-integration.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 675c613e9..659e2f798 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -124,7 +124,8 @@ jobs: - name: "Run tests for PHP 8" if: "matrix.php-version == '8.0'" run: | + # hack to workaround issue with react/promise, normally should just call bin/composer remove and skip the rm rm -rf vendor/symfony/phpunit-bridge - bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs + composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs vendor/bin/phpunit From 1ef352751c8c68eb52a15d0f8529ccb6982fcabc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:45:31 +0200 Subject: [PATCH 119/193] Fix display issue in create-project when installing dev-master --- src/Composer/Command/CreateProjectCommand.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index e79ed8a7d..3bab66946 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -368,6 +368,11 @@ EOT } } + // avoid displaying 9999999-dev as version if dev-master was selected + if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEV_MASTER_ALIAS) { + $package = $package->getAliasOf(); + } + $io->writeError('Installing ' . $package->getName() . ' (' . $package->getFullPrettyVersion(false) . ')'); if ($disablePlugins) { From ec3f18ee923051e305d3cb87f9d8b6341f17fefc Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 6 May 2020 13:47:03 +0200 Subject: [PATCH 120/193] Remove workaround as it is not working anyway --- .github/workflows/continuous-integration.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 659e2f798..4b27be52b 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -124,8 +124,6 @@ jobs: - name: "Run tests for PHP 8" if: "matrix.php-version == '8.0'" run: | - # hack to workaround issue with react/promise, normally should just call bin/composer remove and skip the rm - rm -rf vendor/symfony/phpunit-bridge - composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs + bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs vendor/bin/phpunit From a074819a5193e6cee802947eb9ad873a2b5fe63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Km=C3=ADnek?= Date: Fri, 8 May 2020 17:43:07 +0200 Subject: [PATCH 121/193] Add support for gitlab deploy token (#8867) * feat: Added ability to work with GitLab deploy tokens: https://docs.gitlab.com/ee/user/project/deploy_tokens/ Deploy tokens can be specified two ways: 1) GIT CONFIG: git config --add gitlab.deploytoken.user USERNAME && git config --add gitlab.deploytoken.token TOKEN 2) Auth.json: "gitlab-token": { "gitlab.com": {"username": "USERNAME", "token": "TOKEN"} } --- doc/06-config.md | 8 ++++++-- src/Composer/IO/BaseIO.php | 4 +++- src/Composer/Util/GitLab.php | 23 +++++++++++++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/doc/06-config.md b/doc/06-config.md index 1b0a8e648..30f31b98e 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -85,9 +85,13 @@ gitlab.com the domain names must be also specified with the ## gitlab-token -A list of domain names and private tokens. For example using `{"gitlab.com": +A list of domain names and private tokens. Private token can be either simple +string, or array with username and token. For example using `{"gitlab.com": "privatetoken"}` as the value of this option will use `privatetoken` to access -private repositories on gitlab. Please note: If the package is not hosted at +private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser", + "token": "privatetoken"}}` will use both username and token for gitlab deploy +token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/) +Please note: If the package is not hosted at gitlab.com the domain names must be also specified with the [`gitlab-domains`](06-config.md#gitlab-domains) option. diff --git a/src/Composer/IO/BaseIO.php b/src/Composer/IO/BaseIO.php index e2d916a15..e3a263301 100644 --- a/src/Composer/IO/BaseIO.php +++ b/src/Composer/IO/BaseIO.php @@ -135,7 +135,9 @@ abstract class BaseIO implements IOInterface } foreach ($gitlabToken as $domain => $token) { - $this->checkAndSetAuthentication($domain, $token, 'private-token'); + $username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token; + $password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token'; + $this->checkAndSetAuthentication($domain, $username, $password); } // reload http basic credentials from config if available diff --git a/src/Composer/Util/GitLab.php b/src/Composer/Util/GitLab.php index fb2489b01..ea0c72477 100644 --- a/src/Composer/Util/GitLab.php +++ b/src/Composer/Util/GitLab.php @@ -71,17 +71,28 @@ class GitLab return true; } - // if available use token from composer config - $authTokens = $this->config->get('gitlab-token'); - - if (isset($authTokens[$originUrl])) { - $this->io->setAuthentication($originUrl, $authTokens[$originUrl], 'private-token'); + // if available use deploy token from git config + if (0 === $this->process->execute('git config gitlab.deploytoken.user', $tokenUser) && 0 === $this->process->execute('git config gitlab.deploytoken.token', $tokenPassword)) { + $this->io->setAuthentication($originUrl, trim($tokenUser), trim($tokenPassword)); return true; } + // if available use token from composer config + $authTokens = $this->config->get('gitlab-token'); + + if (isset($authTokens[$originUrl])) { + $token = $authTokens[$originUrl]; + } + if (isset($authTokens[$bcOriginUrl])) { - $this->io->setAuthentication($originUrl, $authTokens[$bcOriginUrl], 'private-token'); + $token = $authTokens[$bcOriginUrl]; + } + + if(isset($token)){ + $username = is_array($token) && array_key_exists("username", $token) ? $token["username"] : $token; + $password = is_array($token) && array_key_exists("token", $token) ? $token["token"] : 'private-token'; + $this->io->setAuthentication($originUrl, $username, $password); return true; } From 42fc372e5208522fc8bafafde916a455c53807d6 Mon Sep 17 00:00:00 2001 From: Jean-Michel DELEHAYE Date: Thu, 7 May 2020 10:14:37 +0200 Subject: [PATCH 122/193] Add function_exists() for 'pcntl_signal' --- src/Composer/Command/CreateProjectCommand.php | 2 +- src/Composer/Command/RequireCommand.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 1801583fd..067fb9a40 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -362,7 +362,7 @@ EOT } // handler Ctrl+C for unix-like systems - if (function_exists('pcntl_async_signals')) { + if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) { @mkdir($directory, 0777, true); if ($realDir = realpath($directory)) { pcntl_async_signals(true); diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index cbdfdaf9c..9b59e7feb 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -84,7 +84,7 @@ EOT protected function execute(InputInterface $input, OutputInterface $output) { - if (function_exists('pcntl_async_signals')) { + if (function_exists('pcntl_async_signals') && function_exists('pcntl_signal')) { pcntl_async_signals(true); pcntl_signal(SIGINT, array($this, 'revertComposerFile')); pcntl_signal(SIGTERM, array($this, 'revertComposerFile')); From b5f8f750c21f492a308b529bfc399363d7cf9d8e Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Sat, 9 May 2020 15:51:57 +0200 Subject: [PATCH 123/193] Test PHP 8.0 with lowest dependencies (#8878) --- .github/workflows/continuous-integration.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4b27be52b..72a1d9547 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -55,6 +55,10 @@ jobs: os: macos-latest dependencies: locked experimental: false + - php-version: 8.0 + dependencies: lowest + os: ubuntu-latest + experimental: true - php-version: 8.0 dependencies: highest-ignore os: ubuntu-latest @@ -125,5 +129,5 @@ jobs: if: "matrix.php-version == '8.0'" run: | bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs - bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs + bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs --with-dependencies vendor/bin/phpunit From 106289e5dc5b9c0fa459fba28a99ea888f5a51b6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 13 May 2020 08:56:37 +0200 Subject: [PATCH 124/193] Fix lowest 8 build to use ignore platform reqs --- .github/workflows/continuous-integration.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 72a1d9547..ffd1f3433 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -56,7 +56,7 @@ jobs: dependencies: locked experimental: false - php-version: 8.0 - dependencies: lowest + dependencies: lowest-ignore os: ubuntu-latest experimental: true - php-version: 8.0 @@ -99,6 +99,10 @@ jobs: if: "matrix.dependencies == 'lowest'" run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest" + - name: "Install lowest dependencies from composer.json using composer binary provided by system, ignoring platform requirements" + if: "matrix.dependencies == 'lowest-ignore'" + run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest --ignore-platform-reqs" + - name: "Install dependencies from composer.lock using composer binary provided by system" if: "matrix.dependencies == 'locked'" run: "composer install ${{ env.COMPOSER_FLAGS }}" From 98e5f81a5f3ac64ef6707c6dd48ee34a5e0afd1a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 12:17:25 +0200 Subject: [PATCH 125/193] Fix detection of fixed package problems to include more cases, fixes #8910 --- src/Composer/DependencyResolver/Problem.php | 4 +- src/Composer/DependencyResolver/Rule.php | 44 ++++++++++++++++++- .../SolverProblemsException.php | 4 +- .../installer/alias-solver-problems2.test | 2 +- ...-downgrades-non-allow-listed-unstable.test | 2 + .../installer/problems-reduce-versions.test | 2 + .../root-alias-change-with-circular-dep.test | 2 +- .../Fixtures/installer/solver-problems.test | 7 ++- ...update-allow-list-require-new-replace.test | 2 +- 9 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 112c13174..16da238b6 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -133,11 +133,11 @@ class Problem return "\n - ".implode("\n - ", $result); } - public function isCausedByLock() + public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool) { foreach ($this->reasons as $sectionRules) { foreach ($sectionRules as $rule) { - if ($rule->isCausedByLock()) { + if ($rule->isCausedByLock($repositorySet, $request, $pool)) { return true; } } diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 4dc483d5f..0a106c854 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -17,7 +17,9 @@ use Composer\Package\Link; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; use Composer\Repository\RepositorySet; +use Composer\Repository\PlatformRepository; use Composer\Package\Version\VersionParser; +use Composer\Semver\Constraint\Constraint; /** * @author Nils Adermann @@ -122,9 +124,47 @@ abstract class Rule abstract public function isAssertion(); - public function isCausedByLock() + public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool) { - return $this->getReason() === self::RULE_FIXED && $this->reasonData['lockable']; + if ($this->getReason() === self::RULE_FIXED && $this->reasonData['lockable']) { + return true; + } + + if ($this->getReason() === self::RULE_PACKAGE_REQUIRES) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $this->reasonData->getTarget())) { + return false; + } + foreach ($request->getFixedPackages() as $package) { + if ($package->getName() === $this->reasonData->getTarget()) { + if ($pool->isUnacceptableFixedPackage($package)) { + return true; + } + if (!$this->reasonData->getConstraint()->matches(new Constraint('=', $package->getVersion()))) { + return true; + } + break; + } + } + } + + if ($this->getReason() === self::RULE_ROOT_REQUIRE) { + if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $this->reasonData['packageName'])) { + return false; + } + foreach ($request->getFixedPackages() as $package) { + if ($package->getName() === $this->reasonData['packageName']) { + if ($pool->isUnacceptableFixedPackage($package)) { + return true; + } + if (!$this->reasonData['constraint']->matches(new Constraint('=', $package->getVersion()))) { + return true; + } + break; + } + } + } + + return false; } public function getPrettyString(RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 2b58b707a..41905175b 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -45,7 +45,7 @@ class SolverProblemsException extends \RuntimeException $hasExtensionProblems = true; } - $isCausedByLock |= $problem->isCausedByLock(); + $isCausedByLock |= $problem->isCausedByLock($repositorySet, $request, $pool); } $i = 1; @@ -63,7 +63,7 @@ class SolverProblemsException extends \RuntimeException } if ($isCausedByLock && !$isDevExtraction) { - $text .= "\nUse the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions."; + $text .= "\nUse the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions."; } if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match') && strpos($text, '- ocramius/package-versions')) { diff --git a/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test b/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test index 344c4ce67..6d69faddf 100644 --- a/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test +++ b/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test @@ -49,6 +49,6 @@ Your requirements could not be resolved to an installable set of packages. - locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file. - Root composer.json requires locked/pkg *@dev -> satisfiable by locked/pkg[dev-master]. -Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions. +Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. --EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test index 25bd4a9c6..b1298ad6e 100644 --- a/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test +++ b/tests/Composer/Test/Fixtures/installer/partial-update-downgrades-non-allow-listed-unstable.test @@ -60,3 +60,5 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - b/unstable is fixed to 1.1.0-alpha (lock file version) by a partial update but that version is rejected by your minimum-stability. Make sure you list it as an argument for the update command. + +Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. diff --git a/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test b/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test index e67f33a43..aa16b8acd 100644 --- a/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test +++ b/tests/Composer/Test/Fixtures/installer/problems-reduce-versions.test @@ -112,5 +112,7 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires b/b ^1.1 || ^2.0 || ^3.0, found b/b[1.1.0, ..., 1.2.9, 2.0.0, ..., 2.3.0-RC, 3.0.0, 3.0.1, 3.0.2, 3.0.3] but the package is fixed to 1.0.0 (lock file version) by a partial update and that version does not match. Make sure you list it as an argument for the update command. +Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. + --EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test b/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test index 07c8cdfd8..5cd80b27b 100644 --- a/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test +++ b/tests/Composer/Test/Fixtures/installer/root-alias-change-with-circular-dep.test @@ -62,6 +62,6 @@ Your lock file does not contain a compatible set of packages. Please run compose - b/requirer is locked to version 1.0.0 and an update of this package was not requested. - b/requirer 1.0.0 requires root/pkg ^1 -> found root/pkg[2.x-dev] but it does not match the constraint. -Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions. +Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. --EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/solver-problems.test b/tests/Composer/Test/Fixtures/installer/solver-problems.test index 06f4c1ba7..9ba5867a1 100644 --- a/tests/Composer/Test/Fixtures/installer/solver-problems.test +++ b/tests/Composer/Test/Fixtures/installer/solver-problems.test @@ -159,9 +159,12 @@ Potential causes: - It's a private package and you forgot to add a custom repository to find it Read for further common problems. - To enable extensions, verify that they are enabled in your .ini files: + +To enable extensions, verify that they are enabled in your .ini files: __inilist__ - You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode. +You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode. + +Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. --EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test b/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test index 641ac7f9e..60f899ba1 100644 --- a/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test +++ b/tests/Composer/Test/Fixtures/installer/update-allow-list-require-new-replace.test @@ -51,5 +51,5 @@ Your requirements could not be resolved to an installable set of packages. - new/pkg[1.0.0] cannot be installed as that would require removing current/dep[1.0.0]. new/pkg replaces current/dep and thus cannot coexist with it. - Root composer.json requires new/pkg 1.* -> satisfiable by new/pkg[1.0.0]. -Use the option --with-all-dependencies to allow updates and removals for packages currently locked to specific versions. +Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. --EXPECT-- From af6444353f078d156de6da817d129f34046e804a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 12:17:57 +0200 Subject: [PATCH 126/193] Refactor hint generation and whitespace handling in SolverProblemsException --- .../SolverProblemsException.php | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 41905175b..e6697fb05 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -54,31 +54,36 @@ class SolverProblemsException extends \RuntimeException $text .= " Problem ".($i++).$problem; } + $hints = array(); if (!$isDevExtraction && (strpos($text, 'could not be found') || strpos($text, 'no matching package found'))) { - $text .= "\nPotential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead for further common problems."; + $hints[] = "Potential causes:\n - A typo in the package name\n - The package is not available in a stable-enough version according to your minimum-stability setting\n see for more details.\n - It's a private package and you forgot to add a custom repository to find it\n\nRead for further common problems."; } if ($hasExtensionProblems) { - $text .= $this->createExtensionHint(); + $hints[] = $this->createExtensionHint(); } if ($isCausedByLock && !$isDevExtraction) { - $text .= "\nUse the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions."; + $hints[] = "Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions."; } if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match') && strpos($text, '- ocramius/package-versions')) { - $text .= "\nocramius/package-versions only provides support for Composer 2 in 1.8+, which requires PHP 7.4.\nIf you can not upgrade PHP you can require composer/package-versions-deprecated to resolve this with PHP 7.0+.\n"; + $hints[] = "ocramius/package-versions only provides support for Composer 2 in 1.8+, which requires PHP 7.4.\nIf you can not upgrade PHP you can require composer/package-versions-deprecated to resolve this with PHP 7.0+."; } // TODO remove before 2.0 final if (!class_exists('PHPUnit\Framework\TestCase', false)) { if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) { - $text .= "\nYou are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs, but this will also ignore your PHP version and may result in bigger problems down the line."; + $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs, but this will also ignore your PHP version and may result in bigger problems down the line."; } else { - $text .= "\nYou are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2."; + $hints[] = "You are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2."; } } + if ($hints) { + $text .= "\n" . implode("\n\n", $hints); + } + return $text; } @@ -95,9 +100,9 @@ class SolverProblemsException extends \RuntimeException return ''; } - $text = "\n To enable extensions, verify that they are enabled in your .ini files:\n - "; + $text = "To enable extensions, verify that they are enabled in your .ini files:\n - "; $text .= implode("\n - ", $paths); - $text .= "\n You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode."; + $text .= "\nYou can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode."; return $text; } From 1d2df5ef55553747e7f187dca45675b3d520db99 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 14:48:58 +0200 Subject: [PATCH 127/193] Add output to remove/require command to highlight the fact it is running an update command, refs #8910 --- src/Composer/Command/RemoveCommand.php | 7 +++++++ src/Composer/Command/RequireCommand.php | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 7e4c7ed49..3d6c56df5 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -191,6 +191,8 @@ EOT } } + $io->writeError(''.$file.' has been updated'); + if ($input->getOption('no-update')) { return 0; } @@ -225,12 +227,17 @@ EOT $apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader'); $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; + $flags = ''; if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) { $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; + $flags .= ' --with-all-dependencies'; } elseif ($input->getOption('no-update-with-dependencies')) { $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED; + $flags .= ' --with-dependencies'; } + $io->writeError('Running composer update '.implode(' ', $packages).$flags); + $install ->setVerbose($input->getOption('verbose')) ->setDevMode($updateDevMode) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 84c52a74c..250e99513 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -264,18 +264,24 @@ EOT $rootPackage->setDevRequires($links['require-dev']); } + $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); $apcu = $input->getOption('apcu-autoloader') || $composer->getConfig()->get('apcu-autoloader'); $updateAllowTransitiveDependencies = Request::UPDATE_ONLY_LISTED; + $flags = ''; if ($input->getOption('update-with-all-dependencies') || $input->getOption('with-all-dependencies')) { $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS; + $flags .= ' --with-all-dependencies'; } elseif ($input->getOption('update-with-dependencies') || $input->getOption('with-dependencies')) { $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; + $flags .= ' --with-dependencies'; } + $io->writeError('Running composer update '.implode(' ', array_keys($requirements)).$flags); + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); From 541692bbfe495bdd2d80aadc1e38285ed8b7c26d Mon Sep 17 00:00:00 2001 From: Timo Webler Date: Mon, 18 May 2020 13:19:27 +0200 Subject: [PATCH 128/193] Use "getInitialWorkingDirectory" instead of "getWorkingDirectory" Follow-up 8d24b61bef4839104db7f05c23400e598a8915e4 --- src/Composer/Command/ExecCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php index d530def66..fba9e7e8a 100644 --- a/src/Composer/Command/ExecCommand.php +++ b/src/Composer/Command/ExecCommand.php @@ -99,7 +99,7 @@ EOT try { chdir($this->getApplication()->getInitialWorkingDirectory()); } catch (\Exception $e) { - throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getWorkingDirectory().'"', 0, $e); + throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getInitialWorkingDirectory().'"', 0, $e); } } From d42e12c5144dff2fca3edd37c74d4b655c857804 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 16:34:51 +0200 Subject: [PATCH 129/193] Make config non optional, fixes #8896 --- src/Composer/Factory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 9160ce2f5..1b084461c 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -584,7 +584,7 @@ class Factory * @param array $options Array of options passed directly to HttpDownloader constructor * @return HttpDownloader */ - public static function createHttpDownloader(IOInterface $io, Config $config = null, $options = array()) + public static function createHttpDownloader(IOInterface $io, Config $config, $options = array()) { static $warned = false; $disableTls = false; From a40e6157debb32b4e86db11748cb24ae61ed6b75 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 16:40:45 +0200 Subject: [PATCH 130/193] Update deps --- composer.lock | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/composer.lock b/composer.lock index c4c5a6c2f..8c9a1d6c2 100644 --- a/composer.lock +++ b/composer.lock @@ -83,12 +83,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "1f88d7a8a0a3d71facc60599e87c6b3107893e90" + "reference": "e639bd3c21f90c8368e6c4a87fd4491f8b21eb2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/1f88d7a8a0a3d71facc60599e87c6b3107893e90", - "reference": "1f88d7a8a0a3d71facc60599e87c6b3107893e90", + "url": "https://api.github.com/repos/composer/semver/zipball/e639bd3c21f90c8368e6c4a87fd4491f8b21eb2c", + "reference": "e639bd3c21f90c8368e6c4a87fd4491f8b21eb2c", "shasum": "" }, "require": { @@ -147,12 +147,16 @@ "url": "https://packagist.com", "type": "custom" }, + { + "url": "https://github.com/composer", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2020-05-06T07:58:50+00:00" + "time": "2020-05-19T14:21:25+00:00" }, { "name": "composer/spdx-licenses", @@ -784,16 +788,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", "shasum": "" }, "require": { @@ -805,7 +809,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -839,7 +843,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.17.0" }, "funding": [ { @@ -855,20 +859,20 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2020-05-12T16:14:59+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c", + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c", "shasum": "" }, "require": { @@ -880,7 +884,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -915,7 +919,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.17.0" }, "funding": [ { @@ -931,7 +935,7 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/process", From 62d0443e07d813ee0fe15db4c07103684eaa8384 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 16:46:33 +0200 Subject: [PATCH 131/193] Avoid checking for readline in non-CLI contexts, fixes #8909 --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index bc334c927..c531b3424 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -608,7 +608,7 @@ EOF; } $extension = var_export($match[1], true); - if ($match[1] === 'pcntl') { + if ($match[1] === 'pcntl' || $match[1] === 'readline') { $requiredExtensions[$extension] = "PHP_SAPI !== 'cli' || extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; } else { $requiredExtensions[$extension] = "extension_loaded($extension) || \$missingExtensions[] = $extension;\n"; From 45d3e133a4691eccb12e9cd6f9dfd76eddc1906d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 17:15:08 +0200 Subject: [PATCH 132/193] Avoid checking for unbounded constraints in platform checks --- composer.lock | 8 ++-- src/Composer/Autoload/AutoloadGenerator.php | 40 +++++++++++++------ .../Fixtures/platform/no_php_lower_bound.php | 4 +- .../Fixtures/platform/no_php_required.php | 4 -- .../Fixtures/platform/no_php_upper_bound.php | 4 +- .../platform/replaced_provided_exts.php | 4 -- 6 files changed, 36 insertions(+), 28 deletions(-) diff --git a/composer.lock b/composer.lock index 8c9a1d6c2..3841369fb 100644 --- a/composer.lock +++ b/composer.lock @@ -83,12 +83,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "e639bd3c21f90c8368e6c4a87fd4491f8b21eb2c" + "reference": "594e5242ff1ba3aac2f4b8401d3f1ca1d781fd89" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/e639bd3c21f90c8368e6c4a87fd4491f8b21eb2c", - "reference": "e639bd3c21f90c8368e6c4a87fd4491f8b21eb2c", + "url": "https://api.github.com/repos/composer/semver/zipball/594e5242ff1ba3aac2f4b8401d3f1ca1d781fd89", + "reference": "594e5242ff1ba3aac2f4b8401d3f1ca1d781fd89", "shasum": "" }, "require": { @@ -156,7 +156,7 @@ "type": "tidelift" } ], - "time": "2020-05-19T14:21:25+00:00" + "time": "2020-05-19T14:49:33+00:00" }, { "name": "composer/spdx-licenses", diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index c531b3424..67eb2506c 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -648,14 +648,34 @@ EOF; return implode('.', $chunks); }; - $lowestOperator = $lowestPhpVersion->isInclusive() ? '>=' : '>'; - $highestOperator = $highestPhpVersion->isInclusive() ? '<=' : '<'; - $lowestPhpVersionId = $formatToPhpVersionId($lowestPhpVersion); - $highestPhpVersionId = $formatToPhpVersionId($highestPhpVersion); - $lowestPhpVersion = $formatToHumanReadable($lowestPhpVersion); - $highestPhpVersion = $formatToHumanReadable($highestPhpVersion); - $requiredExtensions = implode('', $requiredExtensions); + $requiredPhp = array(); + $requiredPhpError = array(); + if (!$lowestPhpVersion->isZero()) { + $operator = $lowestPhpVersion->isInclusive() ? '>=' : '>'; + $requiredPhp[] = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($lowestPhpVersion); + $requiredPhpError[] = '"'.$operator.' '.$formatToHumanReadable($lowestPhpVersion).'"'; + } + if (!$highestPhpVersion->isPositiveInfinity()) { + $operator = $highestPhpVersion->isInclusive() ? '<=' : '<'; + $requiredPhp[] = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($highestPhpVersion); + $requiredPhpError[] = '"'.$operator.' '.$formatToHumanReadable($highestPhpVersion).'"'; + } + if ($requiredPhp) { + $requiredPhp = implode(' && ', $requiredPhp); + $requiredPhpError = implode(' and ', $requiredPhpError); + $requiredPhp = <<= 0 && PHP_VERSION_ID < 80000)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID < 80000)) { + $issues[] = 'Your Composer dependencies require a PHP version "< 8.0.0". You are running ' . PHP_VERSION . '.'; } if ($issues) { diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php index 0cd09927a..f3ae3e071 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_required.php @@ -4,10 +4,6 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 0 && PHP_VERSION_ID < 99999)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 99999". You are running ' . PHP_VERSION . '.'; -} - $missingExtensions = array(); extension_loaded('json') || $missingExtensions[] = 'json'; extension_loaded('xml') || $missingExtensions[] = 'xml'; diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php index 838cbbbba..85a922d49 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_upper_bound.php @@ -4,8 +4,8 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 99999)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 99999". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID >= 70200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; } if ($issues) { diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php b/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php index d57294f0b..8056909d6 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/replaced_provided_exts.php @@ -4,10 +4,6 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 0 && PHP_VERSION_ID < 99999)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 0" and "< 99999". You are running ' . PHP_VERSION . '.'; -} - $missingExtensions = array(); extension_loaded('pdo') || $missingExtensions[] = 'pdo'; From 0a4df6c3b4c23794260188d987ea6940ddab6d83 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 20 May 2020 09:52:49 +0200 Subject: [PATCH 133/193] Bump PHPUnitBridge to higher release as it still supports php 5.3 --- composer.json | 5 ++--- composer.lock | 32 +++++++++----------------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/composer.json b/composer.json index bd2e3e465..415df800f 100644 --- a/composer.json +++ b/composer.json @@ -38,11 +38,10 @@ "react/promise": "^1.2 || ^2.7" }, "conflict": { - "symfony/console": "2.8.38", - "symfony/phpunit-bridge": "3.4.40" + "symfony/console": "2.8.38" }, "require-dev": { - "symfony/phpunit-bridge": "^3.4", + "symfony/phpunit-bridge": "^4.2 || ^5", "phpspec/prophecy": "^1.10" }, "suggest": { diff --git a/composer.lock b/composer.lock index 3841369fb..4f27da9f3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "08293668540de913642a19e6af165dd0", + "content-hash": "a5f07d1ae8479fae25dd57b2d966cd22", "packages": [ { "name": "composer/ca-bundle", @@ -1423,23 +1423,23 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v3.4.39", + "version": "v4.2.12", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9" + "reference": "80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c02893ae43532b46a4f0e0f207d088b939f278d9", - "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7", + "reference": "80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7", "shasum": "" }, "require": { "php": ">=5.3.3" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "suggest": { "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" @@ -1450,7 +1450,7 @@ "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" }, "thanks": { "name": "phpunit/phpunit", @@ -1485,23 +1485,9 @@ "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v3.4.38" + "source": "https://github.com/symfony/phpunit-bridge/tree/4.2" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-02-21T08:01:47+00:00" + "time": "2019-07-05T06:33:37+00:00" } ], "aliases": [], From 4e1f8cf89dce57ef8c98b9ef05002d8c2dde069e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 20 May 2020 14:39:14 +0200 Subject: [PATCH 134/193] Build tweaks --- .github/workflows/continuous-integration.yml | 81 ++++++++++++------- composer.json | 10 +-- composer.lock | 12 +-- phpunit.xml.dist | 1 - tests/Composer/Test/IO/ConsoleIOTest.php | 2 +- tests/Composer/Test/Util/ErrorHandlerTest.php | 6 +- tests/complete.phpunit.xml | 1 - 7 files changed, 68 insertions(+), 45 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index ffd1f3433..6da8f1ead 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -10,7 +10,9 @@ on: env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" - SYMFONY_PHPUNIT_VERSION: "" + COMPOSER_UPDATE_FLAGS: "" + SYMFONY_PHPUNIT_VERSION: "8.3" + SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1" jobs: tests: @@ -68,6 +70,30 @@ jobs: - name: "Checkout" uses: "actions/checkout@v2" + - name: "Install PHP 7.4 to prepare nightly builds" + if: "matrix.php-version == '8.0'" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + extensions: "intl" + ini-values: "memory_limit=-1, phar.readonly=0" + php-version: "7.4" + + - name: "Choose PHPUnit version" + if: "!startsWith(matrix.os, 'windows')" + run: | + if [ "${{ matrix.php-version }}" = "5.3" ] || [ "${{ matrix.php-version }}" = "5.4" ] || [ "${{ matrix.php-version }}" = "5.5" ]; then + echo "::set-env name=SYMFONY_PHPUNIT_VERSION::4.8"; + elif [ "${{ matrix.php-version }}" = "5.6" ]; then + echo "::set-env name=SYMFONY_PHPUNIT_VERSION::5.7"; + elif [ "${{ matrix.php-version }}" = "7.0" ]; then + echo "::set-env name=SYMFONY_PHPUNIT_VERSION::6.5"; + elif [ "${{ matrix.php-version }}" = "7.1" ]; then + echo "::set-env name=SYMFONY_PHPUNIT_VERSION::7.5"; + else + echo "::set-env name=SYMFONY_PHPUNIT_VERSION::8.3"; + fi + - name: "Install PHP" uses: "shivammathur/setup-php@v2" with: @@ -87,28 +113,29 @@ jobs: key: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}-${{ hashFiles('**/composer.lock') }}" restore-keys: "php-${{ matrix.php-version }}-symfony-php-unit-version-${{ env.SYMFONY_PHPUNIT_VERSION }}" - - name: "Install highest dependencies from composer.json using composer binary provided by system" - if: "matrix.dependencies == 'highest'" - run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }}" + - name: "Handle lowest dependencies update" + if: "contains(matrix.dependencies, 'lowest')" + run: "echo \"::set-env name=COMPOSER_UPDATE_FLAGS::$COMPOSER_UPDATE_FLAGS --prefer-lowest\"" - - name: "Install highest dependencies from composer.json using composer binary provided by system, ignoring platform requirements" - if: "matrix.dependencies == 'highest-ignore'" - run: "composer config platform --unset && composer update ${{ env.COMPOSER_FLAGS }} --ignore-platform-reqs" + - name: "Handle ignore-platform-reqs dependencies update" + if: "contains(matrix.dependencies, 'ignore')" + run: "echo \"::set-env name=COMPOSER_FLAGS::$COMPOSER_FLAGS --ignore-platform-reqs\"" - - name: "Install lowest dependencies from composer.json using composer binary provided by system" - if: "matrix.dependencies == 'lowest'" - run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest" + - name: "Remove platform config to get latest dependencies for current PHP version when build is not locked" + run: "composer config platform --unset" - - name: "Install lowest dependencies from composer.json using composer binary provided by system, ignoring platform requirements" - if: "matrix.dependencies == 'lowest-ignore'" - run: "composer update ${{ env.COMPOSER_FLAGS }} --prefer-lowest --ignore-platform-reqs" + - name: "Update dependencies from composer.json using composer binary provided by system" + if: "contains(matrix.dependencies, 'highest') || contains(matrix.dependencies, 'lowest')" + run: "composer update ${{ env.COMPOSER_UPDATE_FLAGS }} ${{ env.COMPOSER_FLAGS }}" - name: "Install dependencies from composer.lock using composer binary provided by system" if: "matrix.dependencies == 'locked'" run: "composer install ${{ env.COMPOSER_FLAGS }}" + - name: "Update Symfony's PHPUnitBridge to latest available for the current PHP always as it is not really a dependency of the project" + run: "composer update ${{ env.COMPOSER_FLAGS }} symfony/phpunit-bridge" + - name: "Run install again using composer binary from source" - if: "matrix.dependencies != 'highest-ignore'" run: "bin/composer install ${{ env.COMPOSER_FLAGS }}" - name: "Validate composer.json" @@ -117,21 +144,15 @@ jobs: - name: "Prepare git environment" run: "git config --global user.name composer && git config --global user.email composer@example.com" - - name: "Set SYMFONY_PHPUNIT_VERSION environment variable" - if: "matrix.php-version == '7.4'" - run: "echo \"::set-env name=SYMFONY_PHPUNIT_VERSION::7.5\"" - - - name: "Run tests" - if: "matrix.php-version != '8.0' && matrix.php-version != '7.3'" - run: "vendor/bin/simple-phpunit" - - - name: "Run Complete test suite" - if: "matrix.php-version == '7.3'" - run: "vendor/bin/simple-phpunit --configuration tests/complete.phpunit.xml" - - - name: "Run tests for PHP 8" + - name: "Pre-install PHPUnit using PHP 7.4 for PHP 8" if: "matrix.php-version == '8.0'" run: | - bin/composer remove --dev symfony/phpunit-bridge --ignore-platform-reqs - bin/composer require phpunit/phpunit:^7.5 --ignore-platform-reqs --with-dependencies - vendor/bin/phpunit + php7.4 vendor/bin/simple-phpunit install + + - name: "Run tests" + if: "matrix.php-version != '7.3'" + run: "vendor/bin/simple-phpunit" + + - name: "Run complete test suite on 7.3" + if: "matrix.php-version == '7.3'" + run: "vendor/bin/simple-phpunit --configuration tests/complete.phpunit.xml" diff --git a/composer.json b/composer.json index 415df800f..7a670ecb0 100644 --- a/composer.json +++ b/composer.json @@ -24,17 +24,17 @@ "require": { "php": "^5.3.2 || ^7.0", "composer/ca-bundle": "^1.0", - "composer/semver": "^2.0@dev", + "composer/semver": "^2.1@dev", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/finder": "^2.7 || ^3.0 || ^4.0 || ^5.0", - "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0", + "symfony/console": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", + "symfony/filesystem": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", + "symfony/finder": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", + "symfony/process": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", "react/promise": "^1.2 || ^2.7" }, "conflict": { diff --git a/composer.lock b/composer.lock index 4f27da9f3..f8c918820 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a5f07d1ae8479fae25dd57b2d966cd22", + "content-hash": "4661f272e877e4aaaaf1cf0848b891b7", "packages": [ { "name": "composer/ca-bundle", @@ -83,12 +83,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "594e5242ff1ba3aac2f4b8401d3f1ca1d781fd89" + "reference": "07a3e324e654298714fcecfbb4604e3ee0c6f3bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/594e5242ff1ba3aac2f4b8401d3f1ca1d781fd89", - "reference": "594e5242ff1ba3aac2f4b8401d3f1ca1d781fd89", + "url": "https://api.github.com/repos/composer/semver/zipball/07a3e324e654298714fcecfbb4604e3ee0c6f3bd", + "reference": "07a3e324e654298714fcecfbb4604e3ee0c6f3bd", "shasum": "" }, "require": { @@ -96,7 +96,7 @@ }, "require-dev": { "phpstan/phpstan": "^0.12.19", - "phpunit/phpunit": "^4.5 || ^5.0.5 || ^7" + "symfony/phpunit-bridge": "^4.2 || ^5" }, "type": "library", "extra": { @@ -156,7 +156,7 @@ "type": "tidelift" } ], - "time": "2020-05-19T14:49:33+00:00" + "time": "2020-05-20T08:27:54+00:00" }, { "name": "composer/spdx-licenses", diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 728125c15..75c2f3c74 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -10,7 +10,6 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" failOnRisky="true" - strict="false" processIsolation="false" stopOnFailure="false" bootstrap="tests/bootstrap.php" diff --git a/tests/Composer/Test/IO/ConsoleIOTest.php b/tests/Composer/Test/IO/ConsoleIOTest.php index ef5096300..1ff86170c 100644 --- a/tests/Composer/Test/IO/ConsoleIOTest.php +++ b/tests/Composer/Test/IO/ConsoleIOTest.php @@ -84,7 +84,7 @@ class ConsoleIOTest extends TestCase ->with( $this->callback(function ($messages) { $result = preg_match("[(.*)/(.*) First line]", $messages[0]) > 0; - $result &= preg_match("[(.*)/(.*) Second line]", $messages[1]) > 0; + $result = $result && preg_match("[(.*)/(.*) Second line]", $messages[1]) > 0; return $result; }), diff --git a/tests/Composer/Test/Util/ErrorHandlerTest.php b/tests/Composer/Test/Util/ErrorHandlerTest.php index 89cc8b4dc..ef501542e 100644 --- a/tests/Composer/Test/Util/ErrorHandlerTest.php +++ b/tests/Composer/Test/Util/ErrorHandlerTest.php @@ -46,7 +46,11 @@ class ErrorHandlerTest extends TestCase */ public function testErrorHandlerCaptureWarning() { - $this->setExpectedException('\ErrorException', 'array_merge'); + if (PHP_VERSION_ID >= 80000) { + $this->setExpectedException('TypeError', 'array_merge'); + } else { + $this->setExpectedException('ErrorException', 'array_merge'); + } array_merge(array(), 'string'); } diff --git a/tests/complete.phpunit.xml b/tests/complete.phpunit.xml index 30faf6837..14b2de093 100644 --- a/tests/complete.phpunit.xml +++ b/tests/complete.phpunit.xml @@ -10,7 +10,6 @@ convertNoticesToExceptions="true" convertWarningsToExceptions="true" failOnRisky="true" - strict="false" processIsolation="false" stopOnFailure="false" bootstrap="./bootstrap.php" From 17fa85d7c2fc96c2dfbf74df9699eaaf013ed21a Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Thu, 21 May 2020 14:49:07 +0100 Subject: [PATCH 135/193] Tweaked symfony versions (#8920) --- composer.json | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 7a670ecb0..5b29439db 100644 --- a/composer.json +++ b/composer.json @@ -31,17 +31,14 @@ "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", - "symfony/console": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", - "symfony/filesystem": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", - "symfony/finder": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", - "symfony/process": "^2.8.52 || ^3.4 || ^4.4 || ^5.0", + "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", + "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", + "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", + "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0", "react/promise": "^1.2 || ^2.7" }, - "conflict": { - "symfony/console": "2.8.38" - }, "require-dev": { - "symfony/phpunit-bridge": "^4.2 || ^5", + "symfony/phpunit-bridge": "^4.2 || ^5.0", "phpspec/prophecy": "^1.10" }, "suggest": { From c7fb15faf47d05449366f47117b94d30c77e8b24 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 21 May 2020 16:12:41 +0200 Subject: [PATCH 136/193] Update to MatchAllConstraint --- composer.lock | 10 +++++----- src/Composer/Autoload/AutoloadGenerator.php | 4 ++-- src/Composer/DependencyResolver/Pool.php | 1 - src/Composer/DependencyResolver/PoolBuilder.php | 6 +++--- src/Composer/Repository/ComposerRepository.php | 4 ++-- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index f8c918820..d7fd5fb0f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4661f272e877e4aaaaf1cf0848b891b7", + "content-hash": "aaa28d3c716a6b5d973347f4046f4931", "packages": [ { "name": "composer/ca-bundle", @@ -83,12 +83,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "07a3e324e654298714fcecfbb4604e3ee0c6f3bd" + "reference": "7401fc3628694736b1cac8ec8d5c7dfdcfe54ea6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/07a3e324e654298714fcecfbb4604e3ee0c6f3bd", - "reference": "07a3e324e654298714fcecfbb4604e3ee0c6f3bd", + "url": "https://api.github.com/repos/composer/semver/zipball/7401fc3628694736b1cac8ec8d5c7dfdcfe54ea6", + "reference": "7401fc3628694736b1cac8ec8d5c7dfdcfe54ea6", "shasum": "" }, "require": { @@ -156,7 +156,7 @@ "type": "tidelift" } ], - "time": "2020-05-20T08:27:54+00:00" + "time": "2020-05-21T14:08:19+00:00" }, { "name": "composer/spdx-licenses", diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 67eb2506c..91bf5ba7e 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -20,7 +20,7 @@ use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; use Composer\Semver\Constraint\Bound; -use Composer\Semver\Constraint\EmptyConstraint; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Util\Filesystem; use Composer\Script\ScriptEvents; use Composer\Util\PackageSorter; @@ -580,7 +580,7 @@ EOF; list($package, $installPath) = $item; foreach (array_merge($package->getReplaces(), $package->getProvides()) as $link) { if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { - $extensionProviders[$match[1]][] = $link->getConstraint() ?: new EmptyConstraint(); + $extensionProviders[$match[1]][] = $link->getConstraint() ?: new MatchAllConstraint(); } } } diff --git a/src/Composer/DependencyResolver/Pool.php b/src/Composer/DependencyResolver/Pool.php index b848ae37e..26ca947d3 100644 --- a/src/Composer/DependencyResolver/Pool.php +++ b/src/Composer/DependencyResolver/Pool.php @@ -16,7 +16,6 @@ use Composer\Package\AliasPackage; use Composer\Package\Version\VersionParser; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\Constraint; -use Composer\Semver\Constraint\EmptyConstraint; use Composer\Package\PackageInterface; /** diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index 9d2d3ade9..9e1f9f433 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -22,7 +22,7 @@ use Composer\Repository\PlatformRepository; use Composer\Repository\RootPackageRepository; use Composer\Semver\Constraint\Constraint; use Composer\Semver\Constraint\ConstraintInterface; -use Composer\Semver\Constraint\EmptyConstraint; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Semver\Constraint\MultiConstraint; use Composer\EventDispatcher\EventDispatcher; use Composer\Plugin\PrePoolCreateEvent; @@ -153,7 +153,7 @@ class PoolBuilder } $loadNames[$packageName] = $constraint; - $this->nameConstraints[$packageName] = $constraint && !($constraint instanceof EmptyConstraint) ? array($constraint) : null; + $this->nameConstraints[$packageName] = $constraint && !($constraint instanceof MatchAllConstraint) ? array($constraint) : null; } // clean up loadNames for anything we manually marked loaded above @@ -305,7 +305,7 @@ class PoolBuilder } $linkConstraint = $link->getConstraint(); - if ($linkConstraint && !($linkConstraint instanceof EmptyConstraint)) { + if ($linkConstraint && !($linkConstraint instanceof MatchAllConstraint)) { if (!\array_key_exists($require, $this->nameConstraints)) { $this->nameConstraints[$require] = array($linkConstraint); } elseif (\is_array($this->nameConstraints[$require])) { diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 34584e2db..e960f63be 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -31,7 +31,7 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Downloader\TransportException; use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\Constraint; -use Composer\Semver\Constraint\EmptyConstraint; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Util\Http\Response; use Composer\Util\MetadataMinifier; use Composer\Util\Url; @@ -265,7 +265,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if (is_array($this->availablePackages)) { $packageMap = array(); foreach ($this->availablePackages as $name) { - $packageMap[$name] = new EmptyConstraint(); + $packageMap[$name] = new MatchAllConstraint(); } $result = $this->loadAsyncPackages($packageMap); From 30f994e4246ca736b82779eb15e036127de2de53 Mon Sep 17 00:00:00 2001 From: azjezz Date: Thu, 21 May 2020 18:08:26 +0100 Subject: [PATCH 137/193] fix deprecations in PHP 8 --- .../EventDispatcher/EventDispatcher.php | 19 ++++++++++++++----- src/Composer/Repository/RepositoryManager.php | 16 ++++++++++++++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 6b2449d99..3d4542a96 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -326,13 +326,22 @@ class EventDispatcher return $event; } - $typehint = $reflected->getClass(); - - if (!$typehint instanceof \ReflectionClass) { - return $event; + $expected = null; + $isClass = false; + if (\PHP_VERSION_ID >= 70000) { + $reflectionType = $reflected->getType(); + if ($reflectionType) { + $expected = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string)$reflectionType; + $isClass = !$reflectionType->isBuiltin(); + } + } else { + $expected = $reflected->getClass() ? $reflected->getClass()->getName() : null; + $isClass = null !== $expected; } - $expected = $typehint->getName(); + if (!$isClass) { + return $event; + } // BC support if (!$event instanceof $expected && $expected === 'Composer\Script\CommandEvent') { diff --git a/src/Composer/Repository/RepositoryManager.php b/src/Composer/Repository/RepositoryManager.php index 87b82d14d..a724f644f 100644 --- a/src/Composer/Repository/RepositoryManager.php +++ b/src/Composer/Repository/RepositoryManager.php @@ -127,8 +127,20 @@ class RepositoryManager $reflMethod = new \ReflectionMethod($class, '__construct'); $params = $reflMethod->getParameters(); - if (isset($params[4]) && $params[4]->getClass() && $params[4]->getClass()->getName() === 'Composer\Util\RemoteFilesystem') { - return new $class($config, $this->io, $this->config, $this->eventDispatcher, $this->rfs); + if (isset($params[4])) { + $paramType = null; + if (\PHP_VERSION_ID >= 70000) { + $reflectionType = $params[4]->getType(); + if ($reflectionType) { + $paramType = $reflectionType instanceof \ReflectionNamedType ? $reflectionType->getName() : (string)$reflectionType; + } + } else { + $paramType = $params[4]->getClass() ? $params[4]->getClass()->getName() : null; + } + + if ($paramType === 'Composer\Util\RemoteFilesystem') { + return new $class($config, $this->io, $this->config, $this->eventDispatcher, $this->rfs); + } } return new $class($config, $this->io, $this->config, $this->eventDispatcher); From 385655f02aaafddeb7ef64d83fd0b75ecb608193 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 22 May 2020 13:23:14 +0200 Subject: [PATCH 138/193] Mark temp files as such for clarity in case any gets left over --- src/Composer/Downloader/FileDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 21a3e858b..c598efd9a 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -325,7 +325,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface */ protected function getFileName(PackageInterface $package, $path) { - return rtrim($this->config->get('vendor-dir').'/composer/'.md5($package.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.'); + return rtrim($this->config->get('vendor-dir').'/composer/tmp-'.md5($package.spl_object_hash($package)).'.'.pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_EXTENSION), '.'); } /** From a07f9ffba98fe5472a3004b56299ca99e22dc6c9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 22 May 2020 13:24:30 +0200 Subject: [PATCH 139/193] Catch SIGINT/Ctrl+C during installs and run cleanups on all packages before exiting --- .../Installer/InstallationManager.php | 137 ++++++++++++------ 1 file changed, 90 insertions(+), 47 deletions(-) diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 4b8db7877..a8266bfe7 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -171,34 +171,89 @@ class InstallationManager public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true) { $promises = array(); - - foreach ($operations as $operation) { - $opType = $operation->getOperationType(); - $promise = null; - - if ($opType === 'install') { - $package = $operation->getPackage(); - $installer = $this->getInstaller($package->getType()); - $promise = $installer->download($package); - } elseif ($opType === 'update') { - $target = $operation->getTargetPackage(); - $targetType = $target->getType(); - $installer = $this->getInstaller($targetType); - $promise = $installer->download($target, $operation->getInitialPackage()); - } - - if ($promise) { - $promises[] = $promise; - } - } - - if (!empty($promises)) { - $this->loop->wait($promises); - } - $cleanupPromises = array(); + + $loop = $this->loop; + $runCleanup = function () use (&$cleanupPromises, $loop) { + $promises = array(); + + foreach ($cleanupPromises as $cleanup) { + $promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) { + $promise = $cleanup(); + if (null === $promise) { + $resolve(); + } else { + $promise->then(function () use ($resolve) { + $resolve(); + }); + } + }); + } + + if (!empty($promises)) { + $loop->wait($promises); + } + }; + + // handler Ctrl+C for unix-like systems + $handleInterrupts = function_exists('pcntl_async_signals') && function_exists('pcntl_signal'); + $prevHandler = null; + if ($handleInterrupts) { + pcntl_async_signals(true); + $prevHandler = pcntl_signal_get_handler(SIGINT); + pcntl_signal(SIGINT, function ($sig) use ($runCleanup, $prevHandler) { + $runCleanup(); + + if (!in_array($prevHandler, array(SIG_DFL, SIG_IGN), true)) { + call_user_func($prevHandler, $sig); + } + + exit(130); + }); + } + try { - foreach ($operations as $operation) { + foreach ($operations as $index => $operation) { + $opType = $operation->getOperationType(); + + // ignoring alias ops as they don't need to execute anything at this stage + if (!in_array($opType, array('update', 'install', 'uninstall'))) { + continue; + } + + if ($opType === 'update') { + $package = $operation->getTargetPackage(); + $initialPackage = $operation->getInitialPackage(); + } else { + $package = $operation->getPackage(); + $initialPackage = null; + } + $installer = $this->getInstaller($package->getType()); + + $cleanupPromises[$index] = function () use ($opType, $installer, $package, $initialPackage) { + // avoid calling cleanup if the download was not even initialized for a package + // as without installation source configured nothing will work + if (!$package->getInstallationSource()) { + return; + } + + return $installer->cleanup($opType, $package, $initialPackage); + }; + + if ($opType !== 'uninstall') { + $promise = $installer->download($package, $initialPackage); + if ($promise) { + $promises[] = $promise; + } + } + } + + // execute all downloads first + if (!empty($promises)) { + $this->loop->wait($promises); + } + + foreach ($operations as $index => $operation) { $opType = $operation->getOperationType(); // ignoring alias ops as they don't need to execute anything @@ -236,13 +291,9 @@ class InstallationManager $promise = new \React\Promise\Promise(function ($resolve, $reject) { $resolve(); }); } - $cleanupPromise = function () use ($opType, $installer, $package, $initialPackage) { - return $installer->cleanup($opType, $package, $initialPackage); - }; - $promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) { return $installManager->$opType($repo, $operation); - })->then($cleanupPromise) + })->then($cleanupPromises[$index]) ->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) { $repo->write($devMode, $installManager); @@ -256,35 +307,27 @@ class InstallationManager throw $e; }); - $cleanupPromises[] = $cleanupPromise; $promises[] = $promise; } + // execute all prepare => installs/updates/removes => cleanup steps if (!empty($promises)) { $this->loop->wait($promises); } } catch (\Exception $e) { - $promises = array(); - foreach ($cleanupPromises as $cleanup) { - $promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) { - $promise = $cleanup(); - if (null === $promise) { - $resolve(); - } else { - $promise->then(function () use ($resolve) { - $resolve(); - }); - } - }); - } + $runCleanup(); - if (!empty($promises)) { - $this->loop->wait($promises); + if ($handleInterrupts) { + pcntl_signal(SIGINT, $prevHandler); } throw $e; } + if ($handleInterrupts) { + pcntl_signal(SIGINT, $prevHandler); + } + // do a last write so that we write the repository even if nothing changed // as that can trigger an update of some files like InstalledVersions.php if // running a new composer version From d72a07db06a4f8b00c36ce0d885fb7db9ade54e1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 22 May 2020 13:28:47 +0200 Subject: [PATCH 140/193] Fix tests --- tests/Composer/Test/Downloader/ArchiveDownloaderTest.php | 2 +- tests/Composer/Test/Downloader/FileDownloaderTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php index 793c5eab5..977fb6574 100644 --- a/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ArchiveDownloaderTest.php @@ -36,7 +36,7 @@ class ArchiveDownloaderTest extends TestCase ->will($this->returnValue('/vendor')); $first = $method->invoke($downloader, $packageMock, '/path'); - $this->assertRegExp('#/vendor/composer/[a-z0-9]+\.js#', $first); + $this->assertRegExp('#/vendor/composer/tmp-[a-z0-9]+\.js#', $first); $this->assertSame($first, $method->invoke($downloader, $packageMock, '/path')); } diff --git a/tests/Composer/Test/Downloader/FileDownloaderTest.php b/tests/Composer/Test/Downloader/FileDownloaderTest.php index af5a9da11..c86ffa2f7 100644 --- a/tests/Composer/Test/Downloader/FileDownloaderTest.php +++ b/tests/Composer/Test/Downloader/FileDownloaderTest.php @@ -99,7 +99,7 @@ class FileDownloaderTest extends TestCase ->with('vendor-dir') ->will($this->returnValue('/vendor')); - $this->assertRegExp('#/vendor/composer/[a-z0-9]+\.js#', $method->invoke($downloader, $packageMock, '/path')); + $this->assertRegExp('#/vendor/composer/tmp-[a-z0-9]+\.js#', $method->invoke($downloader, $packageMock, '/path')); } public function testDownloadButFileIsUnsaved() From 6a0e02db1bc6cd402a3054f3b22381ae6c210f58 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 23 May 2020 21:22:07 +0200 Subject: [PATCH 141/193] Autoload-Generator: support glob patterns in classmaps --- 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 f242a0594..b86a4e9ce 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -65,7 +65,7 @@ class ClassMapGenerator if (is_string($path)) { if (is_file($path)) { $path = array(new \SplFileInfo($path)); - } elseif (is_dir($path)) { + } elseif (is_dir($path) || strpos($path, '*') !== false) { $path = Finder::create()->files()->followLinks()->name('/\.(php|inc|hh)$/')->in($path); } else { throw new \RuntimeException( From 863e7076baff02b9de46718a77fce77364db7f65 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 May 2020 18:03:49 +0200 Subject: [PATCH 142/193] Bump to use composer/semver 3.x --- composer.json | 2 +- composer.lock | 12 ++++++------ .../Composer/Test/DependencyResolver/SolverTest.php | 4 ++-- .../Test/Repository/FilterRepositoryTest.php | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/composer.json b/composer.json index 5b29439db..6a89758a4 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require": { "php": "^5.3.2 || ^7.0", "composer/ca-bundle": "^1.0", - "composer/semver": "^2.1@dev", + "composer/semver": "^3.0@dev", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", diff --git a/composer.lock b/composer.lock index d7fd5fb0f..e9fa2fea3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "aaa28d3c716a6b5d973347f4046f4931", + "content-hash": "b596d49d8047528b59baeab735f1f98f", "packages": [ { "name": "composer/ca-bundle", @@ -83,12 +83,12 @@ "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "7401fc3628694736b1cac8ec8d5c7dfdcfe54ea6" + "reference": "e28fa06aecbe17194fed4c58ee1de18190892677" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/7401fc3628694736b1cac8ec8d5c7dfdcfe54ea6", - "reference": "7401fc3628694736b1cac8ec8d5c7dfdcfe54ea6", + "url": "https://api.github.com/repos/composer/semver/zipball/e28fa06aecbe17194fed4c58ee1de18190892677", + "reference": "e28fa06aecbe17194fed4c58ee1de18190892677", "shasum": "" }, "require": { @@ -101,7 +101,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.x-dev" + "dev-master": "3.x-dev" } }, "autoload": { @@ -156,7 +156,7 @@ "type": "tidelift" } ], - "time": "2020-05-21T14:08:19+00:00" + "time": "2020-05-26T12:57:06+00:00" }, { "name": "composer/spdx-licenses", diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index b7d0e7bc1..5201bf6d5 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -25,7 +25,7 @@ use Composer\Repository\InstalledArrayRepository; use Composer\Repository\RepositorySet; use Composer\Test\TestCase; use Composer\Semver\Constraint\MultiConstraint; -use Composer\Semver\Constraint\EmptyConstraint; +use Composer\Semver\Constraint\MatchNoneConstraint; class SolverTest extends TestCase { @@ -379,7 +379,7 @@ class SolverTest extends TestCase { $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageB->setReplaces(array('a' => new Link('B', 'A', new EmptyConstraint()))); + $packageB->setReplaces(array('a' => new Link('B', 'A', new MatchNoneConstraint()))); $this->reposComplete(); diff --git a/tests/Composer/Test/Repository/FilterRepositoryTest.php b/tests/Composer/Test/Repository/FilterRepositoryTest.php index 2973e445d..b026a2e2b 100644 --- a/tests/Composer/Test/Repository/FilterRepositoryTest.php +++ b/tests/Composer/Test/Repository/FilterRepositoryTest.php @@ -15,7 +15,7 @@ namespace Composer\Test\Repository; use Composer\Test\TestCase; use Composer\Repository\FilterRepository; use Composer\Repository\ArrayRepository; -use Composer\Semver\Constraint\EmptyConstraint; +use Composer\Semver\Constraint\MatchNoneConstraint; use Composer\Package\BasePackage; class FilterRepositoryTest extends TestCase @@ -54,7 +54,7 @@ class FilterRepositoryTest extends TestCase public function testCanonicalDefaultTrue() { $repo = new FilterRepository($this->arrayRepo, array()); - $result = $repo->loadPackages(array('foo/aaa' => new EmptyConstraint), BasePackage::$stabilities, array()); + $result = $repo->loadPackages(array('foo/aaa' => new MatchNoneConstraint), BasePackage::$stabilities, array()); $this->assertCount(1, $result['packages']); $this->assertCount(1, $result['namesFound']); } @@ -62,7 +62,7 @@ class FilterRepositoryTest extends TestCase public function testNonCanonical() { $repo = new FilterRepository($this->arrayRepo, array('canonical' => false)); - $result = $repo->loadPackages(array('foo/aaa' => new EmptyConstraint), BasePackage::$stabilities, array()); + $result = $repo->loadPackages(array('foo/aaa' => new MatchNoneConstraint), BasePackage::$stabilities, array()); $this->assertCount(1, $result['packages']); $this->assertCount(0, $result['namesFound']); } From b72ac2d01a6918a1ddcea18183648e876dff050e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 May 2020 19:55:40 +0200 Subject: [PATCH 143/193] Oops --- tests/Composer/Test/DependencyResolver/SolverTest.php | 4 ++-- tests/Composer/Test/Repository/FilterRepositoryTest.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Composer/Test/DependencyResolver/SolverTest.php b/tests/Composer/Test/DependencyResolver/SolverTest.php index 5201bf6d5..6fa7f1f84 100644 --- a/tests/Composer/Test/DependencyResolver/SolverTest.php +++ b/tests/Composer/Test/DependencyResolver/SolverTest.php @@ -25,7 +25,7 @@ use Composer\Repository\InstalledArrayRepository; use Composer\Repository\RepositorySet; use Composer\Test\TestCase; use Composer\Semver\Constraint\MultiConstraint; -use Composer\Semver\Constraint\MatchNoneConstraint; +use Composer\Semver\Constraint\MatchAllConstraint; class SolverTest extends TestCase { @@ -379,7 +379,7 @@ class SolverTest extends TestCase { $this->repoLocked->addPackage($packageA = $this->getPackage('A', '1.0')); $this->repo->addPackage($packageB = $this->getPackage('B', '1.0')); - $packageB->setReplaces(array('a' => new Link('B', 'A', new MatchNoneConstraint()))); + $packageB->setReplaces(array('a' => new Link('B', 'A', new MatchAllConstraint()))); $this->reposComplete(); diff --git a/tests/Composer/Test/Repository/FilterRepositoryTest.php b/tests/Composer/Test/Repository/FilterRepositoryTest.php index b026a2e2b..a4e52f4ca 100644 --- a/tests/Composer/Test/Repository/FilterRepositoryTest.php +++ b/tests/Composer/Test/Repository/FilterRepositoryTest.php @@ -15,7 +15,7 @@ namespace Composer\Test\Repository; use Composer\Test\TestCase; use Composer\Repository\FilterRepository; use Composer\Repository\ArrayRepository; -use Composer\Semver\Constraint\MatchNoneConstraint; +use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Package\BasePackage; class FilterRepositoryTest extends TestCase @@ -54,7 +54,7 @@ class FilterRepositoryTest extends TestCase public function testCanonicalDefaultTrue() { $repo = new FilterRepository($this->arrayRepo, array()); - $result = $repo->loadPackages(array('foo/aaa' => new MatchNoneConstraint), BasePackage::$stabilities, array()); + $result = $repo->loadPackages(array('foo/aaa' => new MatchAllConstraint), BasePackage::$stabilities, array()); $this->assertCount(1, $result['packages']); $this->assertCount(1, $result['namesFound']); } @@ -62,7 +62,7 @@ class FilterRepositoryTest extends TestCase public function testNonCanonical() { $repo = new FilterRepository($this->arrayRepo, array('canonical' => false)); - $result = $repo->loadPackages(array('foo/aaa' => new MatchNoneConstraint), BasePackage::$stabilities, array()); + $result = $repo->loadPackages(array('foo/aaa' => new MatchAllConstraint), BasePackage::$stabilities, array()); $this->assertCount(1, $result['packages']); $this->assertCount(0, $result['namesFound']); } From 628730ba349d68caa013ecc35bb2f67bcd02d062 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 May 2020 20:16:52 +0200 Subject: [PATCH 144/193] Run builds with composer snapshots --- .github/workflows/continuous-integration.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6da8f1ead..d322bd937 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -67,6 +67,9 @@ jobs: experimental: true steps: + - name: "Update to latest Composer snapshot" + run: "composer self-update --snapshot" + - name: "Checkout" uses: "actions/checkout@v2" From 42d760fb5198f9fd699fd7c6904159e2ac0a108d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 May 2020 20:24:48 +0200 Subject: [PATCH 145/193] Make the Composer self-update succeed by using sudo --- .github/workflows/continuous-integration.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index d322bd937..41aea833f 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -68,6 +68,11 @@ jobs: steps: - name: "Update to latest Composer snapshot" + if: "!startsWith(matrix.os, 'windows')" + run: "sudo composer self-update --snapshot" + + - name: "Update to latest Composer snapshot on Windows" + if: "startsWith(matrix.os, 'windows')" run: "composer self-update --snapshot" - name: "Checkout" From 59f9633244c67536d0409191750bf1b78539c4a0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 May 2020 20:26:58 +0200 Subject: [PATCH 146/193] Again.. --- .github/workflows/continuous-integration.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 41aea833f..e4b26acc8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -67,14 +67,6 @@ jobs: experimental: true steps: - - name: "Update to latest Composer snapshot" - if: "!startsWith(matrix.os, 'windows')" - run: "sudo composer self-update --snapshot" - - - name: "Update to latest Composer snapshot on Windows" - if: "startsWith(matrix.os, 'windows')" - run: "composer self-update --snapshot" - - name: "Checkout" uses: "actions/checkout@v2" @@ -110,6 +102,14 @@ jobs: ini-values: "memory_limit=-1, phar.readonly=0" php-version: "${{ matrix.php-version }}" + - name: "Update to latest Composer snapshot" + if: "!startsWith(matrix.os, 'windows')" + run: "sudo composer self-update --snapshot" + + - name: "Update to latest Composer snapshot on Windows" + if: "startsWith(matrix.os, 'windows')" + run: "composer self-update --snapshot" + - name: "Determine composer cache directory" id: "determine-composer-cache-directory" run: "echo \"::set-output name=directory::$(composer config cache-dir)\"" From e8acd60245c2a53b9afe1034448464a4a9c4aa91 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 May 2020 21:34:56 +0200 Subject: [PATCH 147/193] Fix sudo command --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index e4b26acc8..f51179160 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -104,7 +104,7 @@ jobs: - name: "Update to latest Composer snapshot" if: "!startsWith(matrix.os, 'windows')" - run: "sudo composer self-update --snapshot" + run: "sudo -i composer self-update --snapshot" - name: "Update to latest Composer snapshot on Windows" if: "startsWith(matrix.os, 'windows')" From 0a3d87512dd16cb6bc22c140f387e70e9bd8b9a3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 May 2020 21:57:40 +0200 Subject: [PATCH 148/193] Update deps --- composer.json | 2 +- composer.lock | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/composer.json b/composer.json index 6a89758a4..fd79e256b 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require": { "php": "^5.3.2 || ^7.0", "composer/ca-bundle": "^1.0", - "composer/semver": "^3.0@dev", + "composer/semver": "^3.0", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", diff --git a/composer.lock b/composer.lock index e9fa2fea3..6a919ca4e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b596d49d8047528b59baeab735f1f98f", + "content-hash": "31f6c4b219ba3639c55066a1d1e0804e", "packages": [ { "name": "composer/ca-bundle", @@ -79,16 +79,16 @@ }, { "name": "composer/semver", - "version": "dev-master", + "version": "3.0.0", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "e28fa06aecbe17194fed4c58ee1de18190892677" + "reference": "3426bd5efa8a12d230824536c42a8a4ad30b7940" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/e28fa06aecbe17194fed4c58ee1de18190892677", - "reference": "e28fa06aecbe17194fed4c58ee1de18190892677", + "url": "https://api.github.com/repos/composer/semver/zipball/3426bd5efa8a12d230824536c42a8a4ad30b7940", + "reference": "3426bd5efa8a12d230824536c42a8a4ad30b7940", "shasum": "" }, "require": { @@ -140,7 +140,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/master" + "source": "https://github.com/composer/semver/tree/3.0.0" }, "funding": [ { @@ -156,7 +156,7 @@ "type": "tidelift" } ], - "time": "2020-05-26T12:57:06+00:00" + "time": "2020-05-26T18:22:04+00:00" }, { "name": "composer/spdx-licenses", @@ -280,16 +280,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "5.2.9", + "version": "5.2.10", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4" + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", "shasum": "" }, "require": { @@ -344,9 +344,9 @@ ], "support": { "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.9" + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10" }, - "time": "2019-09-25T14:49:45+00:00" + "time": "2020-05-27T16:41:55+00:00" }, { "name": "psr/log", @@ -1492,9 +1492,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "composer/semver": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From f7abfc9d7fb8437f1dee87f00c037d13279a6d16 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 May 2020 22:07:55 +0200 Subject: [PATCH 149/193] Update deps --- composer.json | 2 +- composer.lock | 65 +++++++++++++++++++++++++++++---------------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/composer.json b/composer.json index e588cb71e..4fdbcb743 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ "composer/semver": "^1.0", "composer/spdx-licenses": "^1.2", "composer/xdebug-handler": "^1.1", - "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0", + "justinrainbow/json-schema": "^5.2.10", "psr/log": "^1.0", "seld/jsonlint": "^1.4", "seld/phar-utils": "^1.0", diff --git a/composer.lock b/composer.lock index aa67fc010..2fd85b33e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cb76dc8e228d462d248c09f0051da364", + "content-hash": "281c2bedcde245309c1f9c884f65e1a1", "packages": [ { "name": "composer/ca-bundle", @@ -60,6 +60,11 @@ "ssl", "tls" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.2.7" + }, "funding": [ { "url": "https://packagist.com", @@ -191,6 +196,11 @@ "spdx", "validator" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.3" + }, "time": "2020-02-14T07:44:31+00:00" }, { @@ -235,6 +245,11 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/master" + }, "funding": [ { "url": "https://packagist.com", @@ -245,16 +260,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "5.2.9", + "version": "5.2.10", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4" + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4", - "reference": "44c6787311242a979fa15c704327c20e7221a0e4", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", + "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b", "shasum": "" }, "require": { @@ -307,11 +322,7 @@ "json", "schema" ], - "support": { - "issues": "https://github.com/justinrainbow/json-schema/issues", - "source": "https://github.com/justinrainbow/json-schema/tree/5.2.9" - }, - "time": "2019-09-25T14:49:45+00:00" + "time": "2020-05-27T16:41:55+00:00" }, { "name": "psr/log", @@ -468,6 +479,10 @@ "keywords": [ "phar" ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.1.0" + }, "time": "2020-02-14T15:25:33+00:00" }, { @@ -701,16 +716,16 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e94c8b1bbe2bc77507a1056cdb06451c75b427f9", + "reference": "e94c8b1bbe2bc77507a1056cdb06451c75b427f9", "shasum": "" }, "require": { @@ -722,7 +737,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -755,9 +770,6 @@ "polyfill", "portable" ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.15.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -772,20 +784,20 @@ "type": "tidelift" } ], - "time": "2020-02-27T09:26:54+00:00" + "time": "2020-05-12T16:14:59+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fa79b11539418b02fc5e1897267673ba2c19419c", + "reference": "fa79b11539418b02fc5e1897267673ba2c19419c", "shasum": "" }, "require": { @@ -797,7 +809,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.15-dev" + "dev-master": "1.17-dev" } }, "autoload": { @@ -831,9 +843,6 @@ "portable", "shim" ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.15.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -848,7 +857,7 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2020-05-12T16:47:27+00:00" }, { "name": "symfony/process", From 5ab0ecda04a384485c345ee2211c17ceb84e5fb6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 May 2020 22:12:12 +0200 Subject: [PATCH 150/193] Try disabling platform-check for PHPUnitBridge on php8 --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f51179160..7081d812c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -156,6 +156,7 @@ jobs: if: "matrix.php-version == '8.0'" run: | php7.4 vendor/bin/simple-phpunit install + composer config -g platform-check false - name: "Run tests" if: "matrix.php-version != '7.3'" From dce9b129bb10974ed556fc6cf838af92298ee3c7 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 28 May 2020 09:37:22 +0200 Subject: [PATCH 151/193] Use PUB 5.1+ for php8 builds --- .github/workflows/continuous-integration.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 7081d812c..f0ef762b6 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -70,15 +70,6 @@ jobs: - name: "Checkout" uses: "actions/checkout@v2" - - name: "Install PHP 7.4 to prepare nightly builds" - if: "matrix.php-version == '8.0'" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "intl" - ini-values: "memory_limit=-1, phar.readonly=0" - php-version: "7.4" - - name: "Choose PHPUnit version" if: "!startsWith(matrix.os, 'windows')" run: | @@ -152,11 +143,10 @@ jobs: - name: "Prepare git environment" run: "git config --global user.name composer && git config --global user.email composer@example.com" - - name: "Pre-install PHPUnit using PHP 7.4 for PHP 8" + - name: "Require latest PHPUnitBridge for PHP 8" if: "matrix.php-version == '8.0'" run: | - php7.4 vendor/bin/simple-phpunit install - composer config -g platform-check false + composer require --dev symfony/phpunit-bridge:^5.1.0-RC2 - name: "Run tests" if: "matrix.php-version != '7.3'" From 38557b246008c4c18a25b78d0f99772433129b9b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 28 May 2020 09:49:41 +0200 Subject: [PATCH 152/193] Reorder build steps --- .github/workflows/continuous-integration.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index f0ef762b6..098ab9467 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -131,6 +131,11 @@ jobs: if: "matrix.dependencies == 'locked'" run: "composer install ${{ env.COMPOSER_FLAGS }}" + - name: "Require latest PHPUnitBridge for PHP 8" + if: "matrix.php-version == '8.0'" + run: | + composer require --no-update --dev symfony/phpunit-bridge:^5.1.0-RC2 + - name: "Update Symfony's PHPUnitBridge to latest available for the current PHP always as it is not really a dependency of the project" run: "composer update ${{ env.COMPOSER_FLAGS }} symfony/phpunit-bridge" @@ -143,11 +148,6 @@ jobs: - name: "Prepare git environment" run: "git config --global user.name composer && git config --global user.email composer@example.com" - - name: "Require latest PHPUnitBridge for PHP 8" - if: "matrix.php-version == '8.0'" - run: | - composer require --dev symfony/phpunit-bridge:^5.1.0-RC2 - - name: "Run tests" if: "matrix.php-version != '7.3'" run: "vendor/bin/simple-phpunit" From ce52c408f04935c9f1786f52e2b28b21a5ce5aad Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 28 May 2020 10:15:38 +0200 Subject: [PATCH 153/193] Disable platform checks for php8 --- .github/workflows/continuous-integration.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 098ab9467..1ad131739 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -135,6 +135,7 @@ jobs: if: "matrix.php-version == '8.0'" run: | composer require --no-update --dev symfony/phpunit-bridge:^5.1.0-RC2 + composer config -g platform-check false - name: "Update Symfony's PHPUnitBridge to latest available for the current PHP always as it is not really a dependency of the project" run: "composer update ${{ env.COMPOSER_FLAGS }} symfony/phpunit-bridge" From 0a0291e913c8d71d37bb825c0400128a0f5a3e20 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 28 May 2020 10:31:46 +0200 Subject: [PATCH 154/193] Remove deprecated flag --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 1ad131739..35e2f04ea 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -9,7 +9,7 @@ on: - 'doc/**' env: - COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" COMPOSER_UPDATE_FLAGS: "" SYMFONY_PHPUNIT_VERSION: "8.3" SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1" From 21e708f2c47a5c11b38d8b9d74fa7abe48419857 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Jun 2020 09:39:44 +0200 Subject: [PATCH 155/193] Remove upper bound PHP version checks from platform-check as there is not enough value added and it risks causing issues --- src/Composer/Autoload/AutoloadGenerator.php | 33 +++++++++---------- .../Test/Autoload/AutoloadGeneratorTest.php | 10 ++++-- .../autoload_real_files_by_dependency.php | 2 -- .../Fixtures/autoload_real_functions.php | 2 -- ...load_real_functions_with_include_paths.php | 2 -- ...emoved_include_paths_and_autolad_files.php | 2 -- .../Fixtures/autoload_real_include_path.php | 2 -- .../Fixtures/autoload_real_target_dir.php | 2 -- .../platform/no_extensions_required.php | 4 +-- .../Fixtures/platform/no_php_lower_bound.php | 14 -------- .../platform/specific_php_release.php | 4 +-- .../Autoload/Fixtures/platform/typical.php | 4 +-- 12 files changed, 29 insertions(+), 52 deletions(-) delete mode 100644 tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 91bf5ba7e..ef3d54573 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -316,7 +316,13 @@ EOF; $filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); $checkPlatform = $config->get('platform-check'); if ($checkPlatform) { - $filesystem->filePutContentsIfModified($targetDir.'/platform_check.php', $this->getPlatformCheck($packageMap)); + $platformCheckContent = $this->getPlatformCheck($packageMap); + if (null === $platformCheckContent) { + $checkPlatform = false; + } + } + if ($checkPlatform) { + $filesystem->filePutContentsIfModified($targetDir.'/platform_check.php', $platformCheckContent); } elseif (file_exists($targetDir.'/platform_check.php')) { unlink($targetDir.'/platform_check.php'); } @@ -572,7 +578,6 @@ EOF; protected function getPlatformCheck($packageMap) { $lowestPhpVersion = Bound::zero(); - $highestPhpVersion = Bound::positiveInfinity(); $requiredExtensions = array(); $extensionProviders = array(); @@ -592,9 +597,6 @@ EOF; if ($constraint->getLowerBound()->compareTo($lowestPhpVersion, '>')) { $lowestPhpVersion = $constraint->getLowerBound(); } - if ($constraint->getUpperBound()->compareTo($highestPhpVersion, '<')) { - $highestPhpVersion = $constraint->getUpperBound(); - } } if (preg_match('{^ext-(.+)$}iD', $link->getTarget(), $match)) { @@ -648,22 +650,15 @@ EOF; return implode('.', $chunks); }; - $requiredPhp = array(); - $requiredPhpError = array(); + $requiredPhp = ''; + $requiredPhpError = ''; if (!$lowestPhpVersion->isZero()) { $operator = $lowestPhpVersion->isInclusive() ? '>=' : '>'; - $requiredPhp[] = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($lowestPhpVersion); - $requiredPhpError[] = '"'.$operator.' '.$formatToHumanReadable($lowestPhpVersion).'"'; - } - if (!$highestPhpVersion->isPositiveInfinity()) { - $operator = $highestPhpVersion->isInclusive() ? '<=' : '<'; - $requiredPhp[] = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($highestPhpVersion); - $requiredPhpError[] = '"'.$operator.' '.$formatToHumanReadable($highestPhpVersion).'"'; + $requiredPhp = 'PHP_VERSION_ID '.$operator.' '.$formatToPhpVersionId($lowestPhpVersion); + $requiredPhpError = '"'.$operator.' '.$formatToHumanReadable($lowestPhpVersion).'"'; } if ($requiredPhp) { - $requiredPhp = implode(' && ', $requiredPhp); - $requiredPhpError = implode(' and ', $requiredPhpError); $requiredPhp = <<generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); - $this->assertFileContentEquals(__DIR__ . '/Fixtures/platform/' . $expectedFixture . '.php', $this->vendorDir . '/composer/platform_check.php'); + if (null === $expectedFixture) { + $this->assertFalse(file_exists($this->vendorDir . '/composer/platform_check.php')); + $this->assertNotContains("require __DIR__ . '/platform_check.php';", file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + } else { + $this->assertFileContentEquals(__DIR__ . '/Fixtures/platform/' . $expectedFixture . '.php', $this->vendorDir . '/composer/platform_check.php'); + $this->assertContains("require __DIR__ . '/platform_check.php';", file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + } } public function platformCheckProvider() @@ -1687,7 +1693,7 @@ EOF; array( new Link('a', 'php', $versionParser->parseConstraints('< 8')), ), - 'no_php_lower_bound' + null ), 'No PHP upper bound' => array( array( 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 ab3dd2758..fdff2bd42 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 @@ -22,8 +22,6 @@ class ComposerAutoloaderInitFilesAutoloadOrder return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader')); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index 27dc60bf6..67ce8ffe2 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -22,8 +22,6 @@ class ComposerAutoloaderInitFilesAutoload return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); 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 be7dd7907..790029096 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 @@ -22,8 +22,6 @@ class ComposerAutoloaderInitFilesAutoload return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); 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 c3617696b..7b7898df1 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 @@ -22,8 +22,6 @@ class ComposerAutoloaderInitFilesAutoload return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); 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 7639b4336..2747dbc0a 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -22,8 +22,6 @@ class ComposerAutoloaderInitIncludePath return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader')); 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 0d9685d71..8ec8cdd47 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -22,8 +22,6 @@ class ComposerAutoloaderInitTargetDir return self::$loader; } - require __DIR__ . '/platform_check.php'; - spl_autoload_register(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader'), true, true); self::$loader = $loader = new \Composer\Autoload\ClassLoader(); spl_autoload_unregister(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader')); diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php index 1dbfcd455..85a922d49 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/no_extensions_required.php @@ -4,8 +4,8 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 80000)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID >= 70200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; } if ($issues) { diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php b/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php deleted file mode 100644 index 373d93ff5..000000000 --- a/tests/Composer/Test/Autoload/Fixtures/platform/no_php_lower_bound.php +++ /dev/null @@ -1,14 +0,0 @@ -= 70208 && PHP_VERSION_ID < 80000)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.8" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID >= 70208)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.8". You are running ' . PHP_VERSION . '.'; } if ($issues) { diff --git a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php index 728e68b63..dde12ec46 100644 --- a/tests/Composer/Test/Autoload/Fixtures/platform/typical.php +++ b/tests/Composer/Test/Autoload/Fixtures/platform/typical.php @@ -4,8 +4,8 @@ $issues = array(); -if (!(PHP_VERSION_ID >= 70200 && PHP_VERSION_ID < 80000)) { - $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0" and "< 8.0.0". You are running ' . PHP_VERSION . '.'; +if (!(PHP_VERSION_ID >= 70200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.0". You are running ' . PHP_VERSION . '.'; } $missingExtensions = array(); From e85da00dff099370ef6a367ca836461a26243573 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Jun 2020 15:43:24 +0200 Subject: [PATCH 156/193] Add a way to ignore only some packages in --ignore-platform-reqs, and make the platform check ignore those packages which were ignored as requirements, fixes #8861 --- doc/03-cli.md | 24 ++++++++++--- src/Composer/Autoload/AutoloadGenerator.php | 35 +++++++++++++++++-- src/Composer/Command/CreateProjectCommand.php | 19 +++++----- src/Composer/Command/DumpAutoloadCommand.php | 6 ++++ src/Composer/Command/InitCommand.php | 20 ++++++----- src/Composer/Command/InstallCommand.php | 8 +++-- src/Composer/Command/RemoveCommand.php | 8 +++-- src/Composer/Command/RequireCommand.php | 10 ++++-- src/Composer/Command/UpdateCommand.php | 8 +++-- .../DependencyResolver/RuleSetGenerator.php | 9 +++-- src/Composer/DependencyResolver/Solver.php | 12 +++---- .../SolverProblemsException.php | 2 +- src/Composer/Installer.php | 17 +++++++-- .../Package/Version/VersionSelector.php | 12 +++---- tests/Composer/Test/InstallerTest.php | 12 +++++-- 15 files changed, 146 insertions(+), 56 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 161f2ee69..a29098249 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -114,7 +114,9 @@ resolution. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. ## update / u @@ -187,7 +189,9 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.* * **--apcu-autoloader:** Use APCu to cache found/not-found classes. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -233,7 +237,9 @@ If you do not specify a package, composer will prompt you to search for a packag * **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -269,7 +275,9 @@ uninstalled. * **--update-with-dependencies:** Also update dependencies of the removed packages. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. See also the [`platform`](06-config.md#platform) config option. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. @@ -724,7 +732,9 @@ By default the command checks for the packages on packagist.org. * **--no-install:** Disables installation of the vendors. * **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` requirements and force the installation even if the local machine does not - fulfill these. + fulfill these. You can also ignore specific packages only using + `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. ## dump-autoload (dumpautoload) @@ -748,6 +758,10 @@ performance. Implicitly enables `--optimize`. * **--apcu:** Use APCu to cache found/not-found classes. * **--no-dev:** Disables autoload-dev rules. +* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` + requirements and skip the [platform check](07-runtime.md#platform-check) for these. + You can also ignore specific packages only using `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. + See also the [`platform`](06-config.md#platform) config option. ## clear-cache / clearcache / cc diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index ef3d54573..b7c5cee8b 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -61,6 +61,11 @@ class AutoloadGenerator */ private $runScripts = false; + /** + * @var bool|array + */ + private $ignorePlatformReqs = false; + public function __construct(EventDispatcher $eventDispatcher, IOInterface $io = null) { $this->eventDispatcher = $eventDispatcher; @@ -103,6 +108,26 @@ class AutoloadGenerator $this->runScripts = (bool) $runScripts; } + /** + * Sets whether platform requirements should be ignored + * + * If this is set to true, the platform check file will not be generated + * If this is set to false, the platform check file will be generated with all requirements + * If this is set to string[], those packages will be ignored from the platform check file + * + * @param array|bool $ignorePlatformReqs + */ + public function setIgnorePlatformRequirements($ignorePlatformReqs) + { + if (is_array($ignorePlatformReqs)) { + $this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) { + return (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); + }); + } else { + $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + } + } + public function dump(Config $config, InstalledRepositoryInterface $localRepo, PackageInterface $mainPackage, InstallationManager $installationManager, $targetDir, $scanPsrPackages = false, $suffix = '') { if ($this->classMapAuthoritative) { @@ -314,9 +339,9 @@ EOF; unlink($includeFilesFilePath); } $filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); - $checkPlatform = $config->get('platform-check'); + $checkPlatform = $config->get('platform-check') && $this->ignorePlatformReqs !== true; if ($checkPlatform) { - $platformCheckContent = $this->getPlatformCheck($packageMap); + $platformCheckContent = $this->getPlatformCheck($packageMap, $this->ignorePlatformReqs ?: array()); if (null === $platformCheckContent) { $checkPlatform = false; } @@ -575,7 +600,7 @@ EOF; return $baseDir . (($path !== false) ? var_export($path, true) : ""); } - protected function getPlatformCheck($packageMap) + protected function getPlatformCheck($packageMap, array $ignorePlatformReqs) { $lowestPhpVersion = Bound::zero(); $requiredExtensions = array(); @@ -593,6 +618,10 @@ EOF; foreach ($packageMap as $item) { list($package, $installPath) = $item; foreach ($package->getRequires() as $link) { + if (in_array($link->getTarget(), $ignorePlatformReqs, true)) { + continue; + } + if ('php' === $link->getTarget() && ($constraint = $link->getConstraint())) { if ($constraint->getLowerBound()->compareTo($lowestPhpVersion, '>')) { $lowestPhpVersion = $constraint->getLowerBound(); diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 7943e01ac..1960fe1ef 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -80,7 +80,7 @@ class CreateProjectCommand extends BaseCommand new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deleting the vcs folder.'), new InputOption('remove-vcs', null, InputOption::VALUE_NONE, 'Whether to force deletion of the vcs folder without prompting.'), new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), )) ->setHelp( <<setOption('no-plugins', true); } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + return $this->installProject( $io, $config, @@ -143,7 +147,7 @@ EOT $input->getOption('no-scripts'), $input->getOption('no-progress'), $input->getOption('no-install'), - $input->getOption('ignore-platform-reqs'), + $ignorePlatformReqs, !$input->getOption('no-secure-http'), $input->getOption('add-repository') ); @@ -336,19 +340,16 @@ EOT $repositorySet = new RepositorySet($stability); $repositorySet->addRepository($sourceRepo); - $platformRepo = null; - if (!$ignorePlatformReqs) { - $platformOverrides = $config->get('platform') ?: array(); - $platformRepo = new PlatformRepository(array(), $platformOverrides); - } + $platformOverrides = $config->get('platform') ?: array(); + $platformRepo = new PlatformRepository(array(), $platformOverrides); // find the latest version if there are multiple $versionSelector = new VersionSelector($repositorySet, $platformRepo); - $package = $versionSelector->findBestCandidate($name, $packageVersion, $stability); + $package = $versionSelector->findBestCandidate($name, $packageVersion, $stability, $ignorePlatformReqs); if (!$package) { $errorMessage = "Could not find package $name with " . ($packageVersion ? "version $packageVersion" : "stability $stability"); - if ($platformRepo && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) { + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $packageVersion, $stability, true)) { throw new \InvalidArgumentException($errorMessage .' in a version installable using your PHP version, PHP extensions and Composer version.'); } diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index 9627b2a88..240a6c558 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -35,6 +35,7 @@ class DumpAutoloadCommand extends BaseCommand new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'), new InputOption('apcu', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s) from the platform check.'), )) ->setHelp( <<getIO()->write('Generating autoload files'); } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $generator = $composer->getAutoloadGenerator(); $generator->setDevMode(!$input->getOption('no-dev')); $generator->setClassMapAuthoritative($authoritative); $generator->setApcu($apcu); $generator->setRunScripts(!$input->getOption('no-scripts')); + $generator->setIgnorePlatformRequirements($ignorePlatformReqs); $numberOfClasses = $generator->dump($config, $localRepo, $package, $installationManager, 'composer', $optimize); if ($authoritative) { diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 7f00daa5f..3acad2fe5 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -723,26 +723,28 @@ EOT */ private function findBestVersionAndNameForPackage(InputInterface $input, $name, PlatformRepository $platformRepo = null, $preferredStability = 'stable', $requiredVersion = null, $minimumStability = null, $fixed = null) { - // ignore platform repo if platform requirements are ignored - $ignorePlatformReqs = $input->hasOption('ignore-platform-reqs') && $input->getOption('ignore-platform-reqs'); - if ($ignorePlatformReqs) { - $platformRepo = null; + // handle ignore-platform-reqs flag if present + $ignorePlatformReqs = false; + if ($input->hasOption('ignore-platform-reqs')) { + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; } // find the latest version allowed in this repo set $versionSelector = new VersionSelector($this->getRepositorySet($input, $minimumStability), $platformRepo); - $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability); + $package = $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, $ignorePlatformReqs); if (!$package) { // platform packages can not be found in the pool in versions other than the local platform's has // so if platform reqs are ignored we just take the user's word for it - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($name, $ignorePlatformReqs))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) { return array($name, $requiredVersion ?: '*'); } // Check whether the PHP version was the problem - if ($platformRepo && $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true)) { + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, $requiredVersion, $preferredStability, true)) { throw new \InvalidArgumentException(sprintf( 'Package %s at version %s has a PHP requirement incompatible with your PHP version, PHP extensions and Composer version', $name, @@ -750,7 +752,7 @@ EOT )); } // Check whether the required version was the problem - if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $preferredStability)) { + if ($requiredVersion && $versionSelector->findBestCandidate($name, null, $preferredStability, $ignorePlatformReqs)) { throw new \InvalidArgumentException(sprintf( 'Could not find package %s in a version matching %s', $name, @@ -758,7 +760,7 @@ EOT )); } // Check whether the PHP version was the problem for all versions - if ($platformRepo && $versionSelector->findBestCandidate($name, null, $preferredStability, true)) { + if (true !== $ignorePlatformReqs && $versionSelector->findBestCandidate($name, null, $preferredStability, true)) { throw new \InvalidArgumentException(sprintf( 'Could not find package %s in any version matching your PHP version, PHP extensions and Composer version', $name diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 5f5565248..4be349819 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -49,7 +49,7 @@ class InstallCommand extends BaseCommand new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp( @@ -103,6 +103,10 @@ EOT $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); $apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -114,7 +118,7 @@ EOT ->setOptimizeAutoloader($optimize) ->setClassMapAuthoritative($authoritative) ->setApcuAutoloader($apcu) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ; if ($input->getOption('no-plugins')) { diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index 3d6c56df5..ef77391a9 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -50,7 +50,7 @@ class RemoveCommand extends BaseCommand new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'), new InputOption('unused', null, InputOption::VALUE_NONE, 'Remove all packages which are locked but not required by any other package.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), @@ -238,6 +238,10 @@ EOT $io->writeError('Running composer update '.implode(' ', $packages).$flags); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setVerbose($input->getOption('verbose')) ->setDevMode($updateDevMode) @@ -248,7 +252,7 @@ EOT ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setRunScripts(!$input->getOption('no-scripts')) ->setDryRun($dryRun) ; diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 250e99513..69bf1c528 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -69,7 +69,7 @@ class RequireCommand extends InitCommand new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-dependencies'), new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'), @@ -181,7 +181,7 @@ EOT $input, $output, $input->getArgument('packages'), - $input->getOption('ignore-platform-reqs') ? null : $platformRepo, + $platformRepo, $preferredStability, !$input->getOption('no-update'), $input->getOption('fixed') @@ -287,6 +287,10 @@ EOT $install = Installer::create($io, $composer); + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -300,7 +304,7 @@ EOT ->setUpdate(true) ->setInstall(!$input->getOption('no-install')) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 3ca704620..2329d1c24 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -60,7 +60,7 @@ class UpdateCommand extends BaseCommand new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore platform requirements (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'), @@ -196,6 +196,10 @@ EOT $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $install ->setDryRun($input->getOption('dry-run')) ->setVerbose($input->getOption('verbose')) @@ -212,7 +216,7 @@ EOT ->setUpdateMirrors($updateMirrors) ->setUpdateAllowList($packages) ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) + ->setIgnorePlatformRequirements($ignorePlatformReqs) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) ; diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 02c3fd598..ffb88d56c 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -169,7 +169,7 @@ class RuleSetGenerator } foreach ($package->getRequires() as $link) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { continue; } @@ -193,7 +193,7 @@ class RuleSetGenerator continue; } - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($link->getTarget(), $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) { continue; } @@ -253,7 +253,7 @@ class RuleSetGenerator } foreach ($request->getRequires() as $packageName => $constraint) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { continue; } @@ -272,6 +272,9 @@ class RuleSetGenerator } } + /** + * @param bool|array $ignorePlatformReqs + */ public function getRulesFor(Request $request, $ignorePlatformReqs = false) { $this->rules = new RuleSet; diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index d0f90dc59..7c2d3a00c 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -164,13 +164,13 @@ class Solver } /** - * @param Request $request - * @param bool $ignorePlatformReqs + * @param Request $request + * @param bool|array $ignorePlatformReqs */ - protected function checkForRootRequireProblems($request, $ignorePlatformReqs) + protected function checkForRootRequireProblems(Request $request, $ignorePlatformReqs) { foreach ($request->getRequires() as $packageName => $constraint) { - if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { + if ((true === $ignorePlatformReqs || (is_array($ignorePlatformReqs) && in_array($packageName, $ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $packageName)) { continue; } @@ -183,8 +183,8 @@ class Solver } /** - * @param Request $request - * @param bool $ignorePlatformReqs + * @param Request $request + * @param bool|array $ignorePlatformReqs * @return LockTransaction */ public function solve(Request $request, $ignorePlatformReqs = false) diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index e6697fb05..dd1cb48b4 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -74,7 +74,7 @@ class SolverProblemsException extends \RuntimeException // TODO remove before 2.0 final if (!class_exists('PHPUnit\Framework\TestCase', false)) { if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) { - $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs, but this will also ignore your PHP version and may result in bigger problems down the line."; + $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs=composer-plugin-api, but this may result in broken plugins and bigger problems down the line."; } else { $hints[] = "You are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2."; } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 2bca0618b..23421f666 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -308,6 +308,7 @@ class Installer $this->autoloadGenerator->setClassMapAuthoritative($this->classMapAuthoritative); $this->autoloadGenerator->setApcu($this->apcuAutoloader); $this->autoloadGenerator->setRunScripts($this->runScripts); + $this->autoloadGenerator->setIgnorePlatformRequirements($this->ignorePlatformReqs); $this->autoloadGenerator->dump($this->config, $localRepo, $this->package, $this->installationManager, 'composer', $this->optimizeAutoloader); } @@ -737,7 +738,7 @@ class Installer $rootRequires = array(); foreach ($requires as $req => $constraint) { // skip platform requirements from the root package to avoid filtering out existing platform packages - if ($this->ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { + if ((true === $this->ignorePlatformReqs || (is_array($this->ignorePlatformReqs) && in_array($req, $this->ignorePlatformReqs, true))) && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req)) { continue; } if ($constraint instanceof Link) { @@ -1117,12 +1118,22 @@ class Installer /** * set ignore Platform Package requirements * - * @param bool $ignorePlatformReqs + * If this is set to true, all platform requirements are ignored + * If this is set to false, no platform requirements are ignored + * If this is set to string[], those packages will be ignored + * + * @param bool|array $ignorePlatformReqs * @return Installer */ public function setIgnorePlatformRequirements($ignorePlatformReqs = false) { - $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + if (is_array($ignorePlatformReqs)) { + $this->ignorePlatformReqs = array_filter($ignorePlatformReqs, function ($req) { + return (bool) preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $req); + }); + } else { + $this->ignorePlatformReqs = (bool) $ignorePlatformReqs; + } return $this; } diff --git a/src/Composer/Package/Version/VersionSelector.php b/src/Composer/Package/Version/VersionSelector.php index 82d60dce7..d42c9f167 100644 --- a/src/Composer/Package/Version/VersionSelector.php +++ b/src/Composer/Package/Version/VersionSelector.php @@ -33,7 +33,7 @@ class VersionSelector { private $repositorySet; - private $platformConstraints; + private $platformConstraints = array(); private $parser; @@ -44,7 +44,6 @@ class VersionSelector { $this->repositorySet = $repositorySet; if ($platformRepo) { - $this->platformConstraints = array(); foreach ($platformRepo->getPackages() as $package) { $this->platformConstraints[$package->getName()][] = new Constraint('==', $package->getVersion()); } @@ -58,7 +57,7 @@ class VersionSelector * @param string $packageName * @param string $targetPackageVersion * @param string $preferredStability - * @param bool $ignorePlatformReqs + * @param bool|array $ignorePlatformReqs * @return PackageInterface|false */ public function findBestCandidate($packageName, $targetPackageVersion = null, $preferredStability = 'stable', $ignorePlatformReqs = false) @@ -71,13 +70,14 @@ class VersionSelector $constraint = $targetPackageVersion ? $this->getParser()->parseConstraints($targetPackageVersion) : null; $candidates = $this->repositorySet->findPackages(strtolower($packageName), $constraint); - if ($this->platformConstraints && !$ignorePlatformReqs) { + if ($this->platformConstraints && true !== $ignorePlatformReqs) { $platformConstraints = $this->platformConstraints; - $candidates = array_filter($candidates, function ($pkg) use ($platformConstraints) { + $ignorePlatformReqs = $ignorePlatformReqs ?: array(); + $candidates = array_filter($candidates, function ($pkg) use ($platformConstraints, $ignorePlatformReqs) { $reqs = $pkg->getRequires(); foreach ($reqs as $name => $link) { - if (isset($platformConstraints[$name])) { + if (!in_array($name, $ignorePlatformReqs, true) && isset($platformConstraints[$name])) { foreach ($platformConstraints[$name] as $constraint) { if ($link->getConstraint()->matches($constraint)) { continue 2; diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 78c20a1be..1ba25847c 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -264,10 +264,14 @@ class InstallerTest extends TestCase $application = new Application; $application->get('install')->setCode(function ($input, $output) use ($installer) { + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $installer ->setDevMode(!$input->getOption('no-dev')) ->setDryRun($input->getOption('dry-run')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); + ->setIgnorePlatformRequirements($ignorePlatformReqs); return $installer->run(); }); @@ -287,6 +291,10 @@ class InstallerTest extends TestCase $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') + ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) + : false; + $installer ->setDevMode(!$input->getOption('no-dev')) ->setUpdate(true) @@ -297,7 +305,7 @@ class InstallerTest extends TestCase ->setUpdateAllowTransitiveDependencies($updateAllowTransitiveDependencies) ->setPreferStable($input->getOption('prefer-stable')) ->setPreferLowest($input->getOption('prefer-lowest')) - ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')); + ->setIgnorePlatformRequirements($ignorePlatformReqs); return $installer->run(); }); From a9affa8413b6a968cc726f14c1a03b05d2bb4b94 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Jun 2020 15:58:34 +0200 Subject: [PATCH 157/193] Add missing use statement --- src/Composer/Autoload/AutoloadGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index b7c5cee8b..9ef442ba1 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -19,6 +19,7 @@ use Composer\IO\IOInterface; use Composer\Package\AliasPackage; use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; +use Composer\Repository\PlatformRepository; use Composer\Semver\Constraint\Bound; use Composer\Semver\Constraint\MatchAllConstraint; use Composer\Util\Filesystem; From 537402f814537db84b83f6694f774dd59e1213da Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Jun 2020 16:01:43 +0200 Subject: [PATCH 158/193] Add tests for #8861 --- .../Test/Autoload/AutoloadGeneratorTest.php | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 36c20a08d..c178e93d8 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1648,7 +1648,7 @@ EOF; /** * @dataProvider platformCheckProvider */ - public function testGeneratesPlatformCheck(array $requires, $expectedFixture, array $provides = array(), array $replaces = array()) + public function testGeneratesPlatformCheck(array $requires, $expectedFixture, array $provides = array(), array $replaces = array(), $ignorePlatformReqs = false) { $package = new Package('a', '1.0', '1.0'); $package->setRequires($requires); @@ -1665,6 +1665,7 @@ EOF; ->method('getCanonicalPackages') ->will($this->returnValue(array())); + $this->generator->setIgnorePlatformRequirements($ignorePlatformReqs); $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_1'); if (null === $expectedFixture) { @@ -1714,6 +1715,29 @@ EOF; ), 'no_php_required' ), + 'Ignoring all platform requirements skips check completely' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('^7.2')), + new Link('a', 'ext-xml', $versionParser->parseConstraints('*')), + new Link('a', 'ext-json', $versionParser->parseConstraints('*')) + ), + null, + array(), + array(), + true + ), + 'Ignored platform requirements are not checked for' => array( + array( + new Link('a', 'php', $versionParser->parseConstraints('^7.2.8')), + new Link('a', 'ext-xml', $versionParser->parseConstraints('*')), + new Link('a', 'ext-json', $versionParser->parseConstraints('*')), + new Link('a', 'ext-pdo', $versionParser->parseConstraints('*')) + ), + 'no_php_required', + array(), + array(), + array('php', 'ext-pdo') + ), 'No extensions required' => array( array( new Link('a', 'php', $versionParser->parseConstraints('^7.2')), From 1e08097a17471adb64afa15fb624692c72f91e8d Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Jun 2020 16:05:13 +0200 Subject: [PATCH 159/193] Fix phpstan warning --- src/Composer/Autoload/AutoloadGenerator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 9ef442ba1..4dee65417 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -341,6 +341,7 @@ EOF; } $filesystem->filePutContentsIfModified($targetDir.'/autoload_static.php', $this->getStaticFile($suffix, $targetDir, $vendorPath, $basePath, $staticPhpVersion)); $checkPlatform = $config->get('platform-check') && $this->ignorePlatformReqs !== true; + $platformCheckContent = null; if ($checkPlatform) { $platformCheckContent = $this->getPlatformCheck($packageMap, $this->ignorePlatformReqs ?: array()); if (null === $platformCheckContent) { From c0f5c13516a8dfbb0592c6e63d27e7046e9ead1e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 09:45:19 +0200 Subject: [PATCH 160/193] RuleSetGenerator instance does not need to be kept around anymore --- src/Composer/DependencyResolver/Solver.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/Solver.php b/src/Composer/DependencyResolver/Solver.php index 7c2d3a00c..73e79c819 100644 --- a/src/Composer/DependencyResolver/Solver.php +++ b/src/Composer/DependencyResolver/Solver.php @@ -31,8 +31,6 @@ class Solver /** @var RuleSet */ protected $rules; - /** @var RuleSetGenerator */ - protected $ruleSetGenerator; /** @var RuleWatchGraph */ protected $watchGraph; @@ -192,8 +190,9 @@ class Solver $this->setupFixedMap($request); $this->io->writeError('Generating rules', true, IOInterface::DEBUG); - $this->ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool); - $this->rules = $this->ruleSetGenerator->getRulesFor($request, $ignorePlatformReqs); + $ruleSetGenerator = new RuleSetGenerator($this->policy, $this->pool); + $this->rules = $ruleSetGenerator->getRulesFor($request, $ignorePlatformReqs); + unset($ruleSetGenerator); $this->checkForRootRequireProblems($request, $ignorePlatformReqs); $this->decisions = new Decisions($this->pool); $this->watchGraph = new RuleWatchGraph; From dfccf8c0912e7f60e4c11f92453b13d7a2e7b732 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 19 May 2020 13:41:10 +0200 Subject: [PATCH 161/193] Add failing test reproducing the problem of #8902 --- .../installer/github-issues-8902.test | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/github-issues-8902.test diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-8902.test b/tests/Composer/Test/Fixtures/installer/github-issues-8902.test new file mode 100644 index 000000000..c92dda83b --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/github-issues-8902.test @@ -0,0 +1,46 @@ +--TEST-- + +See Github issue #8902 ( https://github.com/composer/composer/issues/8902 ). + +Avoid installing packages twice if they are required in different versions and one is matched by a dev package. + +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "beyondcode/laravel-dump-server", "version": "1.4.0", "require": { "symfony/var-dumper": "^5.0" } }, + { "name": "laravel/framework", "version": "6.8.14", "require": { "symfony/var-dumper": "^4.3.4" } }, + { "name": "symfony/var-dumper", "version": "4.4.0" }, + { "name": "symfony/var-dumper", "version": "dev-master", "extra": { "branch-alias": {"dev-master": "5.2-dev"} } } + ] + } + ], + "require": { + "beyondcode/laravel-dump-server": "^1.3", + "laravel/framework": "^6.8" + }, + "minimum-stability": "dev" +} + +--RUN-- +update + +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies +Your requirements could not be resolved to an installable set of packages. + + Problem 1 + - Root composer.json requires beyondcode/laravel-dump-server ^1.3 -> satisfiable by beyondcode/laravel-dump-server[1.4.0]. + - beyondcode/laravel-dump-server 1.4.0 requires symfony/var-dumper ^5.0 -> satisfiable by symfony/var-dumper[dev-master]. + - Root composer.json requires laravel/framework ^6.8 -> satisfiable by laravel/framework[6.8.14]. + - laravel/framework 6.8.14 requires symfony/var-dumper ^4.3.4 -> satisfiable by symfony/var-dumper[4.4.0]. + etc... + + +--EXPECT-- + +--EXPECT-EXIT-CODE-- +2 From 0ea9eafcafda3ead6b385673c62c78a2cf38cd17 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 10:01:02 +0200 Subject: [PATCH 162/193] Add rules for aliased packages when an alias is added, fixes #8902 --- src/Composer/DependencyResolver/RuleSetGenerator.php | 1 + .../Test/Fixtures/installer/github-issues-8902.test | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index ffb88d56c..12bd17783 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -165,6 +165,7 @@ class RuleSetGenerator $this->addedPackagesByNames[$name][] = $package; } } else { + $workQueue->enqueue($package->getAliasOf()); $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package)); } diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-8902.test b/tests/Composer/Test/Fixtures/installer/github-issues-8902.test index c92dda83b..407dff3af 100644 --- a/tests/Composer/Test/Fixtures/installer/github-issues-8902.test +++ b/tests/Composer/Test/Fixtures/installer/github-issues-8902.test @@ -34,11 +34,11 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires beyondcode/laravel-dump-server ^1.3 -> satisfiable by beyondcode/laravel-dump-server[1.4.0]. - - beyondcode/laravel-dump-server 1.4.0 requires symfony/var-dumper ^5.0 -> satisfiable by symfony/var-dumper[dev-master]. - - Root composer.json requires laravel/framework ^6.8 -> satisfiable by laravel/framework[6.8.14]. + - You can only install one version of a package, so only one of these can be installed: symfony/var-dumper[dev-master, 4.4.0]. + - don't install symfony/var-dumper 5.2.x-dev|install symfony/var-dumper dev-master - laravel/framework 6.8.14 requires symfony/var-dumper ^4.3.4 -> satisfiable by symfony/var-dumper[4.4.0]. - etc... - + - beyondcode/laravel-dump-server 1.4.0 requires symfony/var-dumper ^5.0 -> satisfiable by symfony/var-dumper[5.2.x-dev (alias of dev-master)]. + - Root composer.json requires laravel/framework ^6.8 -> satisfiable by laravel/framework[6.8.14]. --EXPECT-- From 2ddf4346ef6a82eca251c78dc40bf8af56a4b776 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 10:35:04 +0200 Subject: [PATCH 163/193] Improve error reporting for RULE_PACKAGE_ALIAS --- src/Composer/DependencyResolver/Rule.php | 5 ++++- .../Composer/Test/Fixtures/installer/github-issues-8902.test | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 0a106c854..fafc8bda8 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -288,7 +288,10 @@ abstract class Rule return 'Conclusion: '.$ruleText.$learnedString; case self::RULE_PACKAGE_ALIAS: - return $ruleText; + $aliasPackage = $this->deduplicateMasterAlias($pool->literalToPackage($literals[0])); + $package = $this->deduplicateMasterAlias($pool->literalToPackage($literals[1])); + + return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.'; default: return '('.$ruleText.')'; } diff --git a/tests/Composer/Test/Fixtures/installer/github-issues-8902.test b/tests/Composer/Test/Fixtures/installer/github-issues-8902.test index 407dff3af..53fc1275b 100644 --- a/tests/Composer/Test/Fixtures/installer/github-issues-8902.test +++ b/tests/Composer/Test/Fixtures/installer/github-issues-8902.test @@ -35,7 +35,7 @@ Your requirements could not be resolved to an installable set of packages. Problem 1 - Root composer.json requires beyondcode/laravel-dump-server ^1.3 -> satisfiable by beyondcode/laravel-dump-server[1.4.0]. - You can only install one version of a package, so only one of these can be installed: symfony/var-dumper[dev-master, 4.4.0]. - - don't install symfony/var-dumper 5.2.x-dev|install symfony/var-dumper dev-master + - symfony/var-dumper 5.2.x-dev is an alias of symfony/var-dumper dev-master and thus requires it to be installed too. - laravel/framework 6.8.14 requires symfony/var-dumper ^4.3.4 -> satisfiable by symfony/var-dumper[4.4.0]. - beyondcode/laravel-dump-server 1.4.0 requires symfony/var-dumper ^5.0 -> satisfiable by symfony/var-dumper[5.2.x-dev (alias of dev-master)]. - Root composer.json requires laravel/framework ^6.8 -> satisfiable by laravel/framework[6.8.14]. From c8efb50d399bd366f3635dbd2d84705bbf605cff Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 11:17:49 +0200 Subject: [PATCH 164/193] Change the single requirement ignore from --ignore-platform-reqs=xx to --ignore-platform-req=xx to avoid BC issues --- doc/03-cli.md | 55 +++++++++++-------- src/Composer/Command/CreateProjectCommand.php | 7 +-- src/Composer/Command/DumpAutoloadCommand.php | 7 +-- src/Composer/Command/InitCommand.php | 6 +- src/Composer/Command/InstallCommand.php | 7 +-- src/Composer/Command/RemoveCommand.php | 7 +-- src/Composer/Command/RequireCommand.php | 7 +-- src/Composer/Command/UpdateCommand.php | 7 +-- .../SolverProblemsException.php | 2 +- tests/Composer/Test/InstallerTest.php | 8 +-- 10 files changed, 56 insertions(+), 57 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index a29098249..374ef952e 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -112,11 +112,13 @@ resolution. * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. You can also ignore specific packages only using - `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. ## update / u @@ -187,11 +189,13 @@ php composer.phar update vendor/package:2.0.1 vendor/package2:3.0.* * **--classmap-authoritative (-a):** Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`. * **--apcu-autoloader:** Use APCu to cache found/not-found classes. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. You can also ignore specific packages only using - `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -235,11 +239,13 @@ If you do not specify a package, composer will prompt you to search for a packag * **--update-no-dev:** Run the dependency update with the `--no-dev` option. * **--update-with-dependencies:** Also update dependencies of the newly required packages, except those that are root requirements. * **--update-with-all-dependencies:** Also update dependencies of the newly required packages, including those that are root requirements. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. You can also ignore specific packages only using - `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. * **--prefer-stable:** Prefer stable versions of dependencies. * **--prefer-lowest:** Prefer lowest versions of dependencies. Useful for testing minimal versions of requirements, generally used with `--prefer-stable`. @@ -273,11 +279,13 @@ uninstalled. * **--no-scripts:** Skips execution of scripts defined in `composer.json`. * **--update-no-dev:** Run the dependency update with the --no-dev option. * **--update-with-dependencies:** Also update dependencies of the removed packages. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. You can also ignore specific packages only using - `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. * **--optimize-autoloader (-o):** Convert PSR-0/4 autoloading to classmap to get a faster autoloader. This is recommended especially for production, but can take a bit of time to run so it is currently not done by default. @@ -730,11 +738,13 @@ By default the command checks for the packages on packagist.org. mode. * **--remove-vcs:** Force-remove the VCS metadata without prompting. * **--no-install:** Disables installation of the vendors. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` - requirements and force the installation even if the local machine does not - fulfill these. You can also ignore specific packages only using - `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. +* **--ignore-platform-reqs:** ignore all platform requirements (`php`, `hhvm`, + `lib-*` and `ext-*`) and force the installation even if the local machine does + not fulfill these. See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement(`php`, + `hhvm`, `lib-*` and `ext-*`) and force the installation even if the local machine + does not fulfill it. ## dump-autoload (dumpautoload) @@ -758,10 +768,11 @@ performance. Implicitly enables `--optimize`. * **--apcu:** Use APCu to cache found/not-found classes. * **--no-dev:** Disables autoload-dev rules. -* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*` +* **--ignore-platform-reqs:** ignore all `php`, `hhvm`, `lib-*` and `ext-*` requirements and skip the [platform check](07-runtime.md#platform-check) for these. - You can also ignore specific packages only using `--ignore-platform-reqs=ext-foo --ignore-platform-reqs=ext-bar`. See also the [`platform`](06-config.md#platform) config option. +* **--ignore-platform-req:** ignore a specific platform requirement (`php`, `hhvm`, + `lib-*` and `ext-*`) and skip the [platform check](07-runtime.md#platform-check) for it. ## clear-cache / clearcache / cc diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 1960fe1ef..4b4bd0eec 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -80,7 +80,8 @@ class CreateProjectCommand extends BaseCommand new InputOption('keep-vcs', null, InputOption::VALUE_NONE, 'Whether to prevent deleting the vcs folder.'), new InputOption('remove-vcs', null, InputOption::VALUE_NONE, 'Whether to force deletion of the vcs folder without prompting.'), new InputOption('no-install', null, InputOption::VALUE_NONE, 'Whether to skip installation of the package dependencies.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), )) ->setHelp( <<setOption('no-plugins', true); } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); return $this->installProject( $io, diff --git a/src/Composer/Command/DumpAutoloadCommand.php b/src/Composer/Command/DumpAutoloadCommand.php index 240a6c558..7449d197c 100644 --- a/src/Composer/Command/DumpAutoloadCommand.php +++ b/src/Composer/Command/DumpAutoloadCommand.php @@ -35,7 +35,8 @@ class DumpAutoloadCommand extends BaseCommand new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize`.'), new InputOption('apcu', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), new InputOption('no-dev', null, InputOption::VALUE_NONE, 'Disables autoload-dev rules.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s) from the platform check.'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), )) ->setHelp( <<getIO()->write('Generating autoload files'); } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $generator = $composer->getAutoloadGenerator(); $generator->setDevMode(!$input->getOption('no-dev')); diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 3acad2fe5..111edbde9 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -725,10 +725,8 @@ EOT { // handle ignore-platform-reqs flag if present $ignorePlatformReqs = false; - if ($input->hasOption('ignore-platform-reqs')) { - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + if ($input->hasOption('ignore-platform-reqs') && $input->hasOption('ignore-platform-req')) { + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); } // find the latest version allowed in this repo set diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index 4be349819..93523cba7 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -49,7 +49,8 @@ class InstallCommand extends BaseCommand new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Should not be provided, use composer require instead to add a given package to composer.json.'), )) ->setHelp( @@ -103,9 +104,7 @@ EOT $authoritative = $input->getOption('classmap-authoritative') || $config->get('classmap-authoritative'); $apcu = $input->getOption('apcu-autoloader') || $config->get('apcu-autoloader'); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $install ->setDryRun($input->getOption('dry-run')) diff --git a/src/Composer/Command/RemoveCommand.php b/src/Composer/Command/RemoveCommand.php index ef77391a9..62e23dd9f 100644 --- a/src/Composer/Command/RemoveCommand.php +++ b/src/Composer/Command/RemoveCommand.php @@ -50,7 +50,8 @@ class RemoveCommand extends BaseCommand new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), new InputOption('no-update-with-dependencies', null, InputOption::VALUE_NONE, 'Does not allow inherited dependencies to be updated with explicit dependencies.'), new InputOption('unused', null, InputOption::VALUE_NONE, 'Remove all packages which are locked but not required by any other package.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), @@ -238,9 +239,7 @@ EOT $io->writeError('Running composer update '.implode(' ', $packages).$flags); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $install ->setVerbose($input->getOption('verbose')) diff --git a/src/Composer/Command/RequireCommand.php b/src/Composer/Command/RequireCommand.php index 69bf1c528..b77ab5a4e 100644 --- a/src/Composer/Command/RequireCommand.php +++ b/src/Composer/Command/RequireCommand.php @@ -69,7 +69,8 @@ class RequireCommand extends InitCommand new InputOption('update-with-all-dependencies', null, InputOption::VALUE_NONE, 'Allows all inherited dependencies to be updated, including those that are root requirements.'), new InputOption('with-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-dependencies'), new InputOption('with-all-dependencies', null, InputOption::VALUE_NONE, 'Alias for --update-with-all-dependencies'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('sort-packages', null, InputOption::VALUE_NONE, 'Sorts packages when adding/updating a new dependency'), @@ -287,9 +288,7 @@ EOT $install = Installer::create($io, $composer); - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $install ->setDryRun($input->getOption('dry-run')) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 2329d1c24..ae0b41076 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -60,7 +60,8 @@ class UpdateCommand extends BaseCommand new InputOption('optimize-autoloader', 'o', InputOption::VALUE_NONE, 'Optimize autoloader during autoloader dump.'), new InputOption('classmap-authoritative', 'a', InputOption::VALUE_NONE, 'Autoload classes from the classmap only. Implicitly enables `--optimize-autoloader`.'), new InputOption('apcu-autoloader', null, InputOption::VALUE_NONE, 'Use APCu to cache found/not-found classes.'), - new InputOption('ignore-platform-reqs', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Ignore platform requirements (php & ext- packages), optionally can take a package name to ignore specific package(s).'), + new InputOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Ignore a specific platform requirement (php & ext- packages).'), + new InputOption('ignore-platform-reqs', null, InputOption::VALUE_NONE, 'Ignore all platform requirements (php & ext- packages).'), new InputOption('prefer-stable', null, InputOption::VALUE_NONE, 'Prefer stable versions of dependencies.'), new InputOption('prefer-lowest', null, InputOption::VALUE_NONE, 'Prefer lowest versions of dependencies.'), new InputOption('interactive', 'i', InputOption::VALUE_NONE, 'Interactive interface with autocompletion to select the packages to update.'), @@ -196,9 +197,7 @@ EOT $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $install ->setDryRun($input->getOption('dry-run')) diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index dd1cb48b4..ff94c8932 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -74,7 +74,7 @@ class SolverProblemsException extends \RuntimeException // TODO remove before 2.0 final if (!class_exists('PHPUnit\Framework\TestCase', false)) { if (strpos($text, 'found composer-plugin-api[2.0.0] but it does not match')) { - $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-reqs=composer-plugin-api, but this may result in broken plugins and bigger problems down the line."; + $hints[] = "You are using a snapshot build of Composer 2, which some of your plugins seem to be incompatible with. Make sure you update your plugins or report an issue to them to ask them to support Composer 2. To work around this you can run Composer with --ignore-platform-req=composer-plugin-api, but this may result in broken plugins and bigger problems down the line."; } else { $hints[] = "You are using a snapshot build of Composer 2, which may be the cause of the problem. Run `composer self-update --stable` and then try again. In case it solves the problem, please report an issue mentioning Composer 2."; } diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php index 1ba25847c..94a56147e 100644 --- a/tests/Composer/Test/InstallerTest.php +++ b/tests/Composer/Test/InstallerTest.php @@ -264,9 +264,7 @@ class InstallerTest extends TestCase $application = new Application; $application->get('install')->setCode(function ($input, $output) use ($installer) { - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $installer ->setDevMode(!$input->getOption('no-dev')) @@ -291,9 +289,7 @@ class InstallerTest extends TestCase $updateAllowTransitiveDependencies = Request::UPDATE_LISTED_WITH_TRANSITIVE_DEPS_NO_ROOT_REQUIRE; } - $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') - ? (array_filter($input->getOption('ignore-platform-reqs')) ? $input->getOption('ignore-platform-reqs') : true) - : false; + $ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false); $installer ->setDevMode(!$input->getOption('no-dev')) From 5e5f9f8142051b39f4dd61cf5ec95ec68408cca9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 11:22:35 +0200 Subject: [PATCH 165/193] Remove unused RULE_INTERNAL_ALLOW_UPDATE --- src/Composer/DependencyResolver/Rule.php | 4 ---- .../Composer/Test/DependencyResolver/RuleSetIteratorTest.php | 2 +- tests/Composer/Test/DependencyResolver/RuleSetTest.php | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index fafc8bda8..d2bd582a5 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -28,7 +28,6 @@ use Composer\Semver\Constraint\Constraint; abstract class Rule { // reason constants - const RULE_INTERNAL_ALLOW_UPDATE = 1; const RULE_ROOT_REQUIRE = 2; const RULE_FIXED = 3; const RULE_PACKAGE_CONFLICT = 6; @@ -180,9 +179,6 @@ abstract class Rule } switch ($this->getReason()) { - case self::RULE_INTERNAL_ALLOW_UPDATE: - return $ruleText; - case self::RULE_ROOT_REQUIRE: $packageName = $this->reasonData['packageName']; $constraint = $this->reasonData['constraint']; diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 4ebf37bf3..381fbb6e5 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -35,7 +35,7 @@ class RuleSetIteratorTest extends TestCase new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null), ), RuleSet::TYPE_LEARNED => array( - new GenericRule(array(), Rule::RULE_INTERNAL_ALLOW_UPDATE, null), + new GenericRule(array(), Rule::RULE_FIXED, null), ), RuleSet::TYPE_PACKAGE => array(), ); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index 5a89ddf79..12c91837b 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -31,7 +31,7 @@ class RuleSetTest extends TestCase new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null), ), RuleSet::TYPE_LEARNED => array( - new GenericRule(array(), Rule::RULE_INTERNAL_ALLOW_UPDATE, null), + new GenericRule(array(), Rule::RULE_FIXED, null), ), ); From 7a97741aaec76e7df5193350b93cb1808004d43e Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 2 Jun 2020 12:03:58 +0100 Subject: [PATCH 166/193] Use --ignore-platform-req=php (#8936) --- .github/workflows/continuous-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 35e2f04ea..30c035bd4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -118,7 +118,7 @@ jobs: - name: "Handle ignore-platform-reqs dependencies update" if: "contains(matrix.dependencies, 'ignore')" - run: "echo \"::set-env name=COMPOSER_FLAGS::$COMPOSER_FLAGS --ignore-platform-reqs\"" + run: "echo \"::set-env name=COMPOSER_FLAGS::$COMPOSER_FLAGS --ignore-platform-req=php\"" - name: "Remove platform config to get latest dependencies for current PHP version when build is not locked" run: "composer config platform --unset" @@ -134,7 +134,7 @@ jobs: - name: "Require latest PHPUnitBridge for PHP 8" if: "matrix.php-version == '8.0'" run: | - composer require --no-update --dev symfony/phpunit-bridge:^5.1.0-RC2 + composer require --no-update --dev symfony/phpunit-bridge:^5.1 composer config -g platform-check false - name: "Update Symfony's PHPUnitBridge to latest available for the current PHP always as it is not really a dependency of the project" From 1ea3111583f11481d3e5b63395d1e478bca88461 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 13:12:15 +0200 Subject: [PATCH 167/193] Use RULE_LEARNED instead of RULE_FIXED --- tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php | 2 +- tests/Composer/Test/DependencyResolver/RuleSetTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php index 381fbb6e5..ffcae5668 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetIteratorTest.php @@ -35,7 +35,7 @@ class RuleSetIteratorTest extends TestCase new GenericRule(array(), Rule::RULE_ROOT_REQUIRE, null), ), RuleSet::TYPE_LEARNED => array( - new GenericRule(array(), Rule::RULE_FIXED, null), + new GenericRule(array(), Rule::RULE_LEARNED, null), ), RuleSet::TYPE_PACKAGE => array(), ); diff --git a/tests/Composer/Test/DependencyResolver/RuleSetTest.php b/tests/Composer/Test/DependencyResolver/RuleSetTest.php index 12c91837b..966044bb6 100644 --- a/tests/Composer/Test/DependencyResolver/RuleSetTest.php +++ b/tests/Composer/Test/DependencyResolver/RuleSetTest.php @@ -31,7 +31,7 @@ class RuleSetTest extends TestCase new GenericRule(array(2), Rule::RULE_ROOT_REQUIRE, null), ), RuleSet::TYPE_LEARNED => array( - new GenericRule(array(), Rule::RULE_FIXED, null), + new GenericRule(array(), Rule::RULE_LEARNED, null), ), ); From 8a7f3a9a094b895f1477448a3ff4e2eb7ae7de70 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 13:13:11 +0200 Subject: [PATCH 168/193] Avoid rendering problem output for dev-master aliases which is useless --- src/Composer/DependencyResolver/Problem.php | 2 +- src/Composer/DependencyResolver/Rule.php | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index 16da238b6..a7efca100 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -104,7 +104,7 @@ class Problem $template = preg_replace('{^\S+ \S+ }', '%s%s ', $message); $messages[] = $template; $templates[$template][$m[1]][$parser->normalize($m[2])] = $m[2]; - } else { + } elseif ($message !== '') { $messages[] = $message; } } diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index d2bd582a5..5cee6c2e3 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -275,7 +275,10 @@ abstract class Rule $learnedString = ', learned rules:'."\n - "; $reasons = array(); foreach ($learnedPool[$this->reasonData] as $learnedRule) { - $reasons[] = $learnedRule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool); + $reason = $learnedRule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool); + if ($reason !== '') { + $reasons[] = $reason; + } } $learnedString .= implode("\n - ", array_unique($reasons)); } else { @@ -284,7 +287,11 @@ abstract class Rule return 'Conclusion: '.$ruleText.$learnedString; case self::RULE_PACKAGE_ALIAS: - $aliasPackage = $this->deduplicateMasterAlias($pool->literalToPackage($literals[0])); + $aliasPackage = $pool->literalToPackage($literals[0]); + // avoid returning content like "9999999-dev is an alias of dev-master" as it is useless + if ($aliasPackage->getVersion() === VersionParser::DEV_MASTER_ALIAS) { + return ''; + } $package = $this->deduplicateMasterAlias($pool->literalToPackage($literals[1])); return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.'; From 5a4709c3d63b6922ff13ba9d04046dfb822397e6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 13:44:56 +0200 Subject: [PATCH 169/193] Optimize rule creation for alias packages This mainly benefits projects running updates with minimum-stability: dev --- src/Composer/DependencyResolver/RuleSetGenerator.php | 6 ++++++ src/Composer/Package/AliasPackage.php | 9 +++++++++ .../Test/Fixtures/installer/alias-solver-problems.test | 4 ++-- .../Test/Fixtures/installer/alias-solver-problems2.test | 2 +- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 12bd17783..8fae3606f 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -167,6 +167,12 @@ class RuleSetGenerator } else { $workQueue->enqueue($package->getAliasOf()); $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($package->getAliasOf()), Rule::RULE_PACKAGE_ALIAS, $package)); + + // if alias package has no self.version requires, its requirements do not + // need to be added as the aliased package processing will take care of it + if (!$package->hasSelfVersionRequires()) { + continue; + } } foreach ($package->getRequires() as $link) { diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 4a8e2e96e..1f9d5061b 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -25,6 +25,7 @@ class AliasPackage extends BasePackage implements CompletePackageInterface protected $dev; protected $rootPackageAlias = false; protected $stability; + protected $hasSelfVersionRequires = false; /** @var PackageInterface */ protected $aliasOf; @@ -192,6 +193,9 @@ class AliasPackage extends BasePackage implements CompletePackageInterface } else { foreach ($links as $index => $link) { if ('self.version' === $link->getPrettyConstraint()) { + if ($linkType === 'requires') { + $this->hasSelfVersionRequires = true; + } $links[$index] = new Link($link->getSource(), $link->getTarget(), $constraint = new Constraint('=', $this->version), $linkType, $prettyVersion); $constraint->setPrettyString($prettyVersion); } @@ -201,6 +205,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface return $links; } + public function hasSelfVersionRequires() + { + return $this->hasSelfVersionRequires; + } + /*************************************** * Wrappers around the aliased package * ***************************************/ diff --git a/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test b/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test index 613bc4159..19d0ea287 100644 --- a/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test +++ b/tests/Composer/Test/Fixtures/installer/alias-solver-problems.test @@ -48,9 +48,9 @@ Your requirements could not be resolved to an installable set of packages. - Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master]. - a/a dev-master requires d/d 1.0.0 -> satisfiable by d/d[1.0.0]. - You can only install one version of a package, so only one of these can be installed: d/d[1.0.0, 2.0.0]. - - Conclusion: install d/d 2.0.0, learned rules: + - b/b dev-master requires d/d 2.0.0 -> satisfiable by d/d[2.0.0]. + - Conclusion: install b/b dev-master, learned rules: - Root composer.json requires b/b *@dev -> satisfiable by b/b[dev-master]. - - b/b dev-master requires d/d 2.0.0 -> satisfiable by d/d[2.0.0]. - Root composer.json requires a/a *@dev -> satisfiable by a/a[dev-master]. --EXPECT-- diff --git a/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test b/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test index 6d69faddf..2e3598ce2 100644 --- a/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test +++ b/tests/Composer/Test/Fixtures/installer/alias-solver-problems2.test @@ -47,7 +47,7 @@ Your requirements could not be resolved to an installable set of packages. - locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file. Problem 2 - locked/pkg dev-master requires locked/dependency 1.0.0 -> found locked/dependency[1.0.0] in lock file but not in remote repositories, make sure you avoid updating this package to keep the one from lock file. - - Root composer.json requires locked/pkg *@dev -> satisfiable by locked/pkg[dev-master]. + - locked/pkg is locked to version dev-master and an update of this package was not requested. Use the option --with-all-dependencies to allow upgrades, downgrades and removals for packages currently locked to specific versions. --EXPECT-- From 9bd255d8f1915005ab7af1647201d513519eb16b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 14:41:18 +0200 Subject: [PATCH 170/193] Remove some dead code from RuleSetGenerator --- .../DependencyResolver/RuleSetGenerator.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/Composer/DependencyResolver/RuleSetGenerator.php b/src/Composer/DependencyResolver/RuleSetGenerator.php index 12bd17783..9b4326c12 100644 --- a/src/Composer/DependencyResolver/RuleSetGenerator.php +++ b/src/Composer/DependencyResolver/RuleSetGenerator.php @@ -215,20 +215,6 @@ class RuleSetGenerator } } - protected function obsoleteImpossibleForAlias($package, $provider) - { - $packageIsAlias = $package instanceof AliasPackage; - $providerIsAlias = $provider instanceof AliasPackage; - - $impossible = ( - ($packageIsAlias && $package->getAliasOf() === $provider) || - ($providerIsAlias && $provider->getAliasOf() === $package) || - ($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf()) - ); - - return $impossible; - } - protected function addRulesForRequest(Request $request, $ignorePlatformReqs) { $unlockableMap = $request->getUnlockableMap(); From 718021724cc0b71f958beda5ae8739da100599e4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 15:45:58 +0200 Subject: [PATCH 171/193] Deduplicate more content in complex problem output --- src/Composer/DependencyResolver/Problem.php | 12 +++++- src/Composer/DependencyResolver/Rule.php | 48 +++++++++++++-------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/Composer/DependencyResolver/Problem.php b/src/Composer/DependencyResolver/Problem.php index a7efca100..eed4c5043 100644 --- a/src/Composer/DependencyResolver/Problem.php +++ b/src/Composer/DependencyResolver/Problem.php @@ -94,11 +94,19 @@ class Problem } } + return self::formatDeduplicatedRules($reasons, ' ', $repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); + } + + /** + * @internal + */ + public static function formatDeduplicatedRules($rules, $indent, RepositorySet $repositorySet, Request $request, Pool $pool, $isVerbose, array $installedMap = array(), array $learnedPool = array()) + { $messages = array(); $templates = array(); $parser = new VersionParser; $deduplicatableRuleTypes = array(Rule::RULE_PACKAGE_REQUIRES, Rule::RULE_PACKAGE_CONFLICT); - foreach ($reasons as $rule) { + foreach ($rules as $rule) { $message = $rule->getPrettyString($repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool); if (in_array($rule->getReason(), $deduplicatableRuleTypes, true) && preg_match('{^(?P\S+) (?P\S+) (?Prequires|conflicts)}', $message, $m)) { $template = preg_replace('{^\S+ \S+ }', '%s%s ', $message); @@ -130,7 +138,7 @@ class Problem } } - return "\n - ".implode("\n - ", $result); + return "\n$indent- ".implode("\n$indent- ", $result); } public function isCausedByLock(RepositorySet $repositorySet, Request $request, Pool $pool) diff --git a/src/Composer/DependencyResolver/Rule.php b/src/Composer/DependencyResolver/Rule.php index 5cee6c2e3..8ba9756d6 100644 --- a/src/Composer/DependencyResolver/Rule.php +++ b/src/Composer/DependencyResolver/Rule.php @@ -170,14 +170,6 @@ abstract class Rule { $literals = $this->getLiterals(); - $ruleText = ''; - foreach ($literals as $i => $literal) { - if ($i != 0) { - $ruleText .= '|'; - } - $ruleText .= $pool->literalToPrettyString($literal, $installedMap); - } - switch ($this->getReason()) { case self::RULE_ROOT_REQUIRE: $packageName = $this->reasonData['packageName']; @@ -272,19 +264,33 @@ abstract class Rule return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose) . '.'; case self::RULE_LEARNED: if (isset($learnedPool[$this->reasonData])) { - $learnedString = ', learned rules:'."\n - "; - $reasons = array(); - foreach ($learnedPool[$this->reasonData] as $learnedRule) { - $reason = $learnedRule->getPrettyString($repositorySet, $request, $pool, $installedMap, $learnedPool); - if ($reason !== '') { - $reasons[] = $reason; - } - } - $learnedString .= implode("\n - ", array_unique($reasons)); + $learnedString = ', learned rules:' . Problem::formatDeduplicatedRules($learnedPool[$this->reasonData], ' ', $repositorySet, $request, $pool, $installedMap, $learnedPool); } else { $learnedString = ' (reasoning unavailable)'; } + if (count($literals) === 1) { + $ruleText = $pool->literalToPrettyString($literals[0], $installedMap); + } else { + $groups = array(); + foreach ($literals as $literal) { + $package = $pool->literalToPackage($literal); + if (isset($installedMap[$package->id])) { + $group = $literal > 0 ? 'keep' : 'remove'; + } else { + $group = $literal > 0 ? 'install' : 'don\'t install'; + } + + $groups[$group][] = $this->deduplicateMasterAlias($package); + } + $ruleTexts = array(); + foreach ($groups as $group => $packages) { + $ruleTexts[] = $group . (count($packages) > 1 ? ' one of' : '').' ' . $this->formatPackagesUnique($pool, $packages, $isVerbose); + } + + $ruleText = implode(' | ', $ruleTexts); + } + return 'Conclusion: '.$ruleText.$learnedString; case self::RULE_PACKAGE_ALIAS: $aliasPackage = $pool->literalToPackage($literals[0]); @@ -296,6 +302,14 @@ abstract class Rule return $aliasPackage->getPrettyString() .' is an alias of '.$package->getPrettyString().' and thus requires it to be installed too.'; default: + $ruleText = ''; + foreach ($literals as $i => $literal) { + if ($i != 0) { + $ruleText .= '|'; + } + $ruleText .= $pool->literalToPrettyString($literal, $installedMap); + } + return '('.$ruleText.')'; } } From 462dc5a933fe295e79a1277bebdd45e6c63460d1 Mon Sep 17 00:00:00 2001 From: Graham Campbell Date: Tue, 2 Jun 2020 14:52:38 +0100 Subject: [PATCH 172/193] Fix for HHVM 3 --- src/Composer/Util/Http/CurlDownloader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 017b2d1a2..365caf899 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -75,7 +75,7 @@ class CurlDownloader $this->multiHandle = $mh = curl_multi_init(); if (function_exists('curl_multi_setopt')) { curl_multi_setopt($mh, CURLMOPT_PIPELINING, PHP_VERSION_ID >= 70400 ? /* CURLPIPE_MULTIPLEX */ 2 : /*CURLPIPE_HTTP1 | CURLPIPE_MULTIPLEX*/ 3); - if (defined('CURLMOPT_MAX_HOST_CONNECTIONS')) { + if (defined('CURLMOPT_MAX_HOST_CONNECTIONS') && !defined('HHVM_VERSION')) { curl_multi_setopt($mh, CURLMOPT_MAX_HOST_CONNECTIONS, 8); } } From 4d75d524f18ba5101fd8ad153b7ce4fd35f79883 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 16:20:17 +0200 Subject: [PATCH 173/193] Fix class name case, refs #8939 --- src/Composer/Composer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Composer.php b/src/Composer/Composer.php index 57cfdffe1..8f0ac058a 100644 --- a/src/Composer/Composer.php +++ b/src/Composer/Composer.php @@ -189,7 +189,7 @@ class Composer /** * @param Loop $loop */ - public function setLoop(loop $loop) + public function setLoop(Loop $loop) { $this->loop = $loop; } From 1055be7936d33cd181ddab61994754068611a532 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 2 Jun 2020 17:03:41 +0200 Subject: [PATCH 174/193] Fix unfixing of packages when a replacer gets unfixed before a replacee, refs #8882 --- src/Composer/DependencyResolver/PoolBuilder.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php index 9e1f9f433..29903f493 100644 --- a/src/Composer/DependencyResolver/PoolBuilder.php +++ b/src/Composer/DependencyResolver/PoolBuilder.php @@ -113,7 +113,7 @@ class PoolBuilder $request->fixPackage($lockedPackage); $lockedName = $lockedPackage->getName(); // remember which packages we skipped loading remote content for in this partial update - $this->skippedLoad[$lockedPackage->getName()] = $lockedName; + $this->skippedLoad[$lockedName] = $lockedName; foreach ($lockedPackage->getReplaces() as $link) { $this->skippedLoad[$link->getTarget()] = $lockedName; } @@ -413,8 +413,12 @@ class PoolBuilder } } - // if we unfixed a replaced package name, we also need to unfix the replacer itself - if ($this->skippedLoad[$name] !== $name) { + if ( + // if we unfixed a replaced package name, we also need to unfix the replacer itself + $this->skippedLoad[$name] !== $name + // as long as it was not unfixed yet + && isset($this->skippedLoad[$this->skippedLoad[$name]]) + ) { $this->unfixPackage($request, $this->skippedLoad[$name]); } From fafa9599335161dc77f0ebf96c8f83469c460a1f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 08:33:14 +0200 Subject: [PATCH 175/193] Add test to assert prefer-lowest behavior when conflicting branches prevent absolute lowest version from being installed, closes #7408 --- .../installer/prefer-lowest-branches.test | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/prefer-lowest-branches.test diff --git a/tests/Composer/Test/Fixtures/installer/prefer-lowest-branches.test b/tests/Composer/Test/Fixtures/installer/prefer-lowest-branches.test new file mode 100644 index 000000000..930b87ae8 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/prefer-lowest-branches.test @@ -0,0 +1,29 @@ +--TEST-- +Assert that prefer-lowest can not pick the lowest version of all packages when two branches are valid but conflict with each other +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + {"name": "req/pkg", "version": "1.0.0", "require": {"req/pkg2": "^1.2"}}, + {"name": "req/pkg2", "version": "1.0.0", "require": {"req/pkg": "^1.2"}}, + {"name": "req/pkg", "version": "1.2.0", "require": {"req/pkg2": "^1.0"}}, + {"name": "req/pkg2", "version": "1.2.0", "require": {"req/pkg": "^1.0"}}, + {"name": "req/pkg", "version": "1.4.0", "require": {"req/pkg2": "^1.0"}}, + {"name": "req/pkg2", "version": "1.4.0", "require": {"req/pkg": "^1.0"}} + ] + } + ], + "require": { + "req/pkg": "*", + "req/pkg2": "*" + } +} + +--RUN-- +update --prefer-lowest + +--EXPECT-- +Installing req/pkg2 (1.2.0) +Installing req/pkg (1.0.0) From 8d3ec0f71872b87e5613a4823e97c1562e451780 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 09:42:16 +0200 Subject: [PATCH 176/193] Do not reverse sorted packages, fixes #8320 --- src/Composer/Plugin/PluginManager.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 1dbbd2960..8a7ca86c8 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -347,6 +347,8 @@ class PluginManager /** * Load all plugins and installers from a repository * + * If a plugin requires another plugin, the required one will be loaded first + * * Note that plugins in the specified repository that rely on events that * have fired prior to loading will be missed. This means you likely want to * call this method as early as possible. @@ -358,7 +360,7 @@ class PluginManager private function loadRepository(RepositoryInterface $repo) { $packages = $repo->getPackages(); - $sortedPackages = array_reverse(PackageSorter::sortPackages($packages)); + $sortedPackages = PackageSorter::sortPackages($packages); foreach ($sortedPackages as $package) { if (!($package instanceof CompletePackage)) { continue; From 93502eed682bd561a79bef640296cf76406d6c2a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 09:57:32 +0200 Subject: [PATCH 177/193] Add powershell instructions, fixes #8883 --- doc/00-intro.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 897d23609..ea88808a0 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -10,7 +10,7 @@ Composer is **not** a package manager in the same sense as Yum or Apt are. Yes, it deals with "packages" or libraries, but it manages them on a per-project basis, installing them in a directory (e.g. `vendor`) inside your project. By default it does not install anything globally. Thus, it is a dependency -manager. It does however support a "global" project for convenience via the +manager. It does however support a "global" project for convenience via the [global](03-cli.md#global) command. This idea is not new and Composer is strongly inspired by node's @@ -58,7 +58,7 @@ project, or globally as a system wide executable. #### Locally -To install Composer locally, run the installer in your project directory. See +To install Composer locally, run the installer in your project directory. See [the Download page](https://getcomposer.org/download/) for instructions. The installer will check a few PHP settings and then download `composer.phar` @@ -134,8 +134,16 @@ to download `composer.phar`. Create a new `composer.bat` file alongside `composer.phar`: +Using cmd.exe: + ```sh -C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat +C:\bin> echo @php "%~dp0composer.phar" %*>composer.bat +``` + +Using PowerShell: + +```sh +PS C:\bin> Set-Content composer.bat '@php "%~dp0composer.phar" %*' ``` Add the directory to your PATH environment variable if it isn't already. From 547763ab67148e001216f7fb9f0ed38a8da38f7a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 10:03:43 +0200 Subject: [PATCH 178/193] Update changelog --- CHANGELOG.md | 6 ++++++ composer.lock | 15 ++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1d3bba9..962a7759b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### [1.10.7] 2020-06-03 + + * Fix PHP 8 deprecations + * Fixed detection of pcntl_signal being in disabled_functions when pcntl_async_signal is allowed + ### [1.10.6] 2020-05-06 * Fixed version guessing to take composer-runtime-api and composer-plugin-api requirements into account to avoid selecting packages which require Composer 2 @@ -855,6 +860,7 @@ * Initial release +[1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7 [1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6 [1.10.5]: https://github.com/composer/composer/compare/1.10.4...1.10.5 [1.10.4]: https://github.com/composer/composer/compare/1.10.3...1.10.4 diff --git a/composer.lock b/composer.lock index 2fd85b33e..1a5309f88 100644 --- a/composer.lock +++ b/composer.lock @@ -1345,23 +1345,23 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v3.4.39", + "version": "v3.4.41", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9" + "reference": "f926812c6b3d456dfbd13c706293f49e9a10bc2a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c02893ae43532b46a4f0e0f207d088b939f278d9", - "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/f926812c6b3d456dfbd13c706293f49e9a10bc2a", + "reference": "f926812c6b3d456dfbd13c706293f49e9a10bc2a", "shasum": "" }, "require": { "php": ">=5.3.3" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0|9.1.2" }, "suggest": { "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" @@ -1406,9 +1406,6 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v3.4.38" - }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1423,7 +1420,7 @@ "type": "tidelift" } ], - "time": "2020-02-21T08:01:47+00:00" + "time": "2020-05-21T18:33:26+00:00" } ], "aliases": [], From 2490a857cb3cdeeabfc8bc3c47cac44ec81a9532 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 10:24:27 +0200 Subject: [PATCH 179/193] Bump phpunit-bridge version --- composer.json | 5 ++--- composer.lock | 30 ++++++++---------------------- 2 files changed, 10 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index 4fdbcb743..05fc9b85a 100644 --- a/composer.json +++ b/composer.json @@ -37,11 +37,10 @@ "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0" }, "conflict": { - "symfony/console": "2.8.38", - "symfony/phpunit-bridge": "3.4.40" + "symfony/console": "2.8.38" }, "require-dev": { - "symfony/phpunit-bridge": "^3.4", + "symfony/phpunit-bridge": "^4.2", "phpspec/prophecy": "^1.10" }, "suggest": { diff --git a/composer.lock b/composer.lock index 1a5309f88..32344435f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "281c2bedcde245309c1f9c884f65e1a1", + "content-hash": "9bdad77d200af5af7d93ddd289acd4f6", "packages": [ { "name": "composer/ca-bundle", @@ -1345,23 +1345,23 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v3.4.41", + "version": "v4.2.12", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "f926812c6b3d456dfbd13c706293f49e9a10bc2a" + "reference": "80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/f926812c6b3d456dfbd13c706293f49e9a10bc2a", - "reference": "f926812c6b3d456dfbd13c706293f49e9a10bc2a", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7", + "reference": "80f9ffa6afcc27c7b00e8b8446b1d5d48d94bae7", "shasum": "" }, "require": { "php": ">=5.3.3" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0|9.1.2" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" }, "suggest": { "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" @@ -1372,7 +1372,7 @@ "type": "symfony-bridge", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.2-dev" }, "thanks": { "name": "phpunit/phpunit", @@ -1406,21 +1406,7 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2020-05-21T18:33:26+00:00" + "time": "2019-07-05T06:33:37+00:00" } ], "aliases": [], From 81c6fe987b955f525704b3097853c86a661a026b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 10:26:56 +0200 Subject: [PATCH 180/193] Remove empty env var --- .github/workflows/continuous-integration.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 89eafea47..2b4afbf1b 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -10,7 +10,6 @@ on: env: COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --no-suggest --prefer-dist" - SYMFONY_PHPUNIT_VERSION: "" jobs: tests: From 7507b87fb3a388a2451f044c326721bfc69f4781 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 10:40:44 +0200 Subject: [PATCH 181/193] Remove stray var_dump --- src/Composer/Command/UpdateCommand.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index ae0b41076..516b6f472 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -122,7 +122,6 @@ EOT return preg_match('{\S+[ =:]\S+}', $pkg) > 0; }); foreach ($this->formatRequirements($allowlistPackagesWithRequirements) as $package => $constraint) { - var_Dump($package, $constraint); $reqs[$package] = $constraint; } From ccea1b95a86c39bc19d29ab272a9d3ef257fb3ae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 10:43:18 +0200 Subject: [PATCH 182/193] Fix pretty constraint for --with overrides --- src/Composer/Command/UpdateCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 516b6f472..9576b141f 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -293,14 +293,14 @@ EOT { $parser = new VersionParser; $oldPrettyString = $link->getConstraint()->getPrettyString(); - $newConstraint = MultiConstraint::create(array($link->getConstraint(), $parser->parseConstraints($constraint))); - $newConstraint->setPrettyString($oldPrettyString.' && '.$constraint); + $newConstraint = MultiConstraint::create(array($link->getConstraint(), $parser->parseConstraints($constraint)), true); + $newConstraint->setPrettyString($oldPrettyString.', '.$constraint); return new Link( $link->getSource(), $link->getTarget(), $newConstraint, $link->getDescription(), - $link->getPrettyConstraint() . ' && ' . $constraint + $link->getPrettyConstraint() . ', ' . $constraint ); } } From 2a9768a743e051d6fabed94ace65314b360ddc99 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 10:57:42 +0200 Subject: [PATCH 183/193] Update changelog/upgrade guide for 2.0.0-alpha1 --- CHANGELOG.md | 9 ++++++++- UPGRADE-2.0.md | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9160a293..40d38542a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### [2.0.0-?] 2020-?? +### [2.0.0-alpha1] 2020-06-03 * Breaking: This is a major release and while we tried to keep things compatible for most users, you might want to have a look at the [UPGRADE](UPGRADE-2.0.md) guides * Many CPU and memory performance improvements @@ -10,12 +10,14 @@ * Added a `composer-runtime-api` virtual package which you can require (as e.g. `^2.0`) to ensure things like the InstalledVersions class above are present. It will effectively force people to use Composer 2.x to install your project * Added support for parallel downloads of package metadata and zip files, this requires that the curl extension is present and we thus strongly recommend enabling curl * Added much clearer dependency resolution error reporting for common error cases + * Added support for updating to a specific version with partial updates, as well as a [--with flag](doc/03-cli.md#update--u) to pass in temporary constraint overrides * Added support for TTY mode on Linux/OSX/WSL so that script handlers now run in interactive mode * Added `only`, `exclude` and `canonical` options to all repositories, see [repository priorities](https://getcomposer.org/repoprio) for details * Added support for lib-zip platform package * Added `pre-operations-exec` event to be fired before the packages get installed/upgraded/removed * Added `pre-pool-create` event to be fired before the package pool for the dependency solver is created, which lets you modify the list of packages going in * Added `post-file-download` event to be fired after package dist files are downloaded, which lets you do additional checks on the files + * Added --locked flag to `show` command to see the packages from the composer.lock file * Added --unused flag to `remove` command to make sure any packages which are not needed anymore get removed * Added --dry-run flag to `require` and `remove` commands * Added --no-install flag to `update`, `require` and `remove` commands to disable the install step and only do the update step (composer.lock file update) @@ -25,6 +27,10 @@ * Added --json and --merge flags to `config` command to allow editing complex `extra.*` values by using json as input * Added confirmation prompt when running Composer as superuser in interactive mode * Added --no-check-version to `validate` command to remove the warning in case the version is defined + * Added --ignore-platform-req (without s) to all commands supporting --ignore-platform-reqs, which accepts a package name so you can ignore only specific platform requirements + * Added support for configuring GitLab deploy tokens in addition to private tokens, see [gitlab-token](doc/06-config.md#gitlab-token) + * Added support for package version guessing for require and init command to take all platform packages into account, not just php version + * Fixed package ordering when autoloading and especially when loading plugins, to make sure dependencies are loaded before their dependents * Fixed suggest output being very spammy, it now is only one line long and shows more rarely * Fixed conflict rules like e.g. >=5 from matching dev-master, as it is not normalized to 9999999-dev internally anymore @@ -890,6 +896,7 @@ * Initial release +[2.0.0-alpha1]: https://github.com/composer/composer/compare/1.10.7...2.0.0-alpha1 [1.10.7]: https://github.com/composer/composer/compare/1.10.6...1.10.7 [1.10.6]: https://github.com/composer/composer/compare/1.10.5...1.10.6 [1.10.5]: https://github.com/composer/composer/compare/1.10.4...1.10.5 diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md index ba9fa3615..33729371a 100644 --- a/UPGRADE-2.0.md +++ b/UPGRADE-2.0.md @@ -43,6 +43,7 @@ - All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled, then finally cleanup is called for all. Therefore for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can be undone as much as possible. - If you used `RemoteFilesystem` you probably should use `HttpDownloader` instead now - `PRE_DEPENDENCIES_SOLVING` and `POST_DEPENDENCIES_SOLVING` events have been removed, use the new `PRE_OPERATIONS_EXEC` or other existing events instead or talk to us if you think you really need this +- The bundled composer/semver is now the 3.x range, see release notes for [2.0](https://github.com/composer/semver/releases/tag/2.0.0) and [3.0](https://github.com/composer/semver/releases/tag/3.0.0) for the minor breaking changes there ## For Composer repository implementors From 50944e1c423cc9bd79ac7e9df36a1d9d370bba31 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Wed, 3 Jun 2020 11:04:43 +0200 Subject: [PATCH 184/193] Added docs for glob pattern support in classmaps --- doc/04-schema.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/04-schema.md b/doc/04-schema.md index 00070a0b8..de558cd5e 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -610,6 +610,18 @@ Example: } ``` +Its also supported to use glob patterns in a classmap path: + +Example: + +```json +{ + "autoload": { + "classmap": ["src/addons/*/lib/", "3rd-party/*", "Something.php"] + } +} +``` + #### Files If you want to require certain files explicitly on every request then you can use From 501b06323d95dd432c186bcce02f7a739639fcc9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 13:12:22 +0200 Subject: [PATCH 185/193] Fix wording --- doc/04-schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index de558cd5e..8bdad86ee 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -610,7 +610,7 @@ Example: } ``` -Its also supported to use glob patterns in a classmap path: +Wildcards (`*`) are also supported in a classmap paths, and expand to match any directory name: Example: From bf4a659f2aa21d64c2d80a3cef624e2a2f5d226e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 13:14:18 +0200 Subject: [PATCH 186/193] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40d38542a..2e2f52236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ * Added confirmation prompt when running Composer as superuser in interactive mode * Added --no-check-version to `validate` command to remove the warning in case the version is defined * Added --ignore-platform-req (without s) to all commands supporting --ignore-platform-reqs, which accepts a package name so you can ignore only specific platform requirements + * Added support for wildcards (`*`) in classmap autoloader paths * Added support for configuring GitLab deploy tokens in addition to private tokens, see [gitlab-token](doc/06-config.md#gitlab-token) * Added support for package version guessing for require and init command to take all platform packages into account, not just php version * Fixed package ordering when autoloading and especially when loading plugins, to make sure dependencies are loaded before their dependents From 29761cf78d2a85986c53c802b2a38c412689d437 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 14:56:54 +0200 Subject: [PATCH 187/193] Update lock --- composer.lock | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index dc8c0f147..feb7ccd7b 100644 --- a/composer.lock +++ b/composer.lock @@ -137,6 +137,11 @@ "validation", "versioning" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.0.0" + }, "funding": [ { "url": "https://packagist.com", @@ -337,6 +342,10 @@ "json", "schema" ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/5.2.10" + }, "time": "2020-05-27T16:41:55+00:00" }, { @@ -431,6 +440,10 @@ } ], "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/1.0" + }, "time": "2016-03-07T13:46:50+00:00" }, { @@ -829,6 +842,9 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.17.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -902,6 +918,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.17.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -1465,6 +1484,9 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/phpunit-bridge/tree/4.2" + }, "time": "2019-07-05T06:33:37+00:00" } ], @@ -1480,5 +1502,5 @@ "platform-overrides": { "php": "5.3.9" }, - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.0.0" } From 396ad87fd071dd7ef8bc66206ddb1a6255f85980 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 15:18:27 +0200 Subject: [PATCH 188/193] Add handling of ctrl-C on windows for php 7.4+ --- src/Composer/Command/CreateProjectCommand.php | 11 ++++++++ .../Installer/InstallationManager.php | 25 +++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 4b4bd0eec..9642ef466 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -367,6 +367,17 @@ EOT }); } } + // handler Ctrl+C for Windows on PHP 7.4+ + if (function_exists('sapi_windows_set_ctrl_handler')) { + @mkdir($directory, 0777, true); + if ($realDir = realpath($directory)) { + sapi_windows_set_ctrl_handler(function () use ($realDir) { + $fs = new Filesystem(); + $fs->removeDirectory($realDir); + exit(130); + }); + } + } // avoid displaying 9999999-dev as version if dev-master was selected if ($package instanceof AliasPackage && $package->getPrettyVersion() === VersionParser::DEV_MASTER_ALIAS) { diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index a8266bfe7..4939414e7 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -195,10 +195,11 @@ class InstallationManager } }; - // handler Ctrl+C for unix-like systems - $handleInterrupts = function_exists('pcntl_async_signals') && function_exists('pcntl_signal'); + $handleInterruptsUnix = function_exists('pcntl_async_signals') && function_exists('pcntl_signal'); + $handleInterruptsWindows = function_exists('sapi_windows_set_ctrl_handler'); $prevHandler = null; - if ($handleInterrupts) { + $windowsHandler = null; + if ($handleInterruptsUnix) { pcntl_async_signals(true); $prevHandler = pcntl_signal_get_handler(SIGINT); pcntl_signal(SIGINT, function ($sig) use ($runCleanup, $prevHandler) { @@ -211,6 +212,14 @@ class InstallationManager exit(130); }); } + if ($handleInterruptsWindows) { + $windowsHandler = function () use ($runCleanup) { + $runCleanup(); + + exit(130); + }; + sapi_windows_set_ctrl_handler($windowsHandler); + } try { foreach ($operations as $index => $operation) { @@ -317,16 +326,22 @@ class InstallationManager } catch (\Exception $e) { $runCleanup(); - if ($handleInterrupts) { + if ($handleInterruptsUnix) { pcntl_signal(SIGINT, $prevHandler); } + if ($handleInterruptsWindows) { + sapi_windows_set_ctrl_handler($prevHandler, false); + } throw $e; } - if ($handleInterrupts) { + if ($handleInterruptsUnix) { pcntl_signal(SIGINT, $prevHandler); } + if ($handleInterruptsWindows) { + sapi_windows_set_ctrl_handler($prevHandler, false); + } // do a last write so that we write the repository even if nothing changed // as that can trigger an update of some files like InstalledVersions.php if From 02059d96e76ca8bceb28dc9d4c92b643e367d44c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 15:19:02 +0200 Subject: [PATCH 189/193] Fail early if an archive extraction is going to fail at install/update stage to avoid leaving the vendor dir in a half up to date state --- src/Composer/Downloader/ArchiveDownloader.php | 13 ++++++++++++- src/Composer/Downloader/DownloadManager.php | 3 ++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 318e73c6f..666c33e01 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -15,6 +15,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; use Symfony\Component\Finder\Finder; use Composer\IO\IOInterface; +use Composer\Exception\IrrecoverableDownloadException; /** * Base downloader for archives @@ -25,6 +26,16 @@ use Composer\IO\IOInterface; */ abstract class ArchiveDownloader extends FileDownloader { + public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true) + { + $res = parent::download($package, $path, $prevPackage, $output); + if (is_dir($path) && !$this->filesystem->isDirEmpty($path)) { + throw new IrrecoverableDownloadException('Expected empty path to extract '.$package.' into but directory exists: '.$path); + } + + return $res; + } + /** * {@inheritDoc} * @throws \RuntimeException @@ -40,7 +51,7 @@ abstract class ArchiveDownloader extends FileDownloader $this->filesystem->ensureDirectoryExists($path); if (!$this->filesystem->isDirEmpty($path)) { - throw new \RuntimeException('Expected empty path to extract '.$package.' into but directory exists: '.$path); + throw new \UnexpectedValueException('Expected empty path to extract '.$package.' into but directory exists: '.$path); } do { diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 2fcfdd392..45467eb4c 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -15,6 +15,7 @@ namespace Composer\Downloader; use Composer\Package\PackageInterface; use Composer\IO\IOInterface; use Composer\Util\Filesystem; +use Composer\Exception\IrrecoverableDownloadException; use React\Promise\PromiseInterface; /** @@ -195,7 +196,7 @@ class DownloadManager } $handleError = function ($e) use ($sources, $source, $package, $io, $download) { - if ($e instanceof \RuntimeException) { + if ($e instanceof \RuntimeException && !$e instanceof IrrecoverableDownloadException) { if (!$sources) { throw $e; } From 63041fbf5021eacaa65126f87909ad99ed0b0752 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 15:28:10 +0200 Subject: [PATCH 190/193] Add missing class --- .../IrrecoverableDownloadException.php | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/Composer/Exception/IrrecoverableDownloadException.php diff --git a/src/Composer/Exception/IrrecoverableDownloadException.php b/src/Composer/Exception/IrrecoverableDownloadException.php new file mode 100644 index 000000000..72a833c72 --- /dev/null +++ b/src/Composer/Exception/IrrecoverableDownloadException.php @@ -0,0 +1,20 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Exception; + +/** + * @author Jordi Boggiano + */ +class IrrecoverableDownloadException extends \RuntimeException +{ +} From 90bd351b8be5e98fa840461384c65154332a960e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 15:37:36 +0200 Subject: [PATCH 191/193] Allow downgrades to go through even though the target dir for archive extraction exists --- src/Composer/Downloader/ArchiveDownloader.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 666c33e01..283d0103e 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -29,7 +29,9 @@ abstract class ArchiveDownloader extends FileDownloader public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true) { $res = parent::download($package, $path, $prevPackage, $output); - if (is_dir($path) && !$this->filesystem->isDirEmpty($path)) { + + // if not downgrading and the dir already exists it seems we have an inconsistent state in the vendor dir and the user should fix it + if (!$prevPackage && is_dir($path) && !$this->filesystem->isDirEmpty($path)) { throw new IrrecoverableDownloadException('Expected empty path to extract '.$package.' into but directory exists: '.$path); } From 816d8e9d1b3eedad823cf8f831c9a55a547167aa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 3 Jun 2020 15:41:47 +0200 Subject: [PATCH 192/193] Fix phpstan error --- src/Composer/Command/CreateProjectCommand.php | 2 +- src/Composer/Installer/InstallationManager.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 9642ef466..07ff8bed8 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -375,7 +375,7 @@ EOT $fs = new Filesystem(); $fs->removeDirectory($realDir); exit(130); - }); + }, true); } } diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index 4939414e7..bd1bf8aee 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -218,7 +218,7 @@ class InstallationManager exit(130); }; - sapi_windows_set_ctrl_handler($windowsHandler); + sapi_windows_set_ctrl_handler($windowsHandler, true); } try { From 26c7234a0d436e0fd3cff253cf5b6aeb2ae3213d Mon Sep 17 00:00:00 2001 From: johnstevenson Date: Thu, 4 Jun 2020 12:43:37 +0100 Subject: [PATCH 193/193] Update xdebug-handler to 1.4.2 --- composer.lock | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/composer.lock b/composer.lock index feb7ccd7b..a958c8a20 100644 --- a/composer.lock +++ b/composer.lock @@ -225,16 +225,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.1", + "version": "1.4.2", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" + "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", - "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", + "reference": "fa2aaf99e2087f013a14f7432c1cd2dd7d8f1f51", "shasum": "" }, "require": { @@ -274,9 +274,17 @@ { "url": "https://packagist.com", "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" } ], - "time": "2020-03-01T12:26:26+00:00" + "time": "2020-06-04T11:16:35+00:00" }, { "name": "justinrainbow/json-schema",