From cabf373bf455b9ac7c78412b64beeffd93a3034c Mon Sep 17 00:00:00 2001 From: Jeroen Versteeg Date: Tue, 4 Feb 2020 12:55:57 +0100 Subject: [PATCH 01/27] Added test for exclude-from-classmap (see issue #8563) --- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index ba89ccea9..5abed8657 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1525,6 +1525,8 @@ EOF; '/composersrc/ClassToExclude.php', '/composersrc/*/excluded/excsubpath', '**/excsubpath', + 'composers', // should _not_ cause exclusion of /composersrc/** + '/src-ca/', // should _not_ cause exclusion of /src-cake/** ), )); @@ -1547,7 +1549,7 @@ EOF; $this->fs->ensureDirectoryExists($this->workingDir.'/composersrc/tests'); file_put_contents($this->workingDir.'/composersrc/foo.php', 'fs->ensureDirectoryExists($this->workingDir.'/composersrc/excludedTests'); file_put_contents($this->workingDir.'/composersrc/excludedTests/bar.php', 'workingDir.'/composersrc/ClassToExclude.php', ' Date: Thu, 30 Jan 2020 17:35:33 +0000 Subject: [PATCH 02/27] Create project: add option to add the repository to the composer.json --- doc/03-cli.md | 1 + src/Composer/Command/CreateProjectCommand.php | 24 +++++++++++++++++-- src/Composer/Repository/RepositoryFactory.php | 16 +++++++++---- .../Test/Repository/RepositoryFactoryTest.php | 20 ++++++++++++++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index eccafbcc0..2b2c9b192 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -665,6 +665,7 @@ By default the command checks for the packages on packagist.org. to a `composer` repository, a path to a local `packages.json` file, or a JSON string which similar to what the [repositories](04-schema.md#repositories) key accepts. +* **--add-repository:** Add the repository option to the composer.json. * **--dev:** Install packages listed in `require-dev`. * **--no-dev:** Disables installation of require-dev packages. * **--no-scripts:** Disables the execution of the scripts defined in the root diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 2ebf3f8c0..61fe740ee 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -69,6 +69,7 @@ class CreateProjectCommand extends BaseCommand new InputOption('prefer-dist', null, InputOption::VALUE_NONE, 'Forces installation from package dist even for dev versions.'), new InputOption('repository', null, InputOption::VALUE_REQUIRED, 'Pick a different repository (as url or json config) to look for the package.'), new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'DEPRECATED: Use --repository instead.'), + new InputOption('add-repository', null, InputOption::VALUE_NONE, 'Add the repository option to the composer.json.'), new InputOption('dev', null, InputOption::VALUE_NONE, '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('no-custom-installers', null, InputOption::VALUE_NONE, 'DEPRECATED: Use no-plugins instead.'), @@ -142,11 +143,12 @@ EOT $input->getOption('no-progress'), $input->getOption('no-install'), $input->getOption('ignore-platform-reqs'), - !$input->getOption('no-secure-http') + !$input->getOption('no-secure-http'), + $input->getOption('add-repository') ); } - public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true) + public function installProject(IOInterface $io, Config $config, InputInterface $input, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repository = null, $disablePlugins = false, $noScripts = false, $noProgress = false, $noInstall = false, $ignorePlatformReqs = false, $secureHttp = true, $addRepository = false) { $oldCwd = getcwd(); @@ -162,6 +164,24 @@ EOT } $composer = Factory::create($io, null, $disablePlugins); + + // add the repository to the composer.json and use it for the install run later + if ($repository !== null && $addRepository) { + if ($composer->getLocker()->isLocked()) { + $io->writeError('Adding a repository when creating a project that provides a composer.lock file is not supported'); + + return false; + } + + $repoConfig = RepositoryFactory::configFromString($io, $composer->getConfig(), $repository, true); + $composerJsonRepositoriesConfig = $composer->getConfig()->getRepositories(); + $name = RepositoryFactory::generateRepositoryName(0, $repoConfig, $composerJsonRepositoriesConfig); + $configSource = new JsonConfigSource(new JsonFile('composer.json')); + $configSource->addRepository($name, $repoConfig); + + $composer = Factory::create($io, null, $disablePlugins); + } + $composer->getDownloadManager()->setOutputProgress(!$noProgress); $fs = new Filesystem(); diff --git a/src/Composer/Repository/RepositoryFactory.php b/src/Composer/Repository/RepositoryFactory.php index 5d3a2a61f..9bca0bd07 100644 --- a/src/Composer/Repository/RepositoryFactory.php +++ b/src/Composer/Repository/RepositoryFactory.php @@ -153,10 +153,8 @@ class RepositoryFactory if (!isset($repo['type'])) { throw new \UnexpectedValueException('Repository "'.$index.'" ('.json_encode($repo).') must have a type defined'); } - $name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index; - while (isset($repos[$name])) { - $name .= '2'; - } + + $name = self::generateRepositoryName($index, $repo, $repos); if ($repo['type'] === 'filesystem') { $repos[$name] = new FilesystemRepository($repo['json']); } else { @@ -166,4 +164,14 @@ class RepositoryFactory return $repos; } + + public static function generateRepositoryName($index, array $repo, array $existingRepos) + { + $name = is_int($index) && isset($repo['url']) ? preg_replace('{^https?://}i', '', $repo['url']) : $index; + while (isset($existingRepos[$name])) { + $name .= '2'; + } + + return $name; + } } diff --git a/tests/Composer/Test/Repository/RepositoryFactoryTest.php b/tests/Composer/Test/Repository/RepositoryFactoryTest.php index e54624415..acd666430 100644 --- a/tests/Composer/Test/Repository/RepositoryFactoryTest.php +++ b/tests/Composer/Test/Repository/RepositoryFactoryTest.php @@ -46,4 +46,24 @@ class RepositoryFactoryTest extends TestCase 'path', ), array_keys($repositoryClasses)); } + + /** + * @dataProvider generateRepositoryNameProvider + */ + public function testGenerateRepositoryName($index, array $config, array $existingRepos, $expected) + { + $this->assertSame($expected, RepositoryFactory::generateRepositoryName($index, $config, $existingRepos)); + } + + public function generateRepositoryNameProvider() + { + return array( + array(0, array(), array(), 0), + array(0, array(), array(array()), '02'), + array(0, array('url' => 'https://example.org'), array(), 'example.org'), + array(0, array('url' => 'https://example.org'), array('example.org' => array()), 'example.org2'), + array('example.org', array('url' => 'https://example.org/repository'), array(), 'example.org'), + array('example.org', array('url' => 'https://example.org/repository'), array('example.org' => array()), 'example.org2'), + ); + } } From 4d74f5ba957468afe124849ceaf38c2fcf5b871a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 12 Feb 2020 10:23:03 +0100 Subject: [PATCH 03/27] Fix exclude-from-classmap handling to avoid foo matching foo* directories, fixes #8563, refs #8575 --- src/Composer/Autoload/AutoloadGenerator.php | 2 +- tests/Composer/Test/Autoload/AutoloadGeneratorTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 940ab3f18..2bb0c98b1 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -901,7 +901,7 @@ INITIALIZER; } $resolvedPath = realpath($installPath . '/' . $updir); - $autoloads[] = preg_quote(strtr($resolvedPath, '\\', '/')) . '/' . $path; + $autoloads[] = preg_quote(strtr($resolvedPath, '\\', '/')) . '/' . $path . '($|/)'; continue; } diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index ff81581d7..055b03e1c 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1525,8 +1525,8 @@ EOF; '/composersrc/ClassToExclude.php', '/composersrc/*/excluded/excsubpath', '**/excsubpath', - 'composers', // should _not_ cause exclusion of /composersrc/** - '/src-ca/', // should _not_ cause exclusion of /src-cake/** + 'composers', // should _not_ cause exclusion of /composersrc/**, as it is equivalent to /composers/** + '/src-ca/', // should _not_ cause exclusion of /src-cake/**, as it is equivalent to /src-ca/** ), )); From 243ee9b1e41c15a32df430383339e541fed255b6 Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Wed, 12 Feb 2020 10:13:22 +0000 Subject: [PATCH 04/27] Git: log error for sync mirror with existing local copy --- src/Composer/Util/Git.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 81c685011..27bfe766f 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -97,6 +97,7 @@ class Git $auth = null; if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { + $errorMsg = $this->process->getErrorOutput(); // private github repository without ssh key access, try https with auth if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) || preg_match('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match) @@ -117,6 +118,8 @@ class Git if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } + + $errorMsg = $this->process->getErrorOutput(); } } elseif (preg_match('{^https://(bitbucket\.org)/(.*)(\.git)?$}U', $url, $match)) { //bitbucket oauth $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process); @@ -149,6 +152,8 @@ class Git if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } + + $errorMsg = $this->process->getErrorOutput(); } else { // Falling back to ssh $sshUrl = 'git@bitbucket.org:' . $match[2] . '.git'; $this->io->writeError(' No bitbucket authentication configured. Falling back to ssh.'); @@ -156,6 +161,8 @@ class Git if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } + + $errorMsg = $this->process->getErrorOutput(); } } elseif ( preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?)\.git$}i', $url, $match) @@ -186,6 +193,8 @@ class Git if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } + + $errorMsg = $this->process->getErrorOutput(); } } elseif ($this->isAuthenticationFailure($url, $match)) { // private non-github/gitlab/bitbucket repo that failed to authenticate if (strpos($match[2], '@')) { @@ -224,10 +233,11 @@ class Git return; } + + $errorMsg = $this->process->getErrorOutput(); } } - $errorMsg = $this->process->getErrorOutput(); if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } @@ -248,6 +258,8 @@ class Git }; $this->runCommand($commandCallable, $url, $dir); } catch (\Exception $e) { + $this->io->writeError('Sync mirror failed: ' . $e->getMessage() . '', true, IOInterface::DEBUG); + return false; } From 7e2679ffc1034c480b4c22a886709d27b859d410 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Thu, 13 Feb 2020 14:37:08 +0100 Subject: [PATCH 05/27] ValidateCommand: pass $isStrict to outputResult() of with-dependencies too --- src/Composer/Command/ValidateCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 7ec15ccb9..12b9f9fb8 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -108,8 +108,9 @@ EOT $file = $path . '/composer.json'; if (is_dir($path) && file_exists($file)) { list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); - $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors); + $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors, false, array(), false, $isStrict); + // $errors include publish errors when exists $depCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0); $exitCode = max($depCode, $exitCode); } From b5e41d679295734b08fb2371a1173e0018f57648 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Thu, 13 Feb 2020 14:43:26 +0100 Subject: [PATCH 06/27] ValidateCommand: always display all warnings, independently of --strict --- src/Composer/Command/ValidateCommand.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 12b9f9fb8..99a74146b 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -143,25 +143,31 @@ EOT $io->writeError('' . $name . ' is invalid, the following errors/warnings were found:'); } + $allWarnings = $warnings; + // If checking publish errors, display them as errors, otherwise just show them as warnings // Skip when it is a strict check and we don't want to check publish errors if ($checkPublish) { $errors = array_merge($errors, $publishErrors); - } elseif (!$isStrict) { - $warnings = array_merge($warnings, $publishErrors); + } else { + $allWarnings = array_merge($allWarnings, $publishErrors); } // If checking lock errors, display them as errors, otherwise just show them as warnings // Skip when it is a strict check and we don't want to check lock errors if ($checkLock) { $errors = array_merge($errors, $lockErrors); - } elseif (!$isStrict) { - $warnings = array_merge($warnings, $lockErrors); + } else { + $allWarnings = array_merge($allWarnings, $lockErrors); + } + + if (!$isStrict) { + $warnings = $allWarnings; } $messages = array( 'error' => $errors, - 'warning' => $warnings, + 'warning' => $allWarnings, ); foreach ($messages as $style => $msgs) { From 901d177179e25210333ba3ca3345cf46792031f4 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Thu, 13 Feb 2020 14:46:50 +0100 Subject: [PATCH 07/27] ValidateCommand: factorize $printSchemaUrl code --- src/Composer/Command/ValidateCommand.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 99a74146b..aa37e978b 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -126,23 +126,25 @@ EOT private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false, $isStrict = false) { + $doPrintSchemaUrl = false; + if (!$errors && !$publishErrors && !$warnings) { $io->write('' . $name . ' is valid'); } elseif (!$errors && !$publishErrors) { $io->writeError('' . $name . ' is valid, but with a few warnings'); - if ($printSchemaUrl) { - $io->writeError('See https://getcomposer.org/doc/04-schema.md for details on the schema'); - } + $doPrintSchemaUrl = $printSchemaUrl; } elseif (!$errors) { $io->writeError('' . $name . ' is valid for simple usage with composer but has'); $io->writeError('strict errors that make it unable to be published as a package:'); - if ($printSchemaUrl) { - $io->writeError('See https://getcomposer.org/doc/04-schema.md for details on the schema'); - } + $doPrintSchemaUrl = $printSchemaUrl; } else { $io->writeError('' . $name . ' is invalid, the following errors/warnings were found:'); } + if ($doPrintSchemaUrl) { + $io->writeError('See https://getcomposer.org/doc/04-schema.md for details on the schema'); + } + $allWarnings = $warnings; // If checking publish errors, display them as errors, otherwise just show them as warnings From 2f4bd85219e1c49e7dc904cca204fedae0b826b2 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Thu, 13 Feb 2020 14:57:38 +0100 Subject: [PATCH 08/27] ValidateCommand: de-invert if-elseif-else to reduce code duplication --- src/Composer/Command/ValidateCommand.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index aa37e978b..14c4a83af 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -108,6 +108,7 @@ EOT $file = $path . '/composer.json'; if (is_dir($path) && file_exists($file)) { list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); + $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors, false, array(), false, $isStrict); // $errors include publish errors when exists @@ -128,17 +129,17 @@ EOT { $doPrintSchemaUrl = false; - if (!$errors && !$publishErrors && !$warnings) { - $io->write('' . $name . ' is valid'); - } elseif (!$errors && !$publishErrors) { - $io->writeError('' . $name . ' is valid, but with a few warnings'); - $doPrintSchemaUrl = $printSchemaUrl; - } elseif (!$errors) { + if ($errors) { + $io->writeError('' . $name . ' is invalid, the following errors/warnings were found:'); + } elseif ($publishErrors) { $io->writeError('' . $name . ' is valid for simple usage with composer but has'); $io->writeError('strict errors that make it unable to be published as a package:'); $doPrintSchemaUrl = $printSchemaUrl; + } elseif ($warnings) { + $io->writeError('' . $name . ' is valid, but with a few warnings'); + $doPrintSchemaUrl = $printSchemaUrl; } else { - $io->writeError('' . $name . ' is invalid, the following errors/warnings were found:'); + $io->write('' . $name . ' is valid'); } if ($doPrintSchemaUrl) { From 9c5f1f274878c778efb4c1648d02536556ae80ba Mon Sep 17 00:00:00 2001 From: Abdouni Abdelkarim Date: Thu, 13 Feb 2020 15:06:09 +0100 Subject: [PATCH 09/27] Update 01-basic-usage.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hello, I just updated the link 😉. --- doc/01-basic-usage.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/01-basic-usage.md b/doc/01-basic-usage.md index e1faa6318..6dc2fcfd5 100644 --- a/doc/01-basic-usage.md +++ b/doc/01-basic-usage.md @@ -248,7 +248,8 @@ php composer.phar dump-autoload ``` This command will re-generate the `vendor/autoload.php` file. -See the [`dump-autoload`](03-cli.md#dump-autoload) section for more information. +See the [`dump-autoload`](03-cli.md#dump-autoload-dumpautoload-) section for +more information. Including that file will also return the autoloader instance, so you can store the return value of the include call in a variable and add more namespaces. From 35562dcd49ba52d576902b3060680f477d32cb44 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Thu, 13 Feb 2020 15:48:36 +0100 Subject: [PATCH 10/27] ValidateCommand: add a comment that we didn't forget $lockErrors --- src/Composer/Command/ValidateCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 14c4a83af..9c973758e 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -140,6 +140,7 @@ EOT $doPrintSchemaUrl = $printSchemaUrl; } else { $io->write('' . $name . ' is valid'); + // if ($lockErrors) then they will be displayed below } if ($doPrintSchemaUrl) { From 389a98a806efc03af6c9cbecdfa6c064abf6b953 Mon Sep 17 00:00:00 2001 From: Sacha Durand Date: Fri, 14 Feb 2020 03:11:33 +0100 Subject: [PATCH 11/27] Update 00-intro.md --- doc/00-intro.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 067e89061..87b1a9ca3 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -19,13 +19,14 @@ This idea is not new and Composer is strongly inspired by node's Suppose: 1. You have a project that depends on a number of libraries. -1. Some of those libraries depend on other libraries. +2. Some of those libraries depend on other libraries. Composer: 1. Enables you to declare the libraries you depend on. -1. Finds out which versions of which packages can and need to be installed, and +2. Finds out which versions of which packages can and need to be installed, and installs them (meaning it downloads them into your project). +3. you can update all your dependencies in one command. See the [Basic usage](01-basic-usage.md) chapter for more details on declaring dependencies. From 8fcb507d59a2f94a664a18d2798886f823c2c553 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 08:43:03 +0100 Subject: [PATCH 12/27] Update 00-intro.md --- doc/00-intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/00-intro.md b/doc/00-intro.md index 87b1a9ca3..897d23609 100644 --- a/doc/00-intro.md +++ b/doc/00-intro.md @@ -26,7 +26,7 @@ Composer: 1. Enables you to declare the libraries you depend on. 2. Finds out which versions of which packages can and need to be installed, and installs them (meaning it downloads them into your project). -3. you can update all your dependencies in one command. +3. You can update all your dependencies in one command. See the [Basic usage](01-basic-usage.md) chapter for more details on declaring dependencies. From 850bfcddfa946984e083b9111664d502304654ce Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 09:33:53 +0100 Subject: [PATCH 13/27] Fix json manipulation fallback handling of empty objects, fixes #8600 --- src/Composer/Config/JsonConfigSource.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 15d40d200..f455845a4 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -246,6 +246,12 @@ class JsonConfigSource implements ConfigSourceInterface $config = $this->file->read(); $this->arrayUnshiftRef($args, $config); call_user_func_array($fallback, $args); + // avoid ending up with arrays for keys that should be objects + foreach (array('require', 'require-dev', 'conflict', 'provide', 'replace', 'suggest', 'config', 'autoload', 'autoload-dev') as $linkType) { + if (isset($config[$linkType]) && $config[$linkType] === array()) { + $config[$linkType] = new \stdClass; + } + } $this->file->write($config); } From 8fd70d2dc4fc8f80794dbb1ca6f8c3e75173fe44 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 10:03:53 +0100 Subject: [PATCH 14/27] Target ClassMapGenerator ignoring of invalid PSR classes for 2.0 --- src/Composer/Autoload/ClassMapGenerator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index ef12809d5..09bbd447f 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -181,16 +181,16 @@ class ClassMapGenerator if (empty($validClasses)) { foreach ($rejectedClasses as $class) { trigger_error( - "Class $class located in ".preg_replace('{^'.preg_quote(getcwd()).'}', '.', $filePath, 1)." does not comply with $namespaceType autoloading standard. It will not autoload anymore in Composer v1.11+.", + "Class $class located in ".preg_replace('{^'.preg_quote(getcwd()).'}', '.', $filePath, 1)." does not comply with $namespaceType autoloading standard. It will not autoload anymore in Composer v2.0.", E_USER_DEPRECATED ); } - // TODO enable in Composer v1.11 or 2.0 whichever comes first + // TODO enable in Composer 2.0 //return array(); } - // TODO enable in Composer v1.11 or 2.0 whichever comes first & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses + // TODO enable in Composer 2.0 & unskip test in AutoloadGeneratorTest::testPSRToClassMapIgnoresNonPSRClasses //return $validClasses; return $classes; } From 5c4f524d6aeb3237bddfcb35bc2ef1880e3c4724 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Nov 2019 22:34:29 +0100 Subject: [PATCH 15/27] Add funding field to composer.json You can specify a list of funding options each with a type and URL. The type is used to specify the kind of funding or the platform through which funding is possible. --- doc/04-schema.md | 32 +++++++++++++++++++ res/composer-schema.json | 18 +++++++++++ src/Composer/Package/AliasPackage.php | 5 +++ src/Composer/Package/CompletePackage.php | 19 +++++++++++ .../Package/CompletePackageInterface.php | 9 ++++++ src/Composer/Package/Dumper/ArrayDumper.php | 1 + src/Composer/Package/Loader/ArrayLoader.php | 4 +++ .../Package/Loader/ValidatingArrayLoader.php | 26 +++++++++++++++ .../Composer/Test/Json/Fixtures/composer.json | 6 ++++ .../Test/Package/Dumper/ArrayDumperTest.php | 4 +++ .../Test/Package/Loader/ArrayLoaderTest.php | 3 ++ .../Loader/ValidatingArrayLoaderTest.php | 9 ++++++ 12 files changed, 136 insertions(+) diff --git a/doc/04-schema.md b/doc/04-schema.md index 6e273c1a0..e2c5369de 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -258,6 +258,38 @@ An example: Optional. +### funding + +A list of URLs to provide funding to the package authors for maintenance and +development of new functionality. + +Each entry consists of the following + +* **type:** The type of funding or the platform through which funding can be provided, e.g. patreon, opencollective, tidelift or github. +* **url:** URL to a website with details and a way to fund the package. + +An example: + +```json +{ + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/phpdoctrine" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/packagist-doctrine_doctrine-bundle" + }, + { + "type": "other", + "url": "https://www.doctrine-project.org/sponsorship.html" + } +} +``` + +Optional. + ### Package links All of the following take an object which maps package names to diff --git a/res/composer-schema.json b/res/composer-schema.json index c83109151..a74819baa 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -522,6 +522,24 @@ } } }, + "funding": { + "type": "array", + "description": "A list of options to fund the development and maintenance of the package.", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of funding or platform through which funding is possible." + }, + "url": { + "type": "string", + "description": "URL to a website with details on funding and a way to fund the package.", + "format": "uri" + } + } + } + }, "non-feature-branches": { "type": ["array"], "description": "A set of string or regex patterns for non-numeric branch names that will not be handled as feature branches.", diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index 89f197856..ee93ec497 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -377,6 +377,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface return $this->aliasOf->getSupport(); } + public function getFunding() + { + return $this->aliasOf->getFunding(); + } + public function getNotificationUrl() { return $this->aliasOf->getNotificationUrl(); diff --git a/src/Composer/Package/CompletePackage.php b/src/Composer/Package/CompletePackage.php index 5dbdb82e3..785d5817c 100644 --- a/src/Composer/Package/CompletePackage.php +++ b/src/Composer/Package/CompletePackage.php @@ -27,6 +27,7 @@ class CompletePackage extends Package implements CompletePackageInterface protected $homepage; protected $scripts = array(); protected $support = array(); + protected $funding = array(); protected $abandoned = false; /** @@ -171,6 +172,24 @@ class CompletePackage extends Package implements CompletePackageInterface return $this->support; } + /** + * Set the funding + * + * @param array $funding + */ + public function setFunding(array $funding) + { + $this->funding = $funding; + } + + /** + * {@inheritDoc} + */ + public function getFunding() + { + return $this->funding; + } + /** * @return bool */ diff --git a/src/Composer/Package/CompletePackageInterface.php b/src/Composer/Package/CompletePackageInterface.php index 4036b3cec..7782886d3 100644 --- a/src/Composer/Package/CompletePackageInterface.php +++ b/src/Composer/Package/CompletePackageInterface.php @@ -79,6 +79,15 @@ interface CompletePackageInterface extends PackageInterface */ public function getSupport(); + /** + * Returns an array of funding options for the package + * + * Each item will contain type and url keys + * + * @return array + */ + public function getFunding(); + /** * Returns if the package is abandoned or not * diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index b1e20dbf5..dece598f1 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -104,6 +104,7 @@ class ArrayDumper 'keywords', 'repositories', 'support', + 'funding', ); $data = $this->dumpValues($package, $keys, $data); diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index c269afa22..228632b42 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -198,6 +198,10 @@ class ArrayLoader implements LoaderInterface $package->setSupport($config['support']); } + if (!empty($config['funding']) && is_array($config['funding'])) { + $package->setFunding($config['funding']); + } + if (isset($config['abandoned'])) { $package->setAbandoned($config['abandoned']); } diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index 43f23236b..f02f6b165 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -193,6 +193,32 @@ class ValidatingArrayLoader implements LoaderInterface } } + if ($this->validateArray('funding') && !empty($this->config['funding'])) { + foreach ($this->config['funding'] as $key => $fundingOption) { + if (!is_array($fundingOption)) { + $this->errors[] = 'funding.'.$key.' : should be an array, '.gettype($fundingOption).' given'; + unset($this->config['funding'][$key]); + continue; + } + foreach (array('type', 'url') as $fundingData) { + if (isset($fundingOption[$fundingData]) && !is_string($fundingOption[$fundingData])) { + $this->errors[] = 'funding.'.$key.'.'.$fundingData.' : invalid value, must be a string'; + unset($this->config['funding'][$key][$fundingData]); + } + } + if (isset($fundingOption['url']) && !$this->filterUrl($fundingOption['url'])) { + $this->warnings[] = 'funding.'.$key.'.url : invalid value ('.$fundingOption['url'].'), must be an http/https URL'; + unset($this->config['funding'][$key]['url']); + } + if (empty($this->config['funding'][$key])) { + unset($this->config['funding'][$key]); + } + } + if (empty($this->config['funding'])) { + unset($this->config['funding']); + } + } + $unboundConstraint = new Constraint('=', $this->versionParser->normalize('dev-master')); $stableConstraint = new Constraint('=', '1.0.0'); diff --git a/tests/Composer/Test/Json/Fixtures/composer.json b/tests/Composer/Test/Json/Fixtures/composer.json index 66e35a7e7..ec6df7f18 100644 --- a/tests/Composer/Test/Json/Fixtures/composer.json +++ b/tests/Composer/Test/Json/Fixtures/composer.json @@ -21,6 +21,12 @@ "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/composer/issues" }, + "funding": [ + { + "type": "service-subscription", + "url": "https://packagist.com" + } + ], "require": { "php": ">=5.3.2", "justinrainbow/json-schema": "~1.4", diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index 81d94bdc1..ca7d60902 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -191,6 +191,10 @@ class ArrayDumperTest extends TestCase 'support', array('foo' => 'bar'), ), + array( + 'funding', + array('type' => 'foo', 'url' => 'https://example.com'), + ), array( 'require', array(new Link('foo', 'foo/bar', new Constraint('=', '1.0.0.0'), 'requires', '1.0.0'), new Link('bar', 'bar/baz', new Constraint('=', '1.0.0.0'), 'requires', '1.0.0')), diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 5ccaa038a..1d4f8661b 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -97,6 +97,9 @@ class ArrayLoaderTest extends TestCase 'authors' => array( array('name' => 'Bob', 'email' => 'bob@example.org', 'homepage' => 'example.org', 'role' => 'Developer'), ), + 'funding' => array( + array('type' => 'example', 'url' => 'https://example.org/fund'), + ), 'require' => array( 'foo/bar' => '1.0', ), diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 69936f4bf..2cde001ac 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -73,6 +73,15 @@ class ValidatingArrayLoaderTest extends TestCase 'rss' => 'http://example.org/rss', 'chat' => 'http://example.org/chat', ), + 'funding' => array( + array( + 'type' => 'example', + 'url' => 'https://example.org/fund' + ), + array( + 'url' => 'https://example.org/fund' + ), + ), 'require' => array( 'a/b' => '1.*', 'b/c' => '~2', From d665ea7ea962893be7d1500739f5f8721eb9b2c8 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 29 Nov 2019 01:03:55 +0100 Subject: [PATCH 16/27] After update and install commands display how many packages want funding --- src/Composer/Installer.php | 19 +++++++ .../installer/install-funding-notice.test | 54 +++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/Composer/Test/Fixtures/installer/install-funding-notice.test diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index a789b4c88..6ed247fcd 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -35,6 +35,7 @@ use Composer\IO\IOInterface; use Composer\Package\AliasPackage; use Composer\Package\BasePackage; use Composer\Package\CompletePackage; +use Composer\Package\CompletePackageInterface; use Composer\Package\Link; use Composer\Package\Loader\ArrayLoader; use Composer\Package\Dumper\ArrayDumper; @@ -313,6 +314,24 @@ class Installer } } + $fundingCount = 0; + foreach ($localRepo->getPackages() as $package) { + if ($package instanceof CompletePackageInterface && !empty($package->getFunding())) { + $fundingCount++; + } + } + if ($fundingCount) { + $this->io->writeError(array( + sprintf( + "%d package%s you are using %s looking for funding.", + $fundingCount, + 1 === $fundingCount ? '' : 's', + 1 === $fundingCount ? 'is' : 'are' + ), + 'Use the composer fund command to find out more!', + )); + } + if ($this->runScripts) { // dispatch post event $eventName = $this->update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD; diff --git a/tests/Composer/Test/Fixtures/installer/install-funding-notice.test b/tests/Composer/Test/Fixtures/installer/install-funding-notice.test new file mode 100644 index 000000000..638a31d97 --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/install-funding-notice.test @@ -0,0 +1,54 @@ +--TEST-- +Installs a simple package with exact match requirement +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { + "name": "a/a", + "version": "1.0.0", + "funding": [{ "type": "example", "url": "http://example.org/fund" }], + "require": { + "d/d": "^1.0" + } + }, + { + "name": "b/b", + "version": "1.0.0", + "funding": [{ "type": "example", "url": "http://example.org/fund" }] + }, + { + "name": "c/c", + "version": "1.0.0", + "funding": [{ "type": "example", "url": "http://example.org/fund" }] + }, + { + "name": "d/d", + "version": "1.0.0", + "require": { + "b/b": "^1.0" + } + } + ] + } + ], + "require": { + "a/a": "1.0.0" + } +} +--RUN-- +install +--EXPECT-OUTPUT-- +Loading composer repositories with package information +Updating dependencies (including require-dev) +Package operations: 3 installs, 0 updates, 0 removals +Writing lock file +Generating autoload files +2 packages you are using are looking for funding. +Use the composer fund command to find out more! +--EXPECT-- +Installing b/b (1.0.0) +Installing d/d (1.0.0) +Installing a/a (1.0.0) From 538f070a299ba0695ac854ac95f22e71354b2adc Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 2 Dec 2019 11:03:25 +0100 Subject: [PATCH 17/27] JSON Syntax fix for schema doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Andreas Möller --- doc/04-schema.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/04-schema.md b/doc/04-schema.md index e2c5369de..ecff3d996 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -285,6 +285,7 @@ An example: "type": "other", "url": "https://www.doctrine-project.org/sponsorship.html" } + ] } ``` From a222ec5b36dc496b46dc441e3b5210b2a282e17a Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Fri, 14 Feb 2020 10:19:27 +0100 Subject: [PATCH 18/27] ValidateCommand: remove actually unused code --- src/Composer/Command/ValidateCommand.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 9c973758e..860974145 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -96,7 +96,7 @@ EOT $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update `.'; } - $this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true, $isStrict); + $this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true); // $errors include publish and lock errors when exists $exitCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0); @@ -109,7 +109,7 @@ EOT if (is_dir($path) && file_exists($file)) { list($errors, $publishErrors, $warnings) = $validator->validate($file, $checkAll); - $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors, false, array(), false, $isStrict); + $this->outputResult($io, $package->getPrettyName(), $errors, $warnings, $checkPublish, $publishErrors); // $errors include publish errors when exists $depCode = $errors ? 2 : ($isStrict && $warnings ? 1 : 0); @@ -125,7 +125,7 @@ EOT return $exitCode; } - private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false, $isStrict = false) + private function outputResult($io, $name, &$errors, &$warnings, $checkPublish = false, $publishErrors = array(), $checkLock = false, $lockErrors = array(), $printSchemaUrl = false) { $doPrintSchemaUrl = false; @@ -147,10 +147,10 @@ EOT $io->writeError('See https://getcomposer.org/doc/04-schema.md for details on the schema'); } + // Avoid setting the exit code to 1 in case --strict and --no-check-publish/--no-check-lock are combined $allWarnings = $warnings; // If checking publish errors, display them as errors, otherwise just show them as warnings - // Skip when it is a strict check and we don't want to check publish errors if ($checkPublish) { $errors = array_merge($errors, $publishErrors); } else { @@ -158,17 +158,12 @@ EOT } // If checking lock errors, display them as errors, otherwise just show them as warnings - // Skip when it is a strict check and we don't want to check lock errors if ($checkLock) { $errors = array_merge($errors, $lockErrors); } else { $allWarnings = array_merge($allWarnings, $lockErrors); } - if (!$isStrict) { - $warnings = $allWarnings; - } - $messages = array( 'error' => $errors, 'warning' => $allWarnings, From ff8bf0ab822bce0cdcab1c3e1f965bfefbacec15 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 10:45:35 +0100 Subject: [PATCH 19/27] Clarify code Co-Authored-By: Guilliam Xavier --- src/Composer/Command/ValidateCommand.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 860974145..673c609c2 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -148,25 +148,25 @@ EOT } // Avoid setting the exit code to 1 in case --strict and --no-check-publish/--no-check-lock are combined - $allWarnings = $warnings; + $extraWarnings = []; // If checking publish errors, display them as errors, otherwise just show them as warnings if ($checkPublish) { $errors = array_merge($errors, $publishErrors); } else { - $allWarnings = array_merge($allWarnings, $publishErrors); + $extraWarnings = array_merge($extraWarnings, $publishErrors); } // If checking lock errors, display them as errors, otherwise just show them as warnings if ($checkLock) { $errors = array_merge($errors, $lockErrors); } else { - $allWarnings = array_merge($allWarnings, $lockErrors); + $extraWarnings = array_merge($extraWarnings, $lockErrors); } $messages = array( 'error' => $errors, - 'warning' => $allWarnings, + 'warning' => array_merge($warnings, $extraWarnings), ); foreach ($messages as $style => $msgs) { From b033a2ae81a647005c34698c14a6d0520b4f4da1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 10:55:22 +0100 Subject: [PATCH 20/27] Retrieve funding info from github into composer.json if not declared there --- src/Composer/Repository/Vcs/GitHubDriver.php | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index b8948c4c7..538303df1 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -35,6 +35,7 @@ class GitHubDriver extends VcsDriver protected $infoCache = array(); protected $isPrivate = false; private $isArchived = false; + private $fundingInfo; /** * Git Driver @@ -166,6 +167,10 @@ class GitHubDriver extends VcsDriver if (!isset($composer['abandoned']) && $this->isArchived) { $composer['abandoned'] = true; } + + if (!isset($composer['funding']) && $funding = $this->getFundingInfo()) { + $composer['funding'] = $funding; + } } if ($this->shouldCache($identifier)) { @@ -178,6 +183,35 @@ class GitHubDriver extends VcsDriver return $this->infoCache[$identifier]; } + private function getFundingInfo() + { + if (null !== $this->fundingInfo) { + return $this->fundingInfo; + } + + if ($this->originUrl !== 'github.com') { + return $this->fundingInfo = false; + } + + $graphql = 'query{repository(owner:"'.$this->owner.'",name:"'.$this->repository.'"){fundingLinks{platform,url}}}'; + $result = $this->remoteFilesystem->getContents($this->originUrl, 'https://api.github.com/graphql', false, [ + 'http' => [ + 'method' => 'POST', + 'content' => json_encode(['query' => $graphql]), + 'header' => ['Content-Type: application/json'], + ], + ]); + $result = json_decode($result, true); + + if (empty($result['data']['repository']['fundingLinks'])) { + return $this->fundingInfo = false; + } + + return $this->fundingInfo = array_map(function ($link) { + return array('type' => strtolower($link['platform']), 'url' => $link['url']); + }, $result['data']['repository']['fundingLinks']); + } + /** * {@inheritdoc} */ From a17e7e9bd3647768de21606787ea081dc5d53c48 Mon Sep 17 00:00:00 2001 From: Guilliam Xavier Date: Fri, 14 Feb 2020 10:57:19 +0100 Subject: [PATCH 21/27] ValidateCommand: fix array syntax for PHP 5.3 --- 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 673c609c2..6ab95ed1c 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -148,7 +148,7 @@ EOT } // Avoid setting the exit code to 1 in case --strict and --no-check-publish/--no-check-lock are combined - $extraWarnings = []; + $extraWarnings = array(); // If checking publish errors, display them as errors, otherwise just show them as warnings if ($checkPublish) { From f171d1fd89bf06247dc64c44e28f9d60ded4783e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 11:50:56 +0100 Subject: [PATCH 22/27] Avoid requiring auth for the funding API access --- src/Composer/Repository/Vcs/GitHubDriver.php | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 538303df1..8d2a895ab 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -194,13 +194,18 @@ class GitHubDriver extends VcsDriver } $graphql = 'query{repository(owner:"'.$this->owner.'",name:"'.$this->repository.'"){fundingLinks{platform,url}}}'; - $result = $this->remoteFilesystem->getContents($this->originUrl, 'https://api.github.com/graphql', false, [ - 'http' => [ - 'method' => 'POST', - 'content' => json_encode(['query' => $graphql]), - 'header' => ['Content-Type: application/json'], - ], - ]); + try { + $result = $this->remoteFilesystem->getContents($this->originUrl, 'https://api.github.com/graphql', false, [ + 'http' => [ + 'method' => 'POST', + 'content' => json_encode(['query' => $graphql]), + 'header' => ['Content-Type: application/json'], + ], + 'retry-auth-failure' => false, + ]); + } catch (\TransportException $e) { + return $this->fundingInfo = false; + } $result = json_decode($result, true); if (empty($result['data']['repository']['fundingLinks'])) { From 77a477fb57ba82caf5ee4e0640fd6c1dcab89807 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 14:08:26 +0100 Subject: [PATCH 23/27] Add FundCommand --- doc/03-cli.md | 5 ++ src/Composer/Command/FundCommand.php | 89 ++++++++++++++++++++++++++++ src/Composer/Console/Application.php | 1 + 3 files changed, 95 insertions(+) create mode 100644 src/Composer/Command/FundCommand.php diff --git a/doc/03-cli.md b/doc/03-cli.md index f7df60b92..29eeb398c 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -420,6 +420,11 @@ This implies `--by-package --by-suggestion`, showing both lists. * **--by-suggestion:** Groups output by suggested package. * **--no-dev:** Excludes suggestions from `require-dev` packages. +## fund + +Discover how to help fund the maintenance of your dependencies. This lists +all funding links from the installed dependencies. + ## depends (why) The `depends` command tells you which other packages depend on a certain diff --git a/src/Composer/Command/FundCommand.php b/src/Composer/Command/FundCommand.php new file mode 100644 index 000000000..3c80abc23 --- /dev/null +++ b/src/Composer/Command/FundCommand.php @@ -0,0 +1,89 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Composer\Package\CompletePackageInterface; +use Composer\Package\AliasPackage; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * @author Nicolas Grekas + * @author Jordi Boggiano + */ +class FundCommand extends BaseCommand +{ + protected function configure() + { + $this->setName('fund') + ->setDescription('Discover how to help fund the maintenance of your dependencies.') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $composer = $this->getComposer(); + + $repo = $composer->getRepositoryManager()->getLocalRepository(); + $fundings = array(); + foreach ($repo->getPackages() as $package) { + if ($package instanceof AliasPackage) { + continue; + } + if ($package instanceof CompletePackageInterface && $funding = $package->getFunding()) { + foreach ($funding as $fundingOption) { + list($vendor, $packageName) = explode('/', $package->getPrettyName()); + $url = $fundingOption['url']; + if (!empty($fundingOption['type']) && $fundingOption['type'] === 'github' && preg_match('{^https://github.com/([^/]+)$}', $url, $match)) { + $url = 'https://github.com/sponsors/'.$match[1]; + } + $fundings[$vendor][$url][] = $packageName; + } + } + } + + ksort($fundings); + + $io = $this->getIO(); + + if ($fundings) { + $prev = null; + + $io->write('The following packages were found in your dependencies which publish funding information:'); + + foreach ($fundings as $vendor => $links) { + $io->write(''); + $io->write(sprintf("%s", $vendor)); + foreach ($links as $url => $packages) { + $line = sprintf(' %s', implode(', ', $packages)); + + if ($prev !== $line) { + $io->write($line); + $prev = $line; + } + + $io->write(sprintf(' %s', $url)); + } + } + + $io->write(""); + $io->write("Please consider following these links and sponsoring the work of package authors!"); + $io->write("Thank you!"); + } else { + $io->write("No funding links were found in your package dependencies. This doesn't mean they don't need your support!"); + } + + return 0; + } +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index b884de15e..74e270a96 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -436,6 +436,7 @@ class Application extends BaseApplication new Command\ExecCommand(), new Command\OutdatedCommand(), new Command\CheckPlatformReqsCommand(), + new Command\FundCommand(), )); if ('phar:' === substr(__FILE__, 0, 5)) { From 4ebc318510362e91aa32f9ce46174844daa17fa1 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 14:10:42 +0100 Subject: [PATCH 24/27] Fix 5.3/5.4 builds --- src/Composer/Installer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 6ed247fcd..ff40b618f 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -316,7 +316,7 @@ class Installer $fundingCount = 0; foreach ($localRepo->getPackages() as $package) { - if ($package instanceof CompletePackageInterface && !empty($package->getFunding())) { + if ($package instanceof CompletePackageInterface && !$package instanceof AliasPackage && $package->getFunding()) { $fundingCount++; } } From 8dc055bec7836314a19751c2d117df3a073098d0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 14:18:26 +0100 Subject: [PATCH 25/27] Fix 5.3 syntax --- src/Composer/Repository/Vcs/GitHubDriver.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index 8d2a895ab..03025bfd9 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -195,14 +195,14 @@ class GitHubDriver extends VcsDriver $graphql = 'query{repository(owner:"'.$this->owner.'",name:"'.$this->repository.'"){fundingLinks{platform,url}}}'; try { - $result = $this->remoteFilesystem->getContents($this->originUrl, 'https://api.github.com/graphql', false, [ - 'http' => [ + $result = $this->remoteFilesystem->getContents($this->originUrl, 'https://api.github.com/graphql', false, array( + 'http' => array( 'method' => 'POST', - 'content' => json_encode(['query' => $graphql]), - 'header' => ['Content-Type: application/json'], - ], + 'content' => json_encode(array('query' => $graphql)), + 'header' => array('Content-Type: application/json'), + ), 'retry-auth-failure' => false, - ]); + )); } catch (\TransportException $e) { return $this->fundingInfo = false; } From fce60aadd17230ba42fb3e76a8588f3d170da527 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 15:06:17 +0100 Subject: [PATCH 26/27] Update changelog for 1.10 RC --- CHANGELOG.md | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6002b5a79..bec514aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,26 @@ -### [1.10.0] 2020-01-XX +### [1.10.0-RC] 2020-02-14 * Breaking: `composer global exec ...` now executes the process in the current working directory instead of executing it in the global directory. - * Warning: Added a warning when class names are being loaded by a PSR-4 or PSR-0 rule only due to classmap optimization, but would not otherwise be autoloadable. The next minor version will stop autoloading these classes so make sure you fix your autoload configs. + * Warning: Added a warning when class names are being loaded by a PSR-4 or PSR-0 rule only due to classmap optimization, but would not otherwise be autoloadable. Composer 2.0 will stop autoloading these classes so make sure you fix your autoload configs. + * Added new funding key to composer.json to describe ways your package's maintenance can be funded. This reads info from GitHub's FUNDING.yml by default so better configure it there so it shows on GitHub and Composer/Packagist + * Added `composer fund` command to show funding info of your dependencies + * Added support for --format=json output for show command when showing a single package * Added support for configuring suggestions using config command, e.g. `composer config suggest.foo/bar some text` * Added support for configuring fine-grained preferred-install using config command, e.g. `composer config preferred-install.foo/* dist` * Added `@putenv` script handler to set environment variables from composer.json for following scripts * Added `lock` option that can be set to false, in which case no composer.lock file will be generated + * Added --add-repository flag to create-project command which will persist the repo given in --repository into the composer.json of the package being installed * Added support for IPv6 addresses in NO_PROXY * Added package homepage display in the show command * Added debug info about HTTP authentications * Added Symfony 5 compatibility * Added --fixed flag to require command to make it use a fixed constraint instead of a ^x.y constraint when adding the requirement + * Fixed exclude-from-classmap matching subsets of directories e.g. foo/ was excluding foobar/ * Fixed archive command to persist file permissions inside the zip files * Fixed init/require command to avoid suggesting packages which are already selected in the search results * Fixed create-project UX issues + * Fixed filemtime for vendor/composer/* files is now only changing when the files actually change + * Fixed issues detecting docker environment with an active open_basedir ### [1.9.3] 2020-02-04 @@ -804,7 +811,7 @@ * Initial release -[1.10.0]: https://github.com/composer/composer/compare/1.9.3...1.10.0 +[1.10.0-RC]: https://github.com/composer/composer/compare/1.9.3...1.10.0-RC [1.9.3]: https://github.com/composer/composer/compare/1.9.2...1.9.3 [1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2 [1.9.1]: https://github.com/composer/composer/compare/1.9.0...1.9.1 From c49a81cf17afd954cbe9ee79836f609720a3e198 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 14 Feb 2020 15:11:21 +0100 Subject: [PATCH 27/27] Update deps --- composer.lock | 59 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/composer.lock b/composer.lock index 747a5fce4..9bb5c8a15 100644 --- a/composer.lock +++ b/composer.lock @@ -125,16 +125,16 @@ }, { "name": "composer/spdx-licenses", - "version": "1.5.2", + "version": "1.5.3", "source": { "type": "git", "url": "https://github.com/composer/spdx-licenses.git", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5" + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/7ac1e6aec371357df067f8a688c3d6974df68fa5", - "reference": "7ac1e6aec371357df067f8a688c3d6974df68fa5", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae", + "reference": "0c3e51e1880ca149682332770e25977c70cf9dae", "shasum": "" }, "require": { @@ -181,7 +181,7 @@ "spdx", "validator" ], - "time": "2019-07-29T10:31:59+00:00" + "time": "2020-02-14T07:44:31+00:00" }, { "name": "composer/xdebug-handler", @@ -508,6 +508,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" }, { @@ -565,6 +568,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" }, { @@ -615,6 +621,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" }, { @@ -664,20 +673,23 @@ ], "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" }, { "name": "symfony/polyfill-ctype", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3" + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", - "reference": "f8f0b461be3385e56d6de3dbb5a0df24c0c275e3", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", "shasum": "" }, "require": { @@ -689,7 +701,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -722,20 +734,20 @@ "polyfill", "portable" ], - "time": "2019-11-27T13:56:44+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.13.1", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f" + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7b4aab9743c30be783b73de055d24a39cf4b954f", - "reference": "7b4aab9743c30be783b73de055d24a39cf4b954f", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", "shasum": "" }, "require": { @@ -747,7 +759,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -781,7 +793,7 @@ "portable", "shim" ], - "time": "2019-11-27T14:18:11+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/process", @@ -830,6 +842,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" } ], @@ -886,6 +901,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" }, { @@ -929,12 +948,6 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], "time": "2016-01-25T08:17:30+00:00" }, {