From 1ec5fa81e42f45ed2f921365c9c6d1b3d6bb63dd Mon Sep 17 00:00:00 2001 From: Sergii Bondarenko Date: Thu, 12 Nov 2020 13:04:48 +0200 Subject: [PATCH 01/74] #9464: Add the test to reveal the issue --- .../Test/Package/Archiver/ZipArchiverTest.php | 59 ++++++++++++++----- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php index be1b5b6d1..ca179b21c 100644 --- a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php @@ -18,20 +18,44 @@ use Composer\Package\Archiver\ZipArchiver; class ZipArchiverTest extends ArchiverTest { - public function testZipArchive() + /** + * @param string $include + * + * @dataProvider provideGitignoreExcludeNegationTestCases + */ + public function testGitignoreExcludeNegation($include) + { + $this->testZipArchive(array( + 'docs/README.md' => '# The doc', + '.gitignore' => "/*\n.*\n!.git*\n$include", + )); + } + + public function provideGitignoreExcludeNegationTestCases() + { + return array( + array('!/docs'), + array('!/docs/'), + ); + } + + public function testZipArchive(array $files = array()) { if (!class_exists('ZipArchive')) { $this->markTestSkipped('Cannot run ZipArchiverTest, missing class "ZipArchive".'); } - $files = array( - 'file.txt', - 'foo/bar/baz', - 'x/baz', - 'x/includeme', - ); - if (!Platform::isWindows()) { - $files[] = 'foo' . getcwd() . '/file.txt'; + if (empty($files)) { + $files = array( + 'file.txt' => NULL, + 'foo/bar/baz' => NULL, + 'x/baz' => NULL, + 'x/includeme' => NULL, + ); + + if (!Platform::isWindows()) { + $files['foo' . getcwd() . '/file.txt'] = NULL; + } } // Set up repository $this->setupDummyRepo($files); @@ -41,12 +65,12 @@ class ZipArchiverTest extends ArchiverTest // Test archive $archiver = new ZipArchiver(); $archiver->archive($package->getSourceUrl(), $target, 'zip'); - $this->assertFileExists($target); + static::assertFileExists($target); $zip = new ZipArchive(); $res = $zip->open($target); - self::assertTrue($res, 'Failed asserting that Zip file can be opened'); - foreach ($files as $file) { - $this->assertSame('content', $zip->getFromName($file), 'Failed asserting that Zip contains ' . $file); + static::assertTrue($res, 'Failed asserting that Zip file can be opened'); + foreach ($files as $path => $content) { + static::assertSame($content, $zip->getFromName($path), 'Failed asserting that Zip contains ' . $path); } $zip->close(); @@ -57,12 +81,15 @@ class ZipArchiverTest extends ArchiverTest * Create a local dummy repository to run tests against! * @param array $files */ - protected function setupDummyRepo($files) + protected function setupDummyRepo(array &$files) { $currentWorkDir = getcwd(); chdir($this->testDir); - foreach ($files as $file) { - $this->writeFile($file, 'content', $currentWorkDir); + foreach ($files as $path => $content) { + if ($files[$path] === NULL) { + $files[$path] = 'content'; + } + $this->writeFile($path, $files[$path], $currentWorkDir); } chdir($currentWorkDir); From abbc0e8a4ea90764f32a3afe0956f8fae1e26cdd Mon Sep 17 00:00:00 2001 From: Sergii Bondarenko Date: Thu, 12 Nov 2020 14:24:08 +0200 Subject: [PATCH 02/74] #9464: Remove wrapping slashes from the gitignore rules --- .../Package/Archiver/BaseExcludeFilter.php | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index 8e69ed069..a6e668bc1 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -123,26 +123,30 @@ abstract class BaseExcludeFilter protected function generatePattern($rule) { $negate = false; - $pattern = '{'; + $pattern = ''; - if (strlen($rule) && $rule[0] === '!') { + if ($rule !== '' && $rule[0] === '!') { $negate = true; - $rule = substr($rule, 1); + $rule = ltrim($rule, '!'); } - if (strlen($rule) && $rule[0] === '/') { - $pattern .= '^/'; - $rule = substr($rule, 1); - } elseif (strlen($rule) - 1 === strpos($rule, '/')) { - $pattern .= '/'; - $rule = substr($rule, 0, -1); - } elseif (false === strpos($rule, '/')) { - $pattern .= '/'; + if ($rule !== '') { + if ($rule[0] === '/') { + $pattern = '^/'; + } else { + $first_slash_position = strpos($rule, '/'); + + if (false === $first_slash_position || strlen($rule) - 1 === $first_slash_position) { + $pattern = '/'; + } + } + + $rule = trim($rule, '/'); } // remove delimiters as well as caret (^) and dollar sign ($) from the regex - $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2) . '(?=$|/)'; + $rule = substr(Finder\Glob::toRegex($rule), 2, -2); - return array($pattern . '}', $negate, false); + return array('{'.$pattern.$rule.'(?=$|/)}', $negate, false); } } From 6f5c4fcf3eed8272a4d383523135d56e6a6fb56e Mon Sep 17 00:00:00 2001 From: Ali Shaikh Date: Sun, 27 Dec 2020 12:20:55 +0500 Subject: [PATCH 03/74] Improving Composer self-update (version upto date) info message --- src/Composer/Command/SelfUpdateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 2bc36e37a..c23d69f3e 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -185,7 +185,7 @@ EOT if (Composer::VERSION === $updateVersion) { $io->writeError( sprintf( - 'You are already using composer version %s (%s channel).', + 'You are already using the latest available composer version %s (%s channel).', $updateVersion, $channelString ) From bad4e4edbc9496ba4b5d35eca7c2fe3f13276216 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 27 Dec 2020 20:51:22 +0100 Subject: [PATCH 04/74] Fix some more wording --- src/Composer/Command/SelfUpdateCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index c23d69f3e..ea0831818 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -126,7 +126,7 @@ EOT $composeUser = posix_getpwuid(posix_geteuid()); $homeOwner = posix_getpwuid(fileowner($home)); if (isset($composeUser['name'], $homeOwner['name']) && $composeUser['name'] !== $homeOwner['name']) { - $io->writeError('You are running composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"'); + $io->writeError('You are running Composer as "'.$composeUser['name'].'", while "'.$home.'" is owned by "'.$homeOwner['name'].'"'); } } @@ -185,7 +185,7 @@ EOT if (Composer::VERSION === $updateVersion) { $io->writeError( sprintf( - 'You are already using the latest available composer version %s (%s channel).', + 'You are already using the latest available Composer version %s (%s channel).', $updateVersion, $channelString ) From e829ff80bc7312d7fa70fbd7be441863eb6dcc61 Mon Sep 17 00:00:00 2001 From: Ilya Urvachev Date: Tue, 29 Dec 2020 23:57:29 +0300 Subject: [PATCH 05/74] feat(Cache): make cache writes more atomic Fixes #9568 --- src/Composer/Cache.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index c4bb4b2eb..df45e66ed 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -115,7 +115,8 @@ class Cache $this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG); try { - return file_put_contents($this->root . $file.'.tmp', $contents) !== false && rename($this->root . $file . '.tmp', $this->root . $file); + $tempFileName = $this->root . $file . uniqid('.tmp.', true); + return file_put_contents($tempFileName, $contents) !== false && rename($tempFileName, $this->root . $file); } catch (\ErrorException $e) { $this->io->writeError('Failed to write into cache: '.$e->getMessage().'', true, IOInterface::DEBUG); if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) { From ab6e0fa9611ddd6f25e311018b205e1c659a5717 Mon Sep 17 00:00:00 2001 From: Ilya Urvachev Date: Wed, 6 Jan 2021 02:17:34 +0300 Subject: [PATCH 06/74] use `.tmp` extension for temporary files also updated `catch` block to use temporary filename --- src/Composer/Cache.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index df45e66ed..fe2b97c59 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -114,21 +114,21 @@ class Cache $this->io->writeError('Writing '.$this->root . $file.' into cache', true, IOInterface::DEBUG); + $tempFileName = $this->root . $file . uniqid('.', true) . '.tmp'; try { - $tempFileName = $this->root . $file . uniqid('.tmp.', true); return file_put_contents($tempFileName, $contents) !== false && rename($tempFileName, $this->root . $file); } catch (\ErrorException $e) { $this->io->writeError('Failed to write into cache: '.$e->getMessage().'', true, IOInterface::DEBUG); if (preg_match('{^file_put_contents\(\): Only ([0-9]+) of ([0-9]+) bytes written}', $e->getMessage(), $m)) { // Remove partial file. - unlink($this->root . $file); + unlink($tempFileName); $message = sprintf( 'Writing %1$s into cache failed after %2$u of %3$u bytes written, only %4$u bytes of free space available', - $this->root . $file, + $tempFileName, $m[1], $m[2], - @disk_free_space($this->root . dirname($file)) + @disk_free_space($this->root . dirname($tempFileName)) ); $this->io->writeError($message); From cee8e3e3b7f9239d3f66b489ae10b8f73cb6b7e0 Mon Sep 17 00:00:00 2001 From: Ilya Urvachev Date: Wed, 6 Jan 2021 15:21:45 +0300 Subject: [PATCH 07/74] fix `dirname` usage --- src/Composer/Cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Cache.php b/src/Composer/Cache.php index fe2b97c59..9590693ff 100644 --- a/src/Composer/Cache.php +++ b/src/Composer/Cache.php @@ -128,7 +128,7 @@ class Cache $tempFileName, $m[1], $m[2], - @disk_free_space($this->root . dirname($tempFileName)) + @disk_free_space(dirname($tempFileName)) ); $this->io->writeError($message); From 370e082b4a981c2f2a3ccbeed56476effc67c0be Mon Sep 17 00:00:00 2001 From: Wissem Riahi Date: Wed, 6 Jan 2021 16:05:00 +0100 Subject: [PATCH 08/74] Add saving time duration for curl HTTP requests --- src/Composer/Downloader/TransportException.php | 11 +++++++++++ src/Composer/Util/Http/CurlDownloader.php | 7 ++++++- src/Composer/Util/Http/Response.php | 15 +++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php index c682df080..b46f22e4c 100644 --- a/src/Composer/Downloader/TransportException.php +++ b/src/Composer/Downloader/TransportException.php @@ -20,6 +20,7 @@ class TransportException extends \RuntimeException protected $headers; protected $response; protected $statusCode; + protected $totalResponseTime; public function setHeaders($headers) { @@ -50,4 +51,14 @@ class TransportException extends \RuntimeException { return $this->statusCode; } + + public function getTotalResponseTime() + { + return $this->totalResponseTime; + } + + public function setTotalResponseTime($totalResponseTime) + { + $this->totalResponseTime = $totalResponseTime; + } } diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 5345f5ce3..96d20eab7 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -303,7 +303,10 @@ class CurlDownloader if (!$error && function_exists('curl_strerror')) { $error = curl_strerror($errno); } - throw new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); + + $exception = new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); + $exception->setTotalResponseTime($progress['total_time_us']); + throw $exception; } $statusCode = $progress['http_code']; rewind($job['headerHandle']); @@ -322,11 +325,13 @@ class CurlDownloader $contents = stream_get_contents($job['bodyHandle']); } $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents); + $response->setTotalResponseTime($progress['total_time_us']); $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); } else { rewind($job['bodyHandle']); $contents = stream_get_contents($job['bodyHandle']); $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents); + $response->setTotalResponseTime($progress['total_time_us']); $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); } fclose($job['bodyHandle']); diff --git a/src/Composer/Util/Http/Response.php b/src/Composer/Util/Http/Response.php index 29dd934d5..743ab68f3 100644 --- a/src/Composer/Util/Http/Response.php +++ b/src/Composer/Util/Http/Response.php @@ -20,6 +20,7 @@ class Response private $code; private $headers; private $body; + private $totalResponseTime; public function __construct(array $request, $code, array $headers, $body) { @@ -69,6 +70,20 @@ class Response return $this->body; } + /** + * Total duration time it took for the response in micro seconds + * @return int|null + */ + public function getTotalResponseTime() + { + return $this->totalResponseTime; + } + + public function setTotalResponseTime($totalResponseTime) + { + $this->totalResponseTime = $totalResponseTime; + } + public function decodeJson() { return JsonFile::parseJson($this->body, $this->request['url']); From a5d79db0a70cfd6109f3bdbc9caf6346bc326cf9 Mon Sep 17 00:00:00 2001 From: Wissem Riahi Date: Wed, 6 Jan 2021 18:00:48 +0100 Subject: [PATCH 09/74] Make sure total_time_us from curl progress is set --- src/Composer/Util/Http/CurlDownloader.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 96d20eab7..ecc46bc70 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -305,7 +305,7 @@ class CurlDownloader } $exception = new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); - $exception->setTotalResponseTime($progress['total_time_us']); + $exception->setTotalResponseTime(isset($progress['total_time_us']) ? $progress['total_time_us'] : null); throw $exception; } $statusCode = $progress['http_code']; @@ -325,13 +325,13 @@ class CurlDownloader $contents = stream_get_contents($job['bodyHandle']); } $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents); - $response->setTotalResponseTime($progress['total_time_us']); + $response->setTotalResponseTime(isset($progress['total_time_us']) ? $progress['total_time_us'] : null); $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); } else { rewind($job['bodyHandle']); $contents = stream_get_contents($job['bodyHandle']); $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents); - $response->setTotalResponseTime($progress['total_time_us']); + $response->setTotalResponseTime(isset($progress['total_time_us']) ? $progress['total_time_us'] : null); $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); } fclose($job['bodyHandle']); From bdecb4711d9afd317c38110fdd7228b0efcc55e7 Mon Sep 17 00:00:00 2001 From: Wissem Riahi Date: Thu, 7 Jan 2021 12:01:19 +0100 Subject: [PATCH 10/74] Add all curl_getinfo into the response and exception for curl requests --- .../Downloader/TransportException.php | 16 +++++++--- src/Composer/Util/Http/CurlDownloader.php | 9 ++---- src/Composer/Util/Http/CurlResponse.php | 32 +++++++++++++++++++ src/Composer/Util/Http/Response.php | 15 --------- 4 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 src/Composer/Util/Http/CurlResponse.php diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php index b46f22e4c..73454df81 100644 --- a/src/Composer/Downloader/TransportException.php +++ b/src/Composer/Downloader/TransportException.php @@ -20,7 +20,7 @@ class TransportException extends \RuntimeException protected $headers; protected $response; protected $statusCode; - protected $totalResponseTime; + protected $responseInfo; public function setHeaders($headers) { @@ -52,13 +52,19 @@ class TransportException extends \RuntimeException return $this->statusCode; } - public function getTotalResponseTime() + /** + * @return array + */ + public function getResponseInfo() { - return $this->totalResponseTime; + return $this->responseInfo; } - public function setTotalResponseTime($totalResponseTime) + /** + * @param array $responseInfo + */ + public function setResponseInfo($responseInfo) { - $this->totalResponseTime = $totalResponseTime; + $this->responseInfo = $responseInfo; } } diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index ecc46bc70..6f6d69352 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -303,9 +303,8 @@ class CurlDownloader if (!$error && function_exists('curl_strerror')) { $error = curl_strerror($errno); } - $exception = new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); - $exception->setTotalResponseTime(isset($progress['total_time_us']) ? $progress['total_time_us'] : null); + $exception->setResponseInfo($progress); throw $exception; } $statusCode = $progress['http_code']; @@ -324,14 +323,12 @@ class CurlDownloader rewind($job['bodyHandle']); $contents = stream_get_contents($job['bodyHandle']); } - $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents); - $response->setTotalResponseTime(isset($progress['total_time_us']) ? $progress['total_time_us'] : null); + $response = new CurlResponse(array('url' => $progress['url']), $statusCode, $headers, $contents, $progress); $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); } else { rewind($job['bodyHandle']); $contents = stream_get_contents($job['bodyHandle']); - $response = new Response(array('url' => $progress['url']), $statusCode, $headers, $contents); - $response->setTotalResponseTime(isset($progress['total_time_us']) ? $progress['total_time_us'] : null); + $response = new CurlResponse(array('url' => $progress['url']), $statusCode, $headers, $contents, $progress); $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); } fclose($job['bodyHandle']); diff --git a/src/Composer/Util/Http/CurlResponse.php b/src/Composer/Util/Http/CurlResponse.php new file mode 100644 index 000000000..82f725266 --- /dev/null +++ b/src/Composer/Util/Http/CurlResponse.php @@ -0,0 +1,32 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Util\Http; + +class CurlResponse extends Response +{ + private $curlInfo; + + public function __construct(array $request, $code, array $headers, $body, array $curlInfo) + { + parent::__construct($request, $code, $headers, $body); + $this->curlInfo = $curlInfo; + } + + /** + * @return array + */ + public function getCurlInfo() + { + return $this->curlInfo; + } +} diff --git a/src/Composer/Util/Http/Response.php b/src/Composer/Util/Http/Response.php index 743ab68f3..29dd934d5 100644 --- a/src/Composer/Util/Http/Response.php +++ b/src/Composer/Util/Http/Response.php @@ -20,7 +20,6 @@ class Response private $code; private $headers; private $body; - private $totalResponseTime; public function __construct(array $request, $code, array $headers, $body) { @@ -70,20 +69,6 @@ class Response return $this->body; } - /** - * Total duration time it took for the response in micro seconds - * @return int|null - */ - public function getTotalResponseTime() - { - return $this->totalResponseTime; - } - - public function setTotalResponseTime($totalResponseTime) - { - $this->totalResponseTime = $totalResponseTime; - } - public function decodeJson() { return JsonFile::parseJson($this->body, $this->request['url']); From 7f8536711367480facfd00d236422ae2c46c963a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 7 Jan 2021 23:26:29 +0100 Subject: [PATCH 11/74] Make sure an array is always returned --- src/Composer/Downloader/TransportException.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Downloader/TransportException.php b/src/Composer/Downloader/TransportException.php index 73454df81..68c73542f 100644 --- a/src/Composer/Downloader/TransportException.php +++ b/src/Composer/Downloader/TransportException.php @@ -20,7 +20,7 @@ class TransportException extends \RuntimeException protected $headers; protected $response; protected $statusCode; - protected $responseInfo; + protected $responseInfo = array(); public function setHeaders($headers) { @@ -63,7 +63,7 @@ class TransportException extends \RuntimeException /** * @param array $responseInfo */ - public function setResponseInfo($responseInfo) + public function setResponseInfo(array $responseInfo) { $this->responseInfo = $responseInfo; } From 19eef374d012df3d8bb36aa85eb687675b6d5369 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 8 Jan 2021 09:29:25 +0100 Subject: [PATCH 12/74] Clarify package link docs a little, refs #9569 --- doc/04-schema.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index f1859ea95..05149d331 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -401,19 +401,19 @@ Example: #### require -Lists packages required by this package. The package will not be installed +Map of packages required by this package. The package will not be installed unless those requirements can be met. #### require-dev ([root-only](04-schema.md#root-package)) -Lists packages required for developing this package, or running +Map of packages required for developing this package, or running tests, etc. The dev requirements of the root package are installed by default. Both `install` or `update` support the `--no-dev` option that prevents dev dependencies from being installed. #### conflict -Lists packages that conflict with this version of this package. They +Map of packages that conflict with this version of this package. They will not be allowed to be installed together with your package. Note that when specifying ranges like `<1.0 >=1.1` in a `conflict` link, @@ -423,7 +423,7 @@ probably want to go for `<1.0 || >=1.1` in this case. #### replace -Lists packages that are replaced by this package. This allows you to fork a +Map of packages that are replaced by this package. This allows you to fork a package, publish it under a different name with its own version numbers, while packages requiring the original package continue to work with your fork because it replaces the original package. @@ -441,10 +441,12 @@ that exact version, and not any other version, which would be incorrect. #### provide -List of other packages that are provided by this package. This is mostly +Map of packages that are provided by this package. This is mostly useful for implementations of common interfaces. A package could depend on -some virtual `logger-implementation` package, any library that implements -this logger interface would list it in `provide`. +some virtual package e.g. `psr/logger-implementation`, any library that implements +this logger interface would list it in `provide`. Implementors can then +be [found on Packagist.org](https://packagist.org/providers/psr/log-implementation). + Using `provide` with the name of an actual package rather than a virtual one implies that the code of that package is also shipped, in which case `replace` is generally a better choice. A common convention for packages providing an From f7bf63544045b9fc286bd222cd7ba2745922f558 Mon Sep 17 00:00:00 2001 From: Wissem Riahi Date: Fri, 8 Jan 2021 11:28:35 +0100 Subject: [PATCH 13/74] Add response info for 3xx/4xx/5xx http responses --- src/Composer/Util/Http/CurlDownloader.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 6f6d69352..de4cd3d11 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -337,7 +337,7 @@ class CurlDownloader HttpDownloader::outputWarnings($this->io, $job['origin'], json_decode($response->getBody(), true)); } - $result = $this->isAuthenticatedRetryNeeded($job, $response); + $result = $this->isAuthenticatedRetryNeeded($job, $response, $progress); if ($result['retry']) { $this->restartJob($job, $job['url'], array('storeAuth' => $result['storeAuth'])); continue; @@ -345,7 +345,7 @@ class CurlDownloader // handle 3xx redirects, 304 Not Modified is excluded if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['attributes']['redirects'] < $this->maxRedirects) { - $location = $this->handleRedirect($job, $response); + $location = $this->handleRedirect($job, $response, $progress); if ($location) { $this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1)); continue; @@ -354,7 +354,7 @@ class CurlDownloader // fail 4xx and 5xx responses and capture the response if ($statusCode >= 400 && $statusCode <= 599) { - throw $this->failResponse($job, $response, $response->getStatusMessage()); + throw $this->failResponse($job, $response, $response->getStatusMessage(), $progress); } if ($job['attributes']['storeAuth']) { @@ -417,7 +417,7 @@ class CurlDownloader } } - private function handleRedirect(array $job, Response $response) + private function handleRedirect(array $job, Response $response, array $responseInfo) { if ($locationHeader = $response->getHeader('location')) { if (parse_url($locationHeader, PHP_URL_SCHEME)) { @@ -445,10 +445,12 @@ class CurlDownloader return $targetUrl; } - throw new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')'); + $exception = new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')'); + $exception->setResponseInfo($responseInfo); + throw $exception; } - private function isAuthenticatedRetryNeeded(array $job, Response $response) + private function isAuthenticatedRetryNeeded(array $job, Response $response, array $responseInfo) { if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) { $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $response->getHeaders()); @@ -489,7 +491,7 @@ class CurlDownloader } } - throw $this->failResponse($job, $response, $needsAuthRetry); + throw $this->failResponse($job, $response, $needsAuthRetry, $responseInfo); } return array('retry' => false, 'storeAuth' => false); @@ -507,13 +509,15 @@ class CurlDownloader $this->initDownload($job['resolve'], $job['reject'], $origin, $url, $job['options'], $job['filename'], $attributes); } - private function failResponse(array $job, Response $response, $errorMessage) + private function failResponse(array $job, Response $response, $errorMessage, array $responseInfo) { if ($job['filename']) { @unlink($job['filename'].'~'); } - return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')', $response->getStatusCode()); + $exception = new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')', $response->getStatusCode()); + $exception->setResponseInfo($responseInfo); + return $exception; } private function checkCurlResult($code) From b021647e6f2aac37cb1ab7cccbc5d7e3fae2a001 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 11:02:43 +0100 Subject: [PATCH 14/74] Fix JSON manipulation issue with large files and jit enabled, fixes #9595 --- src/Composer/Json/JsonManipulator.php | 12 +- .../Test/Json/JsonManipulatorTest.php | 1306 +++++++++++++++++ 2 files changed, 1312 insertions(+), 6 deletions(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index d6b196e7d..b25a0e63b 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -20,13 +20,13 @@ use Composer\Repository\PlatformRepository; class JsonManipulator { private static $DEFINES = '(?(DEFINE) - (? -? (?= [1-9]|0(?!\d) ) \d+ (\.\d+)? ([eE] [+-]? \d+)? ) + (? -? (?= [1-9]|0(?!\d) ) \d++ (\.\d++)? ([eE] [+-]? \d++)? ) (? true | false | null ) - (? " ([^"\\\\]* | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " ) - (? \[ (?: (?&json) \s* (?: , (?&json) \s* )* )? \s* \] ) - (? \s* (?&string) \s* : (?&json) \s* ) - (? \{ (?: (?&pair) (?: , (?&pair) )* )? \s* \} ) - (? \s* (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) ) + (? " ([^"\\\\]*+ | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " ) + (? \[ (?: (?&json) \s*+ (?: , (?&json) \s*+ )*+ )? \s*+ \] ) + (? \s*+ (?&string) \s*+ : (?&json) \s*+ ) + (? \{ (?: (?&pair) (?: , (?&pair) )*+ )? \s*+ \} ) + (? \s*+ (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) ) )'; private $contents; diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 0df2545f5..61490b4c4 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -2564,6 +2564,1312 @@ class JsonManipulatorTest extends TestCase "foo/baz": "^1.0" } } +', $manipulator->getContents()); + } + + public function testLargeFileDoesNotCauseBacktrackLimitErrorGithubIssue9595() + { + $manipulator = new JsonManipulator('{ + "name": "leoloso/pop", + "require": { + "php": "^7.4|^8.0", + "ext-mbstring": "*", + "brain/cortex": "~1.0.0", + "composer/installers": "~1.0", + "composer/semver": "^1.5", + "erusev/parsedown": "^1.7", + "guzzlehttp/guzzle": "~6.3", + "jrfnl/php-cast-to-type": "^2.0", + "league/pipeline": "^1.0", + "lkwdwrd/wp-muplugin-loader": "dev-feature-composer-v2", + "obsidian/polyfill-hrtime": "^0.1", + "psr/cache": "^1.0", + "symfony/cache": "^5.1", + "symfony/config": "^5.1", + "symfony/dependency-injection": "^5.1", + "symfony/dotenv": "^5.1", + "symfony/expression-language": "^5.1", + "symfony/polyfill-php72": "^1.18", + "symfony/polyfill-php73": "^1.18", + "symfony/polyfill-php74": "^1.18", + "symfony/polyfill-php80": "^1.18", + "symfony/property-access": "^5.1", + "symfony/yaml": "^5.1" + }, + "require-dev": { + "johnpbloch/wordpress": ">=5.5", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": ">=9.3", + "rector/rector": "^0.9", + "squizlabs/php_codesniffer": "^3.0", + "symfony/var-dumper": "^5.1", + "symplify/monorepo-builder": "^9.0", + "szepeviktor/phpstan-wordpress": "^0.6.2" + }, + "autoload": { + "psr-4": { + "GraphQLAPI\\\\ConvertCaseDirectives\\\\": "layers/GraphQLAPIForWP/plugins/convert-case-directives/src", + "GraphQLAPI\\\\GraphQLAPI\\\\": "layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/src", + "GraphQLAPI\\\\SchemaFeedback\\\\": "layers/GraphQLAPIForWP/plugins/schema-feedback/src", + "GraphQLByPoP\\\\GraphQLClientsForWP\\\\": "layers/GraphQLByPoP/packages/graphql-clients-for-wp/src", + "GraphQLByPoP\\\\GraphQLEndpointForWP\\\\": "layers/GraphQLByPoP/packages/graphql-endpoint-for-wp/src", + "GraphQLByPoP\\\\GraphQLParser\\\\": "layers/GraphQLByPoP/packages/graphql-parser/src", + "GraphQLByPoP\\\\GraphQLQuery\\\\": "layers/GraphQLByPoP/packages/graphql-query/src", + "GraphQLByPoP\\\\GraphQLRequest\\\\": "layers/GraphQLByPoP/packages/graphql-request/src", + "GraphQLByPoP\\\\GraphQLServer\\\\": "layers/GraphQLByPoP/packages/graphql-server/src", + "Leoloso\\\\ExamplesForPoP\\\\": "layers/Misc/packages/examples-for-pop/src", + "PoPSchema\\\\BasicDirectives\\\\": "layers/Schema/packages/basic-directives/src", + "PoPSchema\\\\BlockMetadataWP\\\\": "layers/Schema/packages/block-metadata-for-wp/src", + "PoPSchema\\\\CDNDirective\\\\": "layers/Schema/packages/cdn-directive/src", + "PoPSchema\\\\CategoriesWP\\\\": "layers/Schema/packages/categories-wp/src", + "PoPSchema\\\\Categories\\\\": "layers/Schema/packages/categories/src", + "PoPSchema\\\\CommentMetaWP\\\\": "layers/Schema/packages/commentmeta-wp/src", + "PoPSchema\\\\CommentMeta\\\\": "layers/Schema/packages/commentmeta/src", + "PoPSchema\\\\CommentMutationsWP\\\\": "layers/Schema/packages/comment-mutations-wp/src", + "PoPSchema\\\\CommentMutations\\\\": "layers/Schema/packages/comment-mutations/src", + "PoPSchema\\\\CommentsWP\\\\": "layers/Schema/packages/comments-wp/src", + "PoPSchema\\\\Comments\\\\": "layers/Schema/packages/comments/src", + "PoPSchema\\\\ConvertCaseDirectives\\\\": "layers/Schema/packages/convert-case-directives/src", + "PoPSchema\\\\CustomPostMediaMutationsWP\\\\": "layers/Schema/packages/custompostmedia-mutations-wp/src", + "PoPSchema\\\\CustomPostMediaMutations\\\\": "layers/Schema/packages/custompostmedia-mutations/src", + "PoPSchema\\\\CustomPostMediaWP\\\\": "layers/Schema/packages/custompostmedia-wp/src", + "PoPSchema\\\\CustomPostMedia\\\\": "layers/Schema/packages/custompostmedia/src", + "PoPSchema\\\\CustomPostMetaWP\\\\": "layers/Schema/packages/custompostmeta-wp/src", + "PoPSchema\\\\CustomPostMeta\\\\": "layers/Schema/packages/custompostmeta/src", + "PoPSchema\\\\CustomPostMutationsWP\\\\": "layers/Schema/packages/custompost-mutations-wp/src", + "PoPSchema\\\\CustomPostMutations\\\\": "layers/Schema/packages/custompost-mutations/src", + "PoPSchema\\\\CustomPostsWP\\\\": "layers/Schema/packages/customposts-wp/src", + "PoPSchema\\\\CustomPosts\\\\": "layers/Schema/packages/customposts/src", + "PoPSchema\\\\EventMutationsWPEM\\\\": "layers/Schema/packages/event-mutations-wp-em/src", + "PoPSchema\\\\EventMutations\\\\": "layers/Schema/packages/event-mutations/src", + "PoPSchema\\\\EventsWPEM\\\\": "layers/Schema/packages/events-wp-em/src", + "PoPSchema\\\\Events\\\\": "layers/Schema/packages/events/src", + "PoPSchema\\\\EverythingElseWP\\\\": "layers/Schema/packages/everythingelse-wp/src", + "PoPSchema\\\\EverythingElse\\\\": "layers/Schema/packages/everythingelse/src", + "PoPSchema\\\\GenericCustomPosts\\\\": "layers/Schema/packages/generic-customposts/src", + "PoPSchema\\\\GoogleTranslateDirectiveForCustomPosts\\\\": "layers/Schema/packages/google-translate-directive-for-customposts/src", + "PoPSchema\\\\GoogleTranslateDirective\\\\": "layers/Schema/packages/google-translate-directive/src", + "PoPSchema\\\\HighlightsWP\\\\": "layers/Schema/packages/highlights-wp/src", + "PoPSchema\\\\Highlights\\\\": "layers/Schema/packages/highlights/src", + "PoPSchema\\\\LocationPostsWP\\\\": "layers/Schema/packages/locationposts-wp/src", + "PoPSchema\\\\LocationPosts\\\\": "layers/Schema/packages/locationposts/src", + "PoPSchema\\\\LocationsWPEM\\\\": "layers/Schema/packages/locations-wp-em/src", + "PoPSchema\\\\Locations\\\\": "layers/Schema/packages/locations/src", + "PoPSchema\\\\MediaWP\\\\": "layers/Schema/packages/media-wp/src", + "PoPSchema\\\\Media\\\\": "layers/Schema/packages/media/src", + "PoPSchema\\\\MenusWP\\\\": "layers/Schema/packages/menus-wp/src", + "PoPSchema\\\\Menus\\\\": "layers/Schema/packages/menus/src", + "PoPSchema\\\\MetaQueryWP\\\\": "layers/Schema/packages/metaquery-wp/src", + "PoPSchema\\\\MetaQuery\\\\": "layers/Schema/packages/metaquery/src", + "PoPSchema\\\\Meta\\\\": "layers/Schema/packages/meta/src", + "PoPSchema\\\\NotificationsWP\\\\": "layers/Schema/packages/notifications-wp/src", + "PoPSchema\\\\Notifications\\\\": "layers/Schema/packages/notifications/src", + "PoPSchema\\\\PagesWP\\\\": "layers/Schema/packages/pages-wp/src", + "PoPSchema\\\\Pages\\\\": "layers/Schema/packages/pages/src", + "PoPSchema\\\\PostMutations\\\\": "layers/Schema/packages/post-mutations/src", + "PoPSchema\\\\PostTagsWP\\\\": "layers/Schema/packages/post-tags-wp/src", + "PoPSchema\\\\PostTags\\\\": "layers/Schema/packages/post-tags/src", + "PoPSchema\\\\PostsWP\\\\": "layers/Schema/packages/posts-wp/src", + "PoPSchema\\\\Posts\\\\": "layers/Schema/packages/posts/src", + "PoPSchema\\\\QueriedObjectWP\\\\": "layers/Schema/packages/queriedobject-wp/src", + "PoPSchema\\\\QueriedObject\\\\": "layers/Schema/packages/queriedobject/src", + "PoPSchema\\\\SchemaCommons\\\\": "layers/Schema/packages/schema-commons/src", + "PoPSchema\\\\StancesWP\\\\": "layers/Schema/packages/stances-wp/src", + "PoPSchema\\\\Stances\\\\": "layers/Schema/packages/stances/src", + "PoPSchema\\\\TagsWP\\\\": "layers/Schema/packages/tags-wp/src", + "PoPSchema\\\\Tags\\\\": "layers/Schema/packages/tags/src", + "PoPSchema\\\\TaxonomiesWP\\\\": "layers/Schema/packages/taxonomies-wp/src", + "PoPSchema\\\\Taxonomies\\\\": "layers/Schema/packages/taxonomies/src", + "PoPSchema\\\\TaxonomyMetaWP\\\\": "layers/Schema/packages/taxonomymeta-wp/src", + "PoPSchema\\\\TaxonomyMeta\\\\": "layers/Schema/packages/taxonomymeta/src", + "PoPSchema\\\\TaxonomyQueryWP\\\\": "layers/Schema/packages/taxonomyquery-wp/src", + "PoPSchema\\\\TaxonomyQuery\\\\": "layers/Schema/packages/taxonomyquery/src", + "PoPSchema\\\\TranslateDirectiveACL\\\\": "layers/Schema/packages/translate-directive-acl/src", + "PoPSchema\\\\TranslateDirective\\\\": "layers/Schema/packages/translate-directive/src", + "PoPSchema\\\\UserMetaWP\\\\": "layers/Schema/packages/usermeta-wp/src", + "PoPSchema\\\\UserMeta\\\\": "layers/Schema/packages/usermeta/src", + "PoPSchema\\\\UserRolesACL\\\\": "layers/Schema/packages/user-roles-acl/src", + "PoPSchema\\\\UserRolesAccessControl\\\\": "layers/Schema/packages/user-roles-access-control/src", + "PoPSchema\\\\UserRolesWP\\\\": "layers/Schema/packages/user-roles-wp/src", + "PoPSchema\\\\UserRoles\\\\": "layers/Schema/packages/user-roles/src", + "PoPSchema\\\\UserStateAccessControl\\\\": "layers/Schema/packages/user-state-access-control/src", + "PoPSchema\\\\UserStateMutationsWP\\\\": "layers/Schema/packages/user-state-mutations-wp/src", + "PoPSchema\\\\UserStateMutations\\\\": "layers/Schema/packages/user-state-mutations/src", + "PoPSchema\\\\UserStateWP\\\\": "layers/Schema/packages/user-state-wp/src", + "PoPSchema\\\\UserState\\\\": "layers/Schema/packages/user-state/src", + "PoPSchema\\\\UsersWP\\\\": "layers/Schema/packages/users-wp/src", + "PoPSchema\\\\Users\\\\": "layers/Schema/packages/users/src", + "PoPSitesWassup\\\\CommentMutations\\\\": "layers/Wassup/packages/comment-mutations/src", + "PoPSitesWassup\\\\ContactUsMutations\\\\": "layers/Wassup/packages/contactus-mutations/src", + "PoPSitesWassup\\\\ContactUserMutations\\\\": "layers/Wassup/packages/contactuser-mutations/src", + "PoPSitesWassup\\\\CustomPostLinkMutations\\\\": "layers/Wassup/packages/custompostlink-mutations/src", + "PoPSitesWassup\\\\CustomPostMutations\\\\": "layers/Wassup/packages/custompost-mutations/src", + "PoPSitesWassup\\\\EventLinkMutations\\\\": "layers/Wassup/packages/eventlink-mutations/src", + "PoPSitesWassup\\\\EventMutations\\\\": "layers/Wassup/packages/event-mutations/src", + "PoPSitesWassup\\\\EverythingElseMutations\\\\": "layers/Wassup/packages/everythingelse-mutations/src", + "PoPSitesWassup\\\\FlagMutations\\\\": "layers/Wassup/packages/flag-mutations/src", + "PoPSitesWassup\\\\FormMutations\\\\": "layers/Wassup/packages/form-mutations/src", + "PoPSitesWassup\\\\GravityFormsMutations\\\\": "layers/Wassup/packages/gravityforms-mutations/src", + "PoPSitesWassup\\\\HighlightMutations\\\\": "layers/Wassup/packages/highlight-mutations/src", + "PoPSitesWassup\\\\LocationMutations\\\\": "layers/Wassup/packages/location-mutations/src", + "PoPSitesWassup\\\\LocationPostLinkMutations\\\\": "layers/Wassup/packages/locationpostlink-mutations/src", + "PoPSitesWassup\\\\LocationPostMutations\\\\": "layers/Wassup/packages/locationpost-mutations/src", + "PoPSitesWassup\\\\NewsletterMutations\\\\": "layers/Wassup/packages/newsletter-mutations/src", + "PoPSitesWassup\\\\NotificationMutations\\\\": "layers/Wassup/packages/notification-mutations/src", + "PoPSitesWassup\\\\PostLinkMutations\\\\": "layers/Wassup/packages/postlink-mutations/src", + "PoPSitesWassup\\\\PostMutations\\\\": "layers/Wassup/packages/post-mutations/src", + "PoPSitesWassup\\\\ShareMutations\\\\": "layers/Wassup/packages/share-mutations/src", + "PoPSitesWassup\\\\SocialNetworkMutations\\\\": "layers/Wassup/packages/socialnetwork-mutations/src", + "PoPSitesWassup\\\\StanceMutations\\\\": "layers/Wassup/packages/stance-mutations/src", + "PoPSitesWassup\\\\SystemMutations\\\\": "layers/Wassup/packages/system-mutations/src", + "PoPSitesWassup\\\\UserStateMutations\\\\": "layers/Wassup/packages/user-state-mutations/src", + "PoPSitesWassup\\\\VolunteerMutations\\\\": "layers/Wassup/packages/volunteer-mutations/src", + "PoPSitesWassup\\\\Wassup\\\\": "layers/Wassup/packages/wassup/src", + "PoP\\\\APIClients\\\\": "layers/API/packages/api-clients/src", + "PoP\\\\APIEndpointsForWP\\\\": "layers/API/packages/api-endpoints-for-wp/src", + "PoP\\\\APIEndpoints\\\\": "layers/API/packages/api-endpoints/src", + "PoP\\\\APIMirrorQuery\\\\": "layers/API/packages/api-mirrorquery/src", + "PoP\\\\API\\\\": "layers/API/packages/api/src", + "PoP\\\\AccessControl\\\\": "layers/Engine/packages/access-control/src", + "PoP\\\\ApplicationWP\\\\": "layers/SiteBuilder/packages/application-wp/src", + "PoP\\\\Application\\\\": "layers/SiteBuilder/packages/application/src", + "PoP\\\\Base36Definitions\\\\": "layers/SiteBuilder/packages/definitions-base36/src", + "PoP\\\\CacheControl\\\\": "layers/Engine/packages/cache-control/src", + "PoP\\\\ComponentModel\\\\": "layers/Engine/packages/component-model/src", + "PoP\\\\ConfigurableSchemaFeedback\\\\": "layers/Engine/packages/configurable-schema-feedback/src", + "PoP\\\\ConfigurationComponentModel\\\\": "layers/SiteBuilder/packages/component-model-configuration/src", + "PoP\\\\DefinitionPersistence\\\\": "layers/SiteBuilder/packages/definitionpersistence/src", + "PoP\\\\Definitions\\\\": "layers/Engine/packages/definitions/src", + "PoP\\\\EmojiDefinitions\\\\": "layers/SiteBuilder/packages/definitions-emoji/src", + "PoP\\\\EngineWP\\\\": "layers/Engine/packages/engine-wp/src", + "PoP\\\\Engine\\\\": "layers/Engine/packages/engine/src", + "PoP\\\\FieldQuery\\\\": "layers/Engine/packages/field-query/src", + "PoP\\\\FileStore\\\\": "layers/Engine/packages/filestore/src", + "PoP\\\\FunctionFields\\\\": "layers/Engine/packages/function-fields/src", + "PoP\\\\GraphQLAPI\\\\": "layers/API/packages/api-graphql/src", + "PoP\\\\GuzzleHelpers\\\\": "layers/Engine/packages/guzzle-helpers/src", + "PoP\\\\HooksWP\\\\": "layers/Engine/packages/hooks-wp/src", + "PoP\\\\Hooks\\\\": "layers/Engine/packages/hooks/src", + "PoP\\\\LooseContracts\\\\": "layers/Engine/packages/loosecontracts/src", + "PoP\\\\MandatoryDirectivesByConfiguration\\\\": "layers/Engine/packages/mandatory-directives-by-configuration/src", + "PoP\\\\ModuleRouting\\\\": "layers/Engine/packages/modulerouting/src", + "PoP\\\\Multisite\\\\": "layers/SiteBuilder/packages/multisite/src", + "PoP\\\\PoP\\\\": "src", + "PoP\\\\QueryParsing\\\\": "layers/Engine/packages/query-parsing/src", + "PoP\\\\RESTAPI\\\\": "layers/API/packages/api-rest/src", + "PoP\\\\ResourceLoader\\\\": "layers/SiteBuilder/packages/resourceloader/src", + "PoP\\\\Resources\\\\": "layers/SiteBuilder/packages/resources/src", + "PoP\\\\Root\\\\": "layers/Engine/packages/root/src", + "PoP\\\\RoutingWP\\\\": "layers/Engine/packages/routing-wp/src", + "PoP\\\\Routing\\\\": "layers/Engine/packages/routing/src", + "PoP\\\\SPA\\\\": "layers/SiteBuilder/packages/spa/src", + "PoP\\\\SSG\\\\": "layers/SiteBuilder/packages/static-site-generator/src", + "PoP\\\\SiteWP\\\\": "layers/SiteBuilder/packages/site-wp/src", + "PoP\\\\Site\\\\": "layers/SiteBuilder/packages/site/src", + "PoP\\\\TraceTools\\\\": "layers/Engine/packages/trace-tools/src", + "PoP\\\\TranslationWP\\\\": "layers/Engine/packages/translation-wp/src", + "PoP\\\\Translation\\\\": "layers/Engine/packages/translation/src" + } + }, + "autoload-dev": { + "psr-4": { + "GraphQLAPI\\\\ConvertCaseDirectives\\\\": "layers/GraphQLAPIForWP/plugins/convert-case-directives/tests", + "GraphQLAPI\\\\GraphQLAPI\\\\": "layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/tests", + "GraphQLAPI\\\\SchemaFeedback\\\\": "layers/GraphQLAPIForWP/plugins/schema-feedback/tests", + "GraphQLByPoP\\\\GraphQLClientsForWP\\\\": "layers/GraphQLByPoP/packages/graphql-clients-for-wp/tests", + "GraphQLByPoP\\\\GraphQLEndpointForWP\\\\": "layers/GraphQLByPoP/packages/graphql-endpoint-for-wp/tests", + "GraphQLByPoP\\\\GraphQLParser\\\\": "layers/GraphQLByPoP/packages/graphql-parser/tests", + "GraphQLByPoP\\\\GraphQLQuery\\\\": "layers/GraphQLByPoP/packages/graphql-query/tests", + "GraphQLByPoP\\\\GraphQLRequest\\\\": "layers/GraphQLByPoP/packages/graphql-request/tests", + "GraphQLByPoP\\\\GraphQLServer\\\\": "layers/GraphQLByPoP/packages/graphql-server/tests", + "Leoloso\\\\ExamplesForPoP\\\\": "layers/Misc/packages/examples-for-pop/tests", + "PoPSchema\\\\BasicDirectives\\\\": "layers/Schema/packages/basic-directives/tests", + "PoPSchema\\\\BlockMetadataWP\\\\": "layers/Schema/packages/block-metadata-for-wp/tests", + "PoPSchema\\\\CDNDirective\\\\": "layers/Schema/packages/cdn-directive/tests", + "PoPSchema\\\\CategoriesWP\\\\": "layers/Schema/packages/categories-wp/tests", + "PoPSchema\\\\Categories\\\\": "layers/Schema/packages/categories/tests", + "PoPSchema\\\\CommentMetaWP\\\\": "layers/Schema/packages/commentmeta-wp/tests", + "PoPSchema\\\\CommentMeta\\\\": "layers/Schema/packages/commentmeta/tests", + "PoPSchema\\\\CommentMutationsWP\\\\": "layers/Schema/packages/comment-mutations-wp/tests", + "PoPSchema\\\\CommentMutations\\\\": "layers/Schema/packages/comment-mutations/tests", + "PoPSchema\\\\CommentsWP\\\\": "layers/Schema/packages/comments-wp/tests", + "PoPSchema\\\\Comments\\\\": "layers/Schema/packages/comments/tests", + "PoPSchema\\\\ConvertCaseDirectives\\\\": "layers/Schema/packages/convert-case-directives/tests", + "PoPSchema\\\\CustomPostMediaMutationsWP\\\\": "layers/Schema/packages/custompostmedia-mutations-wp/tests", + "PoPSchema\\\\CustomPostMediaMutations\\\\": "layers/Schema/packages/custompostmedia-mutations/tests", + "PoPSchema\\\\CustomPostMediaWP\\\\": "layers/Schema/packages/custompostmedia-wp/tests", + "PoPSchema\\\\CustomPostMedia\\\\": "layers/Schema/packages/custompostmedia/tests", + "PoPSchema\\\\CustomPostMetaWP\\\\": "layers/Schema/packages/custompostmeta-wp/tests", + "PoPSchema\\\\CustomPostMeta\\\\": "layers/Schema/packages/custompostmeta/tests", + "PoPSchema\\\\CustomPostMutationsWP\\\\": "layers/Schema/packages/custompost-mutations-wp/tests", + "PoPSchema\\\\CustomPostMutations\\\\": "layers/Schema/packages/custompost-mutations/tests", + "PoPSchema\\\\CustomPostsWP\\\\": "layers/Schema/packages/customposts-wp/tests", + "PoPSchema\\\\CustomPosts\\\\": "layers/Schema/packages/customposts/tests", + "PoPSchema\\\\EventMutationsWPEM\\\\": "layers/Schema/packages/event-mutations-wp-em/tests", + "PoPSchema\\\\EventMutations\\\\": "layers/Schema/packages/event-mutations/tests", + "PoPSchema\\\\EventsWPEM\\\\": "layers/Schema/packages/events-wp-em/tests", + "PoPSchema\\\\Events\\\\": "layers/Schema/packages/events/tests", + "PoPSchema\\\\EverythingElseWP\\\\": "layers/Schema/packages/everythingelse-wp/tests", + "PoPSchema\\\\EverythingElse\\\\": "layers/Schema/packages/everythingelse/tests", + "PoPSchema\\\\GenericCustomPosts\\\\": "layers/Schema/packages/generic-customposts/tests", + "PoPSchema\\\\GoogleTranslateDirectiveForCustomPosts\\\\": "layers/Schema/packages/google-translate-directive-for-customposts/tests", + "PoPSchema\\\\GoogleTranslateDirective\\\\": "layers/Schema/packages/google-translate-directive/tests", + "PoPSchema\\\\HighlightsWP\\\\": "layers/Schema/packages/highlights-wp/tests", + "PoPSchema\\\\Highlights\\\\": "layers/Schema/packages/highlights/tests", + "PoPSchema\\\\LocationPostsWP\\\\": "layers/Schema/packages/locationposts-wp/tests", + "PoPSchema\\\\LocationPosts\\\\": "layers/Schema/packages/locationposts/tests", + "PoPSchema\\\\LocationsWPEM\\\\": "layers/Schema/packages/locations-wp-em/tests", + "PoPSchema\\\\Locations\\\\": "layers/Schema/packages/locations/tests", + "PoPSchema\\\\MediaWP\\\\": "layers/Schema/packages/media-wp/tests", + "PoPSchema\\\\Media\\\\": "layers/Schema/packages/media/tests", + "PoPSchema\\\\MenusWP\\\\": "layers/Schema/packages/menus-wp/tests", + "PoPSchema\\\\Menus\\\\": "layers/Schema/packages/menus/tests", + "PoPSchema\\\\MetaQueryWP\\\\": "layers/Schema/packages/metaquery-wp/tests", + "PoPSchema\\\\MetaQuery\\\\": "layers/Schema/packages/metaquery/tests", + "PoPSchema\\\\Meta\\\\": "layers/Schema/packages/meta/tests", + "PoPSchema\\\\NotificationsWP\\\\": "layers/Schema/packages/notifications-wp/tests", + "PoPSchema\\\\Notifications\\\\": "layers/Schema/packages/notifications/tests", + "PoPSchema\\\\PagesWP\\\\": "layers/Schema/packages/pages-wp/tests", + "PoPSchema\\\\Pages\\\\": "layers/Schema/packages/pages/tests", + "PoPSchema\\\\PostMutations\\\\": "layers/Schema/packages/post-mutations/tests", + "PoPSchema\\\\PostTagsWP\\\\": "layers/Schema/packages/post-tags-wp/tests", + "PoPSchema\\\\PostTags\\\\": "layers/Schema/packages/post-tags/tests", + "PoPSchema\\\\PostsWP\\\\": "layers/Schema/packages/posts-wp/tests", + "PoPSchema\\\\Posts\\\\": "layers/Schema/packages/posts/tests", + "PoPSchema\\\\QueriedObjectWP\\\\": "layers/Schema/packages/queriedobject-wp/tests", + "PoPSchema\\\\QueriedObject\\\\": "layers/Schema/packages/queriedobject/tests", + "PoPSchema\\\\SchemaCommons\\\\": "layers/Schema/packages/schema-commons/tests", + "PoPSchema\\\\StancesWP\\\\": "layers/Schema/packages/stances-wp/tests", + "PoPSchema\\\\Stances\\\\": "layers/Schema/packages/stances/tests", + "PoPSchema\\\\TagsWP\\\\": "layers/Schema/packages/tags-wp/tests", + "PoPSchema\\\\Tags\\\\": "layers/Schema/packages/tags/tests", + "PoPSchema\\\\TaxonomiesWP\\\\": "layers/Schema/packages/taxonomies-wp/tests", + "PoPSchema\\\\Taxonomies\\\\": "layers/Schema/packages/taxonomies/tests", + "PoPSchema\\\\TaxonomyMetaWP\\\\": "layers/Schema/packages/taxonomymeta-wp/tests", + "PoPSchema\\\\TaxonomyMeta\\\\": "layers/Schema/packages/taxonomymeta/tests", + "PoPSchema\\\\TaxonomyQueryWP\\\\": "layers/Schema/packages/taxonomyquery-wp/tests", + "PoPSchema\\\\TaxonomyQuery\\\\": "layers/Schema/packages/taxonomyquery/tests", + "PoPSchema\\\\TranslateDirectiveACL\\\\": "layers/Schema/packages/translate-directive-acl/tests", + "PoPSchema\\\\TranslateDirective\\\\": "layers/Schema/packages/translate-directive/tests", + "PoPSchema\\\\UserMetaWP\\\\": "layers/Schema/packages/usermeta-wp/tests", + "PoPSchema\\\\UserMeta\\\\": "layers/Schema/packages/usermeta/tests", + "PoPSchema\\\\UserRolesACL\\\\": "layers/Schema/packages/user-roles-acl/tests", + "PoPSchema\\\\UserRolesAccessControl\\\\": "layers/Schema/packages/user-roles-access-control/tests", + "PoPSchema\\\\UserRolesWP\\\\": "layers/Schema/packages/user-roles-wp/tests", + "PoPSchema\\\\UserRoles\\\\": "layers/Schema/packages/user-roles/tests", + "PoPSchema\\\\UserStateAccessControl\\\\": "layers/Schema/packages/user-state-access-control/tests", + "PoPSchema\\\\UserStateMutationsWP\\\\": "layers/Schema/packages/user-state-mutations-wp/tests", + "PoPSchema\\\\UserStateMutations\\\\": "layers/Schema/packages/user-state-mutations/tests", + "PoPSchema\\\\UserStateWP\\\\": "layers/Schema/packages/user-state-wp/tests", + "PoPSchema\\\\UserState\\\\": "layers/Schema/packages/user-state/tests", + "PoPSchema\\\\UsersWP\\\\": "layers/Schema/packages/users-wp/tests", + "PoPSchema\\\\Users\\\\": "layers/Schema/packages/users/tests", + "PoPSitesWassup\\\\CommentMutations\\\\": "layers/Wassup/packages/comment-mutations/tests", + "PoPSitesWassup\\\\ContactUsMutations\\\\": "layers/Wassup/packages/contactus-mutations/tests", + "PoPSitesWassup\\\\ContactUserMutations\\\\": "layers/Wassup/packages/contactuser-mutations/tests", + "PoPSitesWassup\\\\CustomPostLinkMutations\\\\": "layers/Wassup/packages/custompostlink-mutations/tests", + "PoPSitesWassup\\\\CustomPostMutations\\\\": "layers/Wassup/packages/custompost-mutations/tests", + "PoPSitesWassup\\\\EventLinkMutations\\\\": "layers/Wassup/packages/eventlink-mutations/tests", + "PoPSitesWassup\\\\EventMutations\\\\": "layers/Wassup/packages/event-mutations/tests", + "PoPSitesWassup\\\\EverythingElseMutations\\\\": "layers/Wassup/packages/everythingelse-mutations/tests", + "PoPSitesWassup\\\\FlagMutations\\\\": "layers/Wassup/packages/flag-mutations/tests", + "PoPSitesWassup\\\\FormMutations\\\\": "layers/Wassup/packages/form-mutations/tests", + "PoPSitesWassup\\\\GravityFormsMutations\\\\": "layers/Wassup/packages/gravityforms-mutations/tests", + "PoPSitesWassup\\\\HighlightMutations\\\\": "layers/Wassup/packages/highlight-mutations/tests", + "PoPSitesWassup\\\\LocationMutations\\\\": "layers/Wassup/packages/location-mutations/tests", + "PoPSitesWassup\\\\LocationPostLinkMutations\\\\": "layers/Wassup/packages/locationpostlink-mutations/tests", + "PoPSitesWassup\\\\LocationPostMutations\\\\": "layers/Wassup/packages/locationpost-mutations/tests", + "PoPSitesWassup\\\\NewsletterMutations\\\\": "layers/Wassup/packages/newsletter-mutations/tests", + "PoPSitesWassup\\\\NotificationMutations\\\\": "layers/Wassup/packages/notification-mutations/tests", + "PoPSitesWassup\\\\PostLinkMutations\\\\": "layers/Wassup/packages/postlink-mutations/tests", + "PoPSitesWassup\\\\PostMutations\\\\": "layers/Wassup/packages/post-mutations/tests", + "PoPSitesWassup\\\\ShareMutations\\\\": "layers/Wassup/packages/share-mutations/tests", + "PoPSitesWassup\\\\SocialNetworkMutations\\\\": "layers/Wassup/packages/socialnetwork-mutations/tests", + "PoPSitesWassup\\\\StanceMutations\\\\": "layers/Wassup/packages/stance-mutations/tests", + "PoPSitesWassup\\\\SystemMutations\\\\": "layers/Wassup/packages/system-mutations/tests", + "PoPSitesWassup\\\\UserStateMutations\\\\": "layers/Wassup/packages/user-state-mutations/tests", + "PoPSitesWassup\\\\VolunteerMutations\\\\": "layers/Wassup/packages/volunteer-mutations/tests", + "PoPSitesWassup\\\\Wassup\\\\": "layers/Wassup/packages/wassup/tests", + "PoP\\\\APIClients\\\\": "layers/API/packages/api-clients/tests", + "PoP\\\\APIEndpointsForWP\\\\": "layers/API/packages/api-endpoints-for-wp/tests", + "PoP\\\\APIEndpoints\\\\": "layers/API/packages/api-endpoints/tests", + "PoP\\\\APIMirrorQuery\\\\": "layers/API/packages/api-mirrorquery/tests", + "PoP\\\\API\\\\": "layers/API/packages/api/tests", + "PoP\\\\AccessControl\\\\": "layers/Engine/packages/access-control/tests", + "PoP\\\\ApplicationWP\\\\": "layers/SiteBuilder/packages/application-wp/tests", + "PoP\\\\Application\\\\": "layers/SiteBuilder/packages/application/tests", + "PoP\\\\Base36Definitions\\\\": "layers/SiteBuilder/packages/definitions-base36/tests", + "PoP\\\\CacheControl\\\\": "layers/Engine/packages/cache-control/tests", + "PoP\\\\ComponentModel\\\\": "layers/Engine/packages/component-model/tests", + "PoP\\\\ConfigurableSchemaFeedback\\\\": "layers/Engine/packages/configurable-schema-feedback/tests", + "PoP\\\\ConfigurationComponentModel\\\\": "layers/SiteBuilder/packages/component-model-configuration/tests", + "PoP\\\\DefinitionPersistence\\\\": "layers/SiteBuilder/packages/definitionpersistence/tests", + "PoP\\\\Definitions\\\\": "layers/Engine/packages/definitions/tests", + "PoP\\\\EmojiDefinitions\\\\": "layers/SiteBuilder/packages/definitions-emoji/tests", + "PoP\\\\EngineWP\\\\": "layers/Engine/packages/engine-wp/tests", + "PoP\\\\Engine\\\\": "layers/Engine/packages/engine/tests", + "PoP\\\\FieldQuery\\\\": "layers/Engine/packages/field-query/tests", + "PoP\\\\FileStore\\\\": "layers/Engine/packages/filestore/tests", + "PoP\\\\FunctionFields\\\\": "layers/Engine/packages/function-fields/tests", + "PoP\\\\GraphQLAPI\\\\": "layers/API/packages/api-graphql/tests", + "PoP\\\\GuzzleHelpers\\\\": "layers/Engine/packages/guzzle-helpers/tests", + "PoP\\\\HooksWP\\\\": "layers/Engine/packages/hooks-wp/tests", + "PoP\\\\Hooks\\\\": "layers/Engine/packages/hooks/tests", + "PoP\\\\LooseContracts\\\\": "layers/Engine/packages/loosecontracts/tests", + "PoP\\\\MandatoryDirectivesByConfiguration\\\\": "layers/Engine/packages/mandatory-directives-by-configuration/tests", + "PoP\\\\ModuleRouting\\\\": "layers/Engine/packages/modulerouting/tests", + "PoP\\\\Multisite\\\\": "layers/SiteBuilder/packages/multisite/tests", + "PoP\\\\QueryParsing\\\\": "layers/Engine/packages/query-parsing/tests", + "PoP\\\\RESTAPI\\\\": "layers/API/packages/api-rest/tests", + "PoP\\\\ResourceLoader\\\\": "layers/SiteBuilder/packages/resourceloader/tests", + "PoP\\\\Resources\\\\": "layers/SiteBuilder/packages/resources/tests", + "PoP\\\\Root\\\\": "layers/Engine/packages/root/tests", + "PoP\\\\RoutingWP\\\\": "layers/Engine/packages/routing-wp/tests", + "PoP\\\\Routing\\\\": "layers/Engine/packages/routing/tests", + "PoP\\\\SPA\\\\": "layers/SiteBuilder/packages/spa/tests", + "PoP\\\\SSG\\\\": "layers/SiteBuilder/packages/static-site-generator/tests", + "PoP\\\\SiteWP\\\\": "layers/SiteBuilder/packages/site-wp/tests", + "PoP\\\\Site\\\\": "layers/SiteBuilder/packages/site/tests", + "PoP\\\\TraceTools\\\\": "layers/Engine/packages/trace-tools/tests", + "PoP\\\\TranslationWP\\\\": "layers/Engine/packages/translation-wp/tests", + "PoP\\\\Translation\\\\": "layers/Engine/packages/translation/tests" + } + }, + "extra": { + "wordpress-install-dir": "vendor/wordpress/wordpress", + "merge-plugin": { + "include": [ + "composer.local.json" + ], + "recurse": true, + "replace": false, + "ignore-duplicates": false, + "merge-dev": true, + "merge-extra": false, + "merge-extra-deep": false, + "merge-scripts": false + } + }, + "replace": { + "getpop/access-control": "self.version", + "getpop/api": "self.version", + "getpop/api-clients": "self.version", + "getpop/api-endpoints": "self.version", + "getpop/api-endpoints-for-wp": "self.version", + "getpop/api-graphql": "self.version", + "getpop/api-mirrorquery": "self.version", + "getpop/api-rest": "self.version", + "getpop/application": "self.version", + "getpop/application-wp": "self.version", + "getpop/cache-control": "self.version", + "getpop/component-model": "self.version", + "getpop/component-model-configuration": "self.version", + "getpop/configurable-schema-feedback": "self.version", + "getpop/definitionpersistence": "self.version", + "getpop/definitions": "self.version", + "getpop/definitions-base36": "self.version", + "getpop/definitions-emoji": "self.version", + "getpop/engine": "self.version", + "getpop/engine-wp": "self.version", + "getpop/engine-wp-bootloader": "self.version", + "getpop/field-query": "self.version", + "getpop/filestore": "self.version", + "getpop/function-fields": "self.version", + "getpop/guzzle-helpers": "self.version", + "getpop/hooks": "self.version", + "getpop/hooks-wp": "self.version", + "getpop/loosecontracts": "self.version", + "getpop/mandatory-directives-by-configuration": "self.version", + "getpop/migrate-api": "self.version", + "getpop/migrate-api-graphql": "self.version", + "getpop/migrate-component-model": "self.version", + "getpop/migrate-component-model-configuration": "self.version", + "getpop/migrate-engine": "self.version", + "getpop/migrate-engine-wp": "self.version", + "getpop/migrate-static-site-generator": "self.version", + "getpop/modulerouting": "self.version", + "getpop/multisite": "self.version", + "getpop/query-parsing": "self.version", + "getpop/resourceloader": "self.version", + "getpop/resources": "self.version", + "getpop/root": "self.version", + "getpop/routing": "self.version", + "getpop/routing-wp": "self.version", + "getpop/site": "self.version", + "getpop/site-wp": "self.version", + "getpop/spa": "self.version", + "getpop/static-site-generator": "self.version", + "getpop/trace-tools": "self.version", + "getpop/translation": "self.version", + "getpop/translation-wp": "self.version", + "graphql-api/convert-case-directives": "self.version", + "graphql-api/graphql-api-for-wp": "self.version", + "graphql-api/schema-feedback": "self.version", + "graphql-by-pop/graphql-clients-for-wp": "self.version", + "graphql-by-pop/graphql-endpoint-for-wp": "self.version", + "graphql-by-pop/graphql-parser": "self.version", + "graphql-by-pop/graphql-query": "self.version", + "graphql-by-pop/graphql-request": "self.version", + "graphql-by-pop/graphql-server": "self.version", + "leoloso/examples-for-pop": "self.version", + "pop-migrate-everythingelse/cssconverter": "self.version", + "pop-migrate-everythingelse/ssr": "self.version", + "pop-schema/basic-directives": "self.version", + "pop-schema/block-metadata-for-wp": "self.version", + "pop-schema/categories": "self.version", + "pop-schema/categories-wp": "self.version", + "pop-schema/cdn-directive": "self.version", + "pop-schema/comment-mutations": "self.version", + "pop-schema/comment-mutations-wp": "self.version", + "pop-schema/commentmeta": "self.version", + "pop-schema/commentmeta-wp": "self.version", + "pop-schema/comments": "self.version", + "pop-schema/comments-wp": "self.version", + "pop-schema/convert-case-directives": "self.version", + "pop-schema/custompost-mutations": "self.version", + "pop-schema/custompost-mutations-wp": "self.version", + "pop-schema/custompostmedia": "self.version", + "pop-schema/custompostmedia-mutations": "self.version", + "pop-schema/custompostmedia-mutations-wp": "self.version", + "pop-schema/custompostmedia-wp": "self.version", + "pop-schema/custompostmeta": "self.version", + "pop-schema/custompostmeta-wp": "self.version", + "pop-schema/customposts": "self.version", + "pop-schema/customposts-wp": "self.version", + "pop-schema/event-mutations": "self.version", + "pop-schema/event-mutations-wp-em": "self.version", + "pop-schema/events": "self.version", + "pop-schema/events-wp-em": "self.version", + "pop-schema/everythingelse": "self.version", + "pop-schema/everythingelse-wp": "self.version", + "pop-schema/generic-customposts": "self.version", + "pop-schema/google-translate-directive": "self.version", + "pop-schema/google-translate-directive-for-customposts": "self.version", + "pop-schema/highlights": "self.version", + "pop-schema/highlights-wp": "self.version", + "pop-schema/locationposts": "self.version", + "pop-schema/locationposts-wp": "self.version", + "pop-schema/locations": "self.version", + "pop-schema/locations-wp-em": "self.version", + "pop-schema/media": "self.version", + "pop-schema/media-wp": "self.version", + "pop-schema/menus": "self.version", + "pop-schema/menus-wp": "self.version", + "pop-schema/meta": "self.version", + "pop-schema/metaquery": "self.version", + "pop-schema/metaquery-wp": "self.version", + "pop-schema/migrate-categories": "self.version", + "pop-schema/migrate-categories-wp": "self.version", + "pop-schema/migrate-commentmeta": "self.version", + "pop-schema/migrate-commentmeta-wp": "self.version", + "pop-schema/migrate-comments": "self.version", + "pop-schema/migrate-comments-wp": "self.version", + "pop-schema/migrate-custompostmedia": "self.version", + "pop-schema/migrate-custompostmedia-wp": "self.version", + "pop-schema/migrate-custompostmeta": "self.version", + "pop-schema/migrate-custompostmeta-wp": "self.version", + "pop-schema/migrate-customposts": "self.version", + "pop-schema/migrate-customposts-wp": "self.version", + "pop-schema/migrate-events": "self.version", + "pop-schema/migrate-events-wp-em": "self.version", + "pop-schema/migrate-everythingelse": "self.version", + "pop-schema/migrate-locations": "self.version", + "pop-schema/migrate-locations-wp-em": "self.version", + "pop-schema/migrate-media": "self.version", + "pop-schema/migrate-media-wp": "self.version", + "pop-schema/migrate-meta": "self.version", + "pop-schema/migrate-metaquery": "self.version", + "pop-schema/migrate-metaquery-wp": "self.version", + "pop-schema/migrate-pages": "self.version", + "pop-schema/migrate-pages-wp": "self.version", + "pop-schema/migrate-post-tags": "self.version", + "pop-schema/migrate-post-tags-wp": "self.version", + "pop-schema/migrate-posts": "self.version", + "pop-schema/migrate-posts-wp": "self.version", + "pop-schema/migrate-queriedobject": "self.version", + "pop-schema/migrate-queriedobject-wp": "self.version", + "pop-schema/migrate-tags": "self.version", + "pop-schema/migrate-tags-wp": "self.version", + "pop-schema/migrate-taxonomies": "self.version", + "pop-schema/migrate-taxonomies-wp": "self.version", + "pop-schema/migrate-taxonomymeta": "self.version", + "pop-schema/migrate-taxonomymeta-wp": "self.version", + "pop-schema/migrate-taxonomyquery": "self.version", + "pop-schema/migrate-taxonomyquery-wp": "self.version", + "pop-schema/migrate-usermeta": "self.version", + "pop-schema/migrate-usermeta-wp": "self.version", + "pop-schema/migrate-users": "self.version", + "pop-schema/migrate-users-wp": "self.version", + "pop-schema/notifications": "self.version", + "pop-schema/notifications-wp": "self.version", + "pop-schema/pages": "self.version", + "pop-schema/pages-wp": "self.version", + "pop-schema/post-mutations": "self.version", + "pop-schema/post-tags": "self.version", + "pop-schema/post-tags-wp": "self.version", + "pop-schema/posts": "self.version", + "pop-schema/posts-wp": "self.version", + "pop-schema/queriedobject": "self.version", + "pop-schema/queriedobject-wp": "self.version", + "pop-schema/schema-commons": "self.version", + "pop-schema/stances": "self.version", + "pop-schema/stances-wp": "self.version", + "pop-schema/tags": "self.version", + "pop-schema/tags-wp": "self.version", + "pop-schema/taxonomies": "self.version", + "pop-schema/taxonomies-wp": "self.version", + "pop-schema/taxonomymeta": "self.version", + "pop-schema/taxonomymeta-wp": "self.version", + "pop-schema/taxonomyquery": "self.version", + "pop-schema/taxonomyquery-wp": "self.version", + "pop-schema/translate-directive": "self.version", + "pop-schema/translate-directive-acl": "self.version", + "pop-schema/user-roles": "self.version", + "pop-schema/user-roles-access-control": "self.version", + "pop-schema/user-roles-acl": "self.version", + "pop-schema/user-roles-wp": "self.version", + "pop-schema/user-state": "self.version", + "pop-schema/user-state-access-control": "self.version", + "pop-schema/user-state-mutations": "self.version", + "pop-schema/user-state-mutations-wp": "self.version", + "pop-schema/user-state-wp": "self.version", + "pop-schema/usermeta": "self.version", + "pop-schema/usermeta-wp": "self.version", + "pop-schema/users": "self.version", + "pop-schema/users-wp": "self.version", + "pop-sites-wassup/comment-mutations": "self.version", + "pop-sites-wassup/contactus-mutations": "self.version", + "pop-sites-wassup/contactuser-mutations": "self.version", + "pop-sites-wassup/custompost-mutations": "self.version", + "pop-sites-wassup/custompostlink-mutations": "self.version", + "pop-sites-wassup/event-mutations": "self.version", + "pop-sites-wassup/eventlink-mutations": "self.version", + "pop-sites-wassup/everythingelse-mutations": "self.version", + "pop-sites-wassup/flag-mutations": "self.version", + "pop-sites-wassup/form-mutations": "self.version", + "pop-sites-wassup/gravityforms-mutations": "self.version", + "pop-sites-wassup/highlight-mutations": "self.version", + "pop-sites-wassup/location-mutations": "self.version", + "pop-sites-wassup/locationpost-mutations": "self.version", + "pop-sites-wassup/locationpostlink-mutations": "self.version", + "pop-sites-wassup/newsletter-mutations": "self.version", + "pop-sites-wassup/notification-mutations": "self.version", + "pop-sites-wassup/post-mutations": "self.version", + "pop-sites-wassup/postlink-mutations": "self.version", + "pop-sites-wassup/share-mutations": "self.version", + "pop-sites-wassup/socialnetwork-mutations": "self.version", + "pop-sites-wassup/stance-mutations": "self.version", + "pop-sites-wassup/system-mutations": "self.version", + "pop-sites-wassup/user-state-mutations": "self.version", + "pop-sites-wassup/volunteer-mutations": "self.version", + "pop-sites-wassup/wassup": "self.version" + }, + "authors": [ + { + "name": "Leonardo Losoviz", + "email": "leo@getpop.org", + "homepage": "https://getpop.org" + } + ], + "description": "Monorepo for all the PoP packages", + "license": "GPL-2.0-or-later", + "config": { + "sort-packages": true + }, + "repositories": [ + { + "type": "composer", + "url": "https://wpackagist.org" + }, + { + "type": "vcs", + "url": "https://github.com/leoloso/wp-muplugin-loader.git" + }, + { + "type": "vcs", + "url": "https://github.com/mcaskill/composer-merge-plugin.git" + } + ], + "scripts": { + "test": "phpunit", + "check-style": "phpcs -n src $(monorepo-builder source-packages --subfolder=src --subfolder=tests)", + "fix-style": "phpcbf -n src $(monorepo-builder source-packages --subfolder=src --subfolder=tests)", + "analyse": "ci/phpstan.sh \\". $(monorepo-builder source-packages --skip-unmigrated)\\"", + "preview-src-downgrade": "rector process $(monorepo-builder source-packages --subfolder=src) --config=rector-downgrade-code.php --ansi --dry-run || true", + "preview-vendor-downgrade": "layers/Engine/packages/root/ci/downgrade_code.sh 7.1 rector-downgrade-code.php --dry-run || true", + "preview-code-downgrade": [ + "@preview-src-downgrade", + "@preview-vendor-downgrade" + ], + "build-server": [ + "lando init --source remote --remote-url https://wordpress.org/latest.tar.gz --recipe wordpress --webroot wordpress --name graphql-api-dev", + "@start-server" + ], + "start-server": [ + "cd layers/GraphQLAPIForWP/plugins/graphql-api-for-wp && composer install", + "lando start" + ], + "rebuild-server": "lando rebuild -y", + "merge-monorepo": "monorepo-builder merge --ansi", + "propagate-monorepo": "monorepo-builder propagate --ansi", + "validate-monorepo": "monorepo-builder validate --ansi", + "release": "monorepo-builder release patch --ansi" + }, + "minimum-stability": "dev", + "prefer-stable": true +}'); + + $this->assertTrue($manipulator->addSubNode('config', 'platform-check', false)); + $this->assertEquals('{ + "name": "leoloso/pop", + "require": { + "php": "^7.4|^8.0", + "ext-mbstring": "*", + "brain/cortex": "~1.0.0", + "composer/installers": "~1.0", + "composer/semver": "^1.5", + "erusev/parsedown": "^1.7", + "guzzlehttp/guzzle": "~6.3", + "jrfnl/php-cast-to-type": "^2.0", + "league/pipeline": "^1.0", + "lkwdwrd/wp-muplugin-loader": "dev-feature-composer-v2", + "obsidian/polyfill-hrtime": "^0.1", + "psr/cache": "^1.0", + "symfony/cache": "^5.1", + "symfony/config": "^5.1", + "symfony/dependency-injection": "^5.1", + "symfony/dotenv": "^5.1", + "symfony/expression-language": "^5.1", + "symfony/polyfill-php72": "^1.18", + "symfony/polyfill-php73": "^1.18", + "symfony/polyfill-php74": "^1.18", + "symfony/polyfill-php80": "^1.18", + "symfony/property-access": "^5.1", + "symfony/yaml": "^5.1" + }, + "require-dev": { + "johnpbloch/wordpress": ">=5.5", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": ">=9.3", + "rector/rector": "^0.9", + "squizlabs/php_codesniffer": "^3.0", + "symfony/var-dumper": "^5.1", + "symplify/monorepo-builder": "^9.0", + "szepeviktor/phpstan-wordpress": "^0.6.2" + }, + "autoload": { + "psr-4": { + "GraphQLAPI\\\\ConvertCaseDirectives\\\\": "layers/GraphQLAPIForWP/plugins/convert-case-directives/src", + "GraphQLAPI\\\\GraphQLAPI\\\\": "layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/src", + "GraphQLAPI\\\\SchemaFeedback\\\\": "layers/GraphQLAPIForWP/plugins/schema-feedback/src", + "GraphQLByPoP\\\\GraphQLClientsForWP\\\\": "layers/GraphQLByPoP/packages/graphql-clients-for-wp/src", + "GraphQLByPoP\\\\GraphQLEndpointForWP\\\\": "layers/GraphQLByPoP/packages/graphql-endpoint-for-wp/src", + "GraphQLByPoP\\\\GraphQLParser\\\\": "layers/GraphQLByPoP/packages/graphql-parser/src", + "GraphQLByPoP\\\\GraphQLQuery\\\\": "layers/GraphQLByPoP/packages/graphql-query/src", + "GraphQLByPoP\\\\GraphQLRequest\\\\": "layers/GraphQLByPoP/packages/graphql-request/src", + "GraphQLByPoP\\\\GraphQLServer\\\\": "layers/GraphQLByPoP/packages/graphql-server/src", + "Leoloso\\\\ExamplesForPoP\\\\": "layers/Misc/packages/examples-for-pop/src", + "PoPSchema\\\\BasicDirectives\\\\": "layers/Schema/packages/basic-directives/src", + "PoPSchema\\\\BlockMetadataWP\\\\": "layers/Schema/packages/block-metadata-for-wp/src", + "PoPSchema\\\\CDNDirective\\\\": "layers/Schema/packages/cdn-directive/src", + "PoPSchema\\\\CategoriesWP\\\\": "layers/Schema/packages/categories-wp/src", + "PoPSchema\\\\Categories\\\\": "layers/Schema/packages/categories/src", + "PoPSchema\\\\CommentMetaWP\\\\": "layers/Schema/packages/commentmeta-wp/src", + "PoPSchema\\\\CommentMeta\\\\": "layers/Schema/packages/commentmeta/src", + "PoPSchema\\\\CommentMutationsWP\\\\": "layers/Schema/packages/comment-mutations-wp/src", + "PoPSchema\\\\CommentMutations\\\\": "layers/Schema/packages/comment-mutations/src", + "PoPSchema\\\\CommentsWP\\\\": "layers/Schema/packages/comments-wp/src", + "PoPSchema\\\\Comments\\\\": "layers/Schema/packages/comments/src", + "PoPSchema\\\\ConvertCaseDirectives\\\\": "layers/Schema/packages/convert-case-directives/src", + "PoPSchema\\\\CustomPostMediaMutationsWP\\\\": "layers/Schema/packages/custompostmedia-mutations-wp/src", + "PoPSchema\\\\CustomPostMediaMutations\\\\": "layers/Schema/packages/custompostmedia-mutations/src", + "PoPSchema\\\\CustomPostMediaWP\\\\": "layers/Schema/packages/custompostmedia-wp/src", + "PoPSchema\\\\CustomPostMedia\\\\": "layers/Schema/packages/custompostmedia/src", + "PoPSchema\\\\CustomPostMetaWP\\\\": "layers/Schema/packages/custompostmeta-wp/src", + "PoPSchema\\\\CustomPostMeta\\\\": "layers/Schema/packages/custompostmeta/src", + "PoPSchema\\\\CustomPostMutationsWP\\\\": "layers/Schema/packages/custompost-mutations-wp/src", + "PoPSchema\\\\CustomPostMutations\\\\": "layers/Schema/packages/custompost-mutations/src", + "PoPSchema\\\\CustomPostsWP\\\\": "layers/Schema/packages/customposts-wp/src", + "PoPSchema\\\\CustomPosts\\\\": "layers/Schema/packages/customposts/src", + "PoPSchema\\\\EventMutationsWPEM\\\\": "layers/Schema/packages/event-mutations-wp-em/src", + "PoPSchema\\\\EventMutations\\\\": "layers/Schema/packages/event-mutations/src", + "PoPSchema\\\\EventsWPEM\\\\": "layers/Schema/packages/events-wp-em/src", + "PoPSchema\\\\Events\\\\": "layers/Schema/packages/events/src", + "PoPSchema\\\\EverythingElseWP\\\\": "layers/Schema/packages/everythingelse-wp/src", + "PoPSchema\\\\EverythingElse\\\\": "layers/Schema/packages/everythingelse/src", + "PoPSchema\\\\GenericCustomPosts\\\\": "layers/Schema/packages/generic-customposts/src", + "PoPSchema\\\\GoogleTranslateDirectiveForCustomPosts\\\\": "layers/Schema/packages/google-translate-directive-for-customposts/src", + "PoPSchema\\\\GoogleTranslateDirective\\\\": "layers/Schema/packages/google-translate-directive/src", + "PoPSchema\\\\HighlightsWP\\\\": "layers/Schema/packages/highlights-wp/src", + "PoPSchema\\\\Highlights\\\\": "layers/Schema/packages/highlights/src", + "PoPSchema\\\\LocationPostsWP\\\\": "layers/Schema/packages/locationposts-wp/src", + "PoPSchema\\\\LocationPosts\\\\": "layers/Schema/packages/locationposts/src", + "PoPSchema\\\\LocationsWPEM\\\\": "layers/Schema/packages/locations-wp-em/src", + "PoPSchema\\\\Locations\\\\": "layers/Schema/packages/locations/src", + "PoPSchema\\\\MediaWP\\\\": "layers/Schema/packages/media-wp/src", + "PoPSchema\\\\Media\\\\": "layers/Schema/packages/media/src", + "PoPSchema\\\\MenusWP\\\\": "layers/Schema/packages/menus-wp/src", + "PoPSchema\\\\Menus\\\\": "layers/Schema/packages/menus/src", + "PoPSchema\\\\MetaQueryWP\\\\": "layers/Schema/packages/metaquery-wp/src", + "PoPSchema\\\\MetaQuery\\\\": "layers/Schema/packages/metaquery/src", + "PoPSchema\\\\Meta\\\\": "layers/Schema/packages/meta/src", + "PoPSchema\\\\NotificationsWP\\\\": "layers/Schema/packages/notifications-wp/src", + "PoPSchema\\\\Notifications\\\\": "layers/Schema/packages/notifications/src", + "PoPSchema\\\\PagesWP\\\\": "layers/Schema/packages/pages-wp/src", + "PoPSchema\\\\Pages\\\\": "layers/Schema/packages/pages/src", + "PoPSchema\\\\PostMutations\\\\": "layers/Schema/packages/post-mutations/src", + "PoPSchema\\\\PostTagsWP\\\\": "layers/Schema/packages/post-tags-wp/src", + "PoPSchema\\\\PostTags\\\\": "layers/Schema/packages/post-tags/src", + "PoPSchema\\\\PostsWP\\\\": "layers/Schema/packages/posts-wp/src", + "PoPSchema\\\\Posts\\\\": "layers/Schema/packages/posts/src", + "PoPSchema\\\\QueriedObjectWP\\\\": "layers/Schema/packages/queriedobject-wp/src", + "PoPSchema\\\\QueriedObject\\\\": "layers/Schema/packages/queriedobject/src", + "PoPSchema\\\\SchemaCommons\\\\": "layers/Schema/packages/schema-commons/src", + "PoPSchema\\\\StancesWP\\\\": "layers/Schema/packages/stances-wp/src", + "PoPSchema\\\\Stances\\\\": "layers/Schema/packages/stances/src", + "PoPSchema\\\\TagsWP\\\\": "layers/Schema/packages/tags-wp/src", + "PoPSchema\\\\Tags\\\\": "layers/Schema/packages/tags/src", + "PoPSchema\\\\TaxonomiesWP\\\\": "layers/Schema/packages/taxonomies-wp/src", + "PoPSchema\\\\Taxonomies\\\\": "layers/Schema/packages/taxonomies/src", + "PoPSchema\\\\TaxonomyMetaWP\\\\": "layers/Schema/packages/taxonomymeta-wp/src", + "PoPSchema\\\\TaxonomyMeta\\\\": "layers/Schema/packages/taxonomymeta/src", + "PoPSchema\\\\TaxonomyQueryWP\\\\": "layers/Schema/packages/taxonomyquery-wp/src", + "PoPSchema\\\\TaxonomyQuery\\\\": "layers/Schema/packages/taxonomyquery/src", + "PoPSchema\\\\TranslateDirectiveACL\\\\": "layers/Schema/packages/translate-directive-acl/src", + "PoPSchema\\\\TranslateDirective\\\\": "layers/Schema/packages/translate-directive/src", + "PoPSchema\\\\UserMetaWP\\\\": "layers/Schema/packages/usermeta-wp/src", + "PoPSchema\\\\UserMeta\\\\": "layers/Schema/packages/usermeta/src", + "PoPSchema\\\\UserRolesACL\\\\": "layers/Schema/packages/user-roles-acl/src", + "PoPSchema\\\\UserRolesAccessControl\\\\": "layers/Schema/packages/user-roles-access-control/src", + "PoPSchema\\\\UserRolesWP\\\\": "layers/Schema/packages/user-roles-wp/src", + "PoPSchema\\\\UserRoles\\\\": "layers/Schema/packages/user-roles/src", + "PoPSchema\\\\UserStateAccessControl\\\\": "layers/Schema/packages/user-state-access-control/src", + "PoPSchema\\\\UserStateMutationsWP\\\\": "layers/Schema/packages/user-state-mutations-wp/src", + "PoPSchema\\\\UserStateMutations\\\\": "layers/Schema/packages/user-state-mutations/src", + "PoPSchema\\\\UserStateWP\\\\": "layers/Schema/packages/user-state-wp/src", + "PoPSchema\\\\UserState\\\\": "layers/Schema/packages/user-state/src", + "PoPSchema\\\\UsersWP\\\\": "layers/Schema/packages/users-wp/src", + "PoPSchema\\\\Users\\\\": "layers/Schema/packages/users/src", + "PoPSitesWassup\\\\CommentMutations\\\\": "layers/Wassup/packages/comment-mutations/src", + "PoPSitesWassup\\\\ContactUsMutations\\\\": "layers/Wassup/packages/contactus-mutations/src", + "PoPSitesWassup\\\\ContactUserMutations\\\\": "layers/Wassup/packages/contactuser-mutations/src", + "PoPSitesWassup\\\\CustomPostLinkMutations\\\\": "layers/Wassup/packages/custompostlink-mutations/src", + "PoPSitesWassup\\\\CustomPostMutations\\\\": "layers/Wassup/packages/custompost-mutations/src", + "PoPSitesWassup\\\\EventLinkMutations\\\\": "layers/Wassup/packages/eventlink-mutations/src", + "PoPSitesWassup\\\\EventMutations\\\\": "layers/Wassup/packages/event-mutations/src", + "PoPSitesWassup\\\\EverythingElseMutations\\\\": "layers/Wassup/packages/everythingelse-mutations/src", + "PoPSitesWassup\\\\FlagMutations\\\\": "layers/Wassup/packages/flag-mutations/src", + "PoPSitesWassup\\\\FormMutations\\\\": "layers/Wassup/packages/form-mutations/src", + "PoPSitesWassup\\\\GravityFormsMutations\\\\": "layers/Wassup/packages/gravityforms-mutations/src", + "PoPSitesWassup\\\\HighlightMutations\\\\": "layers/Wassup/packages/highlight-mutations/src", + "PoPSitesWassup\\\\LocationMutations\\\\": "layers/Wassup/packages/location-mutations/src", + "PoPSitesWassup\\\\LocationPostLinkMutations\\\\": "layers/Wassup/packages/locationpostlink-mutations/src", + "PoPSitesWassup\\\\LocationPostMutations\\\\": "layers/Wassup/packages/locationpost-mutations/src", + "PoPSitesWassup\\\\NewsletterMutations\\\\": "layers/Wassup/packages/newsletter-mutations/src", + "PoPSitesWassup\\\\NotificationMutations\\\\": "layers/Wassup/packages/notification-mutations/src", + "PoPSitesWassup\\\\PostLinkMutations\\\\": "layers/Wassup/packages/postlink-mutations/src", + "PoPSitesWassup\\\\PostMutations\\\\": "layers/Wassup/packages/post-mutations/src", + "PoPSitesWassup\\\\ShareMutations\\\\": "layers/Wassup/packages/share-mutations/src", + "PoPSitesWassup\\\\SocialNetworkMutations\\\\": "layers/Wassup/packages/socialnetwork-mutations/src", + "PoPSitesWassup\\\\StanceMutations\\\\": "layers/Wassup/packages/stance-mutations/src", + "PoPSitesWassup\\\\SystemMutations\\\\": "layers/Wassup/packages/system-mutations/src", + "PoPSitesWassup\\\\UserStateMutations\\\\": "layers/Wassup/packages/user-state-mutations/src", + "PoPSitesWassup\\\\VolunteerMutations\\\\": "layers/Wassup/packages/volunteer-mutations/src", + "PoPSitesWassup\\\\Wassup\\\\": "layers/Wassup/packages/wassup/src", + "PoP\\\\APIClients\\\\": "layers/API/packages/api-clients/src", + "PoP\\\\APIEndpointsForWP\\\\": "layers/API/packages/api-endpoints-for-wp/src", + "PoP\\\\APIEndpoints\\\\": "layers/API/packages/api-endpoints/src", + "PoP\\\\APIMirrorQuery\\\\": "layers/API/packages/api-mirrorquery/src", + "PoP\\\\API\\\\": "layers/API/packages/api/src", + "PoP\\\\AccessControl\\\\": "layers/Engine/packages/access-control/src", + "PoP\\\\ApplicationWP\\\\": "layers/SiteBuilder/packages/application-wp/src", + "PoP\\\\Application\\\\": "layers/SiteBuilder/packages/application/src", + "PoP\\\\Base36Definitions\\\\": "layers/SiteBuilder/packages/definitions-base36/src", + "PoP\\\\CacheControl\\\\": "layers/Engine/packages/cache-control/src", + "PoP\\\\ComponentModel\\\\": "layers/Engine/packages/component-model/src", + "PoP\\\\ConfigurableSchemaFeedback\\\\": "layers/Engine/packages/configurable-schema-feedback/src", + "PoP\\\\ConfigurationComponentModel\\\\": "layers/SiteBuilder/packages/component-model-configuration/src", + "PoP\\\\DefinitionPersistence\\\\": "layers/SiteBuilder/packages/definitionpersistence/src", + "PoP\\\\Definitions\\\\": "layers/Engine/packages/definitions/src", + "PoP\\\\EmojiDefinitions\\\\": "layers/SiteBuilder/packages/definitions-emoji/src", + "PoP\\\\EngineWP\\\\": "layers/Engine/packages/engine-wp/src", + "PoP\\\\Engine\\\\": "layers/Engine/packages/engine/src", + "PoP\\\\FieldQuery\\\\": "layers/Engine/packages/field-query/src", + "PoP\\\\FileStore\\\\": "layers/Engine/packages/filestore/src", + "PoP\\\\FunctionFields\\\\": "layers/Engine/packages/function-fields/src", + "PoP\\\\GraphQLAPI\\\\": "layers/API/packages/api-graphql/src", + "PoP\\\\GuzzleHelpers\\\\": "layers/Engine/packages/guzzle-helpers/src", + "PoP\\\\HooksWP\\\\": "layers/Engine/packages/hooks-wp/src", + "PoP\\\\Hooks\\\\": "layers/Engine/packages/hooks/src", + "PoP\\\\LooseContracts\\\\": "layers/Engine/packages/loosecontracts/src", + "PoP\\\\MandatoryDirectivesByConfiguration\\\\": "layers/Engine/packages/mandatory-directives-by-configuration/src", + "PoP\\\\ModuleRouting\\\\": "layers/Engine/packages/modulerouting/src", + "PoP\\\\Multisite\\\\": "layers/SiteBuilder/packages/multisite/src", + "PoP\\\\PoP\\\\": "src", + "PoP\\\\QueryParsing\\\\": "layers/Engine/packages/query-parsing/src", + "PoP\\\\RESTAPI\\\\": "layers/API/packages/api-rest/src", + "PoP\\\\ResourceLoader\\\\": "layers/SiteBuilder/packages/resourceloader/src", + "PoP\\\\Resources\\\\": "layers/SiteBuilder/packages/resources/src", + "PoP\\\\Root\\\\": "layers/Engine/packages/root/src", + "PoP\\\\RoutingWP\\\\": "layers/Engine/packages/routing-wp/src", + "PoP\\\\Routing\\\\": "layers/Engine/packages/routing/src", + "PoP\\\\SPA\\\\": "layers/SiteBuilder/packages/spa/src", + "PoP\\\\SSG\\\\": "layers/SiteBuilder/packages/static-site-generator/src", + "PoP\\\\SiteWP\\\\": "layers/SiteBuilder/packages/site-wp/src", + "PoP\\\\Site\\\\": "layers/SiteBuilder/packages/site/src", + "PoP\\\\TraceTools\\\\": "layers/Engine/packages/trace-tools/src", + "PoP\\\\TranslationWP\\\\": "layers/Engine/packages/translation-wp/src", + "PoP\\\\Translation\\\\": "layers/Engine/packages/translation/src" + } + }, + "autoload-dev": { + "psr-4": { + "GraphQLAPI\\\\ConvertCaseDirectives\\\\": "layers/GraphQLAPIForWP/plugins/convert-case-directives/tests", + "GraphQLAPI\\\\GraphQLAPI\\\\": "layers/GraphQLAPIForWP/plugins/graphql-api-for-wp/tests", + "GraphQLAPI\\\\SchemaFeedback\\\\": "layers/GraphQLAPIForWP/plugins/schema-feedback/tests", + "GraphQLByPoP\\\\GraphQLClientsForWP\\\\": "layers/GraphQLByPoP/packages/graphql-clients-for-wp/tests", + "GraphQLByPoP\\\\GraphQLEndpointForWP\\\\": "layers/GraphQLByPoP/packages/graphql-endpoint-for-wp/tests", + "GraphQLByPoP\\\\GraphQLParser\\\\": "layers/GraphQLByPoP/packages/graphql-parser/tests", + "GraphQLByPoP\\\\GraphQLQuery\\\\": "layers/GraphQLByPoP/packages/graphql-query/tests", + "GraphQLByPoP\\\\GraphQLRequest\\\\": "layers/GraphQLByPoP/packages/graphql-request/tests", + "GraphQLByPoP\\\\GraphQLServer\\\\": "layers/GraphQLByPoP/packages/graphql-server/tests", + "Leoloso\\\\ExamplesForPoP\\\\": "layers/Misc/packages/examples-for-pop/tests", + "PoPSchema\\\\BasicDirectives\\\\": "layers/Schema/packages/basic-directives/tests", + "PoPSchema\\\\BlockMetadataWP\\\\": "layers/Schema/packages/block-metadata-for-wp/tests", + "PoPSchema\\\\CDNDirective\\\\": "layers/Schema/packages/cdn-directive/tests", + "PoPSchema\\\\CategoriesWP\\\\": "layers/Schema/packages/categories-wp/tests", + "PoPSchema\\\\Categories\\\\": "layers/Schema/packages/categories/tests", + "PoPSchema\\\\CommentMetaWP\\\\": "layers/Schema/packages/commentmeta-wp/tests", + "PoPSchema\\\\CommentMeta\\\\": "layers/Schema/packages/commentmeta/tests", + "PoPSchema\\\\CommentMutationsWP\\\\": "layers/Schema/packages/comment-mutations-wp/tests", + "PoPSchema\\\\CommentMutations\\\\": "layers/Schema/packages/comment-mutations/tests", + "PoPSchema\\\\CommentsWP\\\\": "layers/Schema/packages/comments-wp/tests", + "PoPSchema\\\\Comments\\\\": "layers/Schema/packages/comments/tests", + "PoPSchema\\\\ConvertCaseDirectives\\\\": "layers/Schema/packages/convert-case-directives/tests", + "PoPSchema\\\\CustomPostMediaMutationsWP\\\\": "layers/Schema/packages/custompostmedia-mutations-wp/tests", + "PoPSchema\\\\CustomPostMediaMutations\\\\": "layers/Schema/packages/custompostmedia-mutations/tests", + "PoPSchema\\\\CustomPostMediaWP\\\\": "layers/Schema/packages/custompostmedia-wp/tests", + "PoPSchema\\\\CustomPostMedia\\\\": "layers/Schema/packages/custompostmedia/tests", + "PoPSchema\\\\CustomPostMetaWP\\\\": "layers/Schema/packages/custompostmeta-wp/tests", + "PoPSchema\\\\CustomPostMeta\\\\": "layers/Schema/packages/custompostmeta/tests", + "PoPSchema\\\\CustomPostMutationsWP\\\\": "layers/Schema/packages/custompost-mutations-wp/tests", + "PoPSchema\\\\CustomPostMutations\\\\": "layers/Schema/packages/custompost-mutations/tests", + "PoPSchema\\\\CustomPostsWP\\\\": "layers/Schema/packages/customposts-wp/tests", + "PoPSchema\\\\CustomPosts\\\\": "layers/Schema/packages/customposts/tests", + "PoPSchema\\\\EventMutationsWPEM\\\\": "layers/Schema/packages/event-mutations-wp-em/tests", + "PoPSchema\\\\EventMutations\\\\": "layers/Schema/packages/event-mutations/tests", + "PoPSchema\\\\EventsWPEM\\\\": "layers/Schema/packages/events-wp-em/tests", + "PoPSchema\\\\Events\\\\": "layers/Schema/packages/events/tests", + "PoPSchema\\\\EverythingElseWP\\\\": "layers/Schema/packages/everythingelse-wp/tests", + "PoPSchema\\\\EverythingElse\\\\": "layers/Schema/packages/everythingelse/tests", + "PoPSchema\\\\GenericCustomPosts\\\\": "layers/Schema/packages/generic-customposts/tests", + "PoPSchema\\\\GoogleTranslateDirectiveForCustomPosts\\\\": "layers/Schema/packages/google-translate-directive-for-customposts/tests", + "PoPSchema\\\\GoogleTranslateDirective\\\\": "layers/Schema/packages/google-translate-directive/tests", + "PoPSchema\\\\HighlightsWP\\\\": "layers/Schema/packages/highlights-wp/tests", + "PoPSchema\\\\Highlights\\\\": "layers/Schema/packages/highlights/tests", + "PoPSchema\\\\LocationPostsWP\\\\": "layers/Schema/packages/locationposts-wp/tests", + "PoPSchema\\\\LocationPosts\\\\": "layers/Schema/packages/locationposts/tests", + "PoPSchema\\\\LocationsWPEM\\\\": "layers/Schema/packages/locations-wp-em/tests", + "PoPSchema\\\\Locations\\\\": "layers/Schema/packages/locations/tests", + "PoPSchema\\\\MediaWP\\\\": "layers/Schema/packages/media-wp/tests", + "PoPSchema\\\\Media\\\\": "layers/Schema/packages/media/tests", + "PoPSchema\\\\MenusWP\\\\": "layers/Schema/packages/menus-wp/tests", + "PoPSchema\\\\Menus\\\\": "layers/Schema/packages/menus/tests", + "PoPSchema\\\\MetaQueryWP\\\\": "layers/Schema/packages/metaquery-wp/tests", + "PoPSchema\\\\MetaQuery\\\\": "layers/Schema/packages/metaquery/tests", + "PoPSchema\\\\Meta\\\\": "layers/Schema/packages/meta/tests", + "PoPSchema\\\\NotificationsWP\\\\": "layers/Schema/packages/notifications-wp/tests", + "PoPSchema\\\\Notifications\\\\": "layers/Schema/packages/notifications/tests", + "PoPSchema\\\\PagesWP\\\\": "layers/Schema/packages/pages-wp/tests", + "PoPSchema\\\\Pages\\\\": "layers/Schema/packages/pages/tests", + "PoPSchema\\\\PostMutations\\\\": "layers/Schema/packages/post-mutations/tests", + "PoPSchema\\\\PostTagsWP\\\\": "layers/Schema/packages/post-tags-wp/tests", + "PoPSchema\\\\PostTags\\\\": "layers/Schema/packages/post-tags/tests", + "PoPSchema\\\\PostsWP\\\\": "layers/Schema/packages/posts-wp/tests", + "PoPSchema\\\\Posts\\\\": "layers/Schema/packages/posts/tests", + "PoPSchema\\\\QueriedObjectWP\\\\": "layers/Schema/packages/queriedobject-wp/tests", + "PoPSchema\\\\QueriedObject\\\\": "layers/Schema/packages/queriedobject/tests", + "PoPSchema\\\\SchemaCommons\\\\": "layers/Schema/packages/schema-commons/tests", + "PoPSchema\\\\StancesWP\\\\": "layers/Schema/packages/stances-wp/tests", + "PoPSchema\\\\Stances\\\\": "layers/Schema/packages/stances/tests", + "PoPSchema\\\\TagsWP\\\\": "layers/Schema/packages/tags-wp/tests", + "PoPSchema\\\\Tags\\\\": "layers/Schema/packages/tags/tests", + "PoPSchema\\\\TaxonomiesWP\\\\": "layers/Schema/packages/taxonomies-wp/tests", + "PoPSchema\\\\Taxonomies\\\\": "layers/Schema/packages/taxonomies/tests", + "PoPSchema\\\\TaxonomyMetaWP\\\\": "layers/Schema/packages/taxonomymeta-wp/tests", + "PoPSchema\\\\TaxonomyMeta\\\\": "layers/Schema/packages/taxonomymeta/tests", + "PoPSchema\\\\TaxonomyQueryWP\\\\": "layers/Schema/packages/taxonomyquery-wp/tests", + "PoPSchema\\\\TaxonomyQuery\\\\": "layers/Schema/packages/taxonomyquery/tests", + "PoPSchema\\\\TranslateDirectiveACL\\\\": "layers/Schema/packages/translate-directive-acl/tests", + "PoPSchema\\\\TranslateDirective\\\\": "layers/Schema/packages/translate-directive/tests", + "PoPSchema\\\\UserMetaWP\\\\": "layers/Schema/packages/usermeta-wp/tests", + "PoPSchema\\\\UserMeta\\\\": "layers/Schema/packages/usermeta/tests", + "PoPSchema\\\\UserRolesACL\\\\": "layers/Schema/packages/user-roles-acl/tests", + "PoPSchema\\\\UserRolesAccessControl\\\\": "layers/Schema/packages/user-roles-access-control/tests", + "PoPSchema\\\\UserRolesWP\\\\": "layers/Schema/packages/user-roles-wp/tests", + "PoPSchema\\\\UserRoles\\\\": "layers/Schema/packages/user-roles/tests", + "PoPSchema\\\\UserStateAccessControl\\\\": "layers/Schema/packages/user-state-access-control/tests", + "PoPSchema\\\\UserStateMutationsWP\\\\": "layers/Schema/packages/user-state-mutations-wp/tests", + "PoPSchema\\\\UserStateMutations\\\\": "layers/Schema/packages/user-state-mutations/tests", + "PoPSchema\\\\UserStateWP\\\\": "layers/Schema/packages/user-state-wp/tests", + "PoPSchema\\\\UserState\\\\": "layers/Schema/packages/user-state/tests", + "PoPSchema\\\\UsersWP\\\\": "layers/Schema/packages/users-wp/tests", + "PoPSchema\\\\Users\\\\": "layers/Schema/packages/users/tests", + "PoPSitesWassup\\\\CommentMutations\\\\": "layers/Wassup/packages/comment-mutations/tests", + "PoPSitesWassup\\\\ContactUsMutations\\\\": "layers/Wassup/packages/contactus-mutations/tests", + "PoPSitesWassup\\\\ContactUserMutations\\\\": "layers/Wassup/packages/contactuser-mutations/tests", + "PoPSitesWassup\\\\CustomPostLinkMutations\\\\": "layers/Wassup/packages/custompostlink-mutations/tests", + "PoPSitesWassup\\\\CustomPostMutations\\\\": "layers/Wassup/packages/custompost-mutations/tests", + "PoPSitesWassup\\\\EventLinkMutations\\\\": "layers/Wassup/packages/eventlink-mutations/tests", + "PoPSitesWassup\\\\EventMutations\\\\": "layers/Wassup/packages/event-mutations/tests", + "PoPSitesWassup\\\\EverythingElseMutations\\\\": "layers/Wassup/packages/everythingelse-mutations/tests", + "PoPSitesWassup\\\\FlagMutations\\\\": "layers/Wassup/packages/flag-mutations/tests", + "PoPSitesWassup\\\\FormMutations\\\\": "layers/Wassup/packages/form-mutations/tests", + "PoPSitesWassup\\\\GravityFormsMutations\\\\": "layers/Wassup/packages/gravityforms-mutations/tests", + "PoPSitesWassup\\\\HighlightMutations\\\\": "layers/Wassup/packages/highlight-mutations/tests", + "PoPSitesWassup\\\\LocationMutations\\\\": "layers/Wassup/packages/location-mutations/tests", + "PoPSitesWassup\\\\LocationPostLinkMutations\\\\": "layers/Wassup/packages/locationpostlink-mutations/tests", + "PoPSitesWassup\\\\LocationPostMutations\\\\": "layers/Wassup/packages/locationpost-mutations/tests", + "PoPSitesWassup\\\\NewsletterMutations\\\\": "layers/Wassup/packages/newsletter-mutations/tests", + "PoPSitesWassup\\\\NotificationMutations\\\\": "layers/Wassup/packages/notification-mutations/tests", + "PoPSitesWassup\\\\PostLinkMutations\\\\": "layers/Wassup/packages/postlink-mutations/tests", + "PoPSitesWassup\\\\PostMutations\\\\": "layers/Wassup/packages/post-mutations/tests", + "PoPSitesWassup\\\\ShareMutations\\\\": "layers/Wassup/packages/share-mutations/tests", + "PoPSitesWassup\\\\SocialNetworkMutations\\\\": "layers/Wassup/packages/socialnetwork-mutations/tests", + "PoPSitesWassup\\\\StanceMutations\\\\": "layers/Wassup/packages/stance-mutations/tests", + "PoPSitesWassup\\\\SystemMutations\\\\": "layers/Wassup/packages/system-mutations/tests", + "PoPSitesWassup\\\\UserStateMutations\\\\": "layers/Wassup/packages/user-state-mutations/tests", + "PoPSitesWassup\\\\VolunteerMutations\\\\": "layers/Wassup/packages/volunteer-mutations/tests", + "PoPSitesWassup\\\\Wassup\\\\": "layers/Wassup/packages/wassup/tests", + "PoP\\\\APIClients\\\\": "layers/API/packages/api-clients/tests", + "PoP\\\\APIEndpointsForWP\\\\": "layers/API/packages/api-endpoints-for-wp/tests", + "PoP\\\\APIEndpoints\\\\": "layers/API/packages/api-endpoints/tests", + "PoP\\\\APIMirrorQuery\\\\": "layers/API/packages/api-mirrorquery/tests", + "PoP\\\\API\\\\": "layers/API/packages/api/tests", + "PoP\\\\AccessControl\\\\": "layers/Engine/packages/access-control/tests", + "PoP\\\\ApplicationWP\\\\": "layers/SiteBuilder/packages/application-wp/tests", + "PoP\\\\Application\\\\": "layers/SiteBuilder/packages/application/tests", + "PoP\\\\Base36Definitions\\\\": "layers/SiteBuilder/packages/definitions-base36/tests", + "PoP\\\\CacheControl\\\\": "layers/Engine/packages/cache-control/tests", + "PoP\\\\ComponentModel\\\\": "layers/Engine/packages/component-model/tests", + "PoP\\\\ConfigurableSchemaFeedback\\\\": "layers/Engine/packages/configurable-schema-feedback/tests", + "PoP\\\\ConfigurationComponentModel\\\\": "layers/SiteBuilder/packages/component-model-configuration/tests", + "PoP\\\\DefinitionPersistence\\\\": "layers/SiteBuilder/packages/definitionpersistence/tests", + "PoP\\\\Definitions\\\\": "layers/Engine/packages/definitions/tests", + "PoP\\\\EmojiDefinitions\\\\": "layers/SiteBuilder/packages/definitions-emoji/tests", + "PoP\\\\EngineWP\\\\": "layers/Engine/packages/engine-wp/tests", + "PoP\\\\Engine\\\\": "layers/Engine/packages/engine/tests", + "PoP\\\\FieldQuery\\\\": "layers/Engine/packages/field-query/tests", + "PoP\\\\FileStore\\\\": "layers/Engine/packages/filestore/tests", + "PoP\\\\FunctionFields\\\\": "layers/Engine/packages/function-fields/tests", + "PoP\\\\GraphQLAPI\\\\": "layers/API/packages/api-graphql/tests", + "PoP\\\\GuzzleHelpers\\\\": "layers/Engine/packages/guzzle-helpers/tests", + "PoP\\\\HooksWP\\\\": "layers/Engine/packages/hooks-wp/tests", + "PoP\\\\Hooks\\\\": "layers/Engine/packages/hooks/tests", + "PoP\\\\LooseContracts\\\\": "layers/Engine/packages/loosecontracts/tests", + "PoP\\\\MandatoryDirectivesByConfiguration\\\\": "layers/Engine/packages/mandatory-directives-by-configuration/tests", + "PoP\\\\ModuleRouting\\\\": "layers/Engine/packages/modulerouting/tests", + "PoP\\\\Multisite\\\\": "layers/SiteBuilder/packages/multisite/tests", + "PoP\\\\QueryParsing\\\\": "layers/Engine/packages/query-parsing/tests", + "PoP\\\\RESTAPI\\\\": "layers/API/packages/api-rest/tests", + "PoP\\\\ResourceLoader\\\\": "layers/SiteBuilder/packages/resourceloader/tests", + "PoP\\\\Resources\\\\": "layers/SiteBuilder/packages/resources/tests", + "PoP\\\\Root\\\\": "layers/Engine/packages/root/tests", + "PoP\\\\RoutingWP\\\\": "layers/Engine/packages/routing-wp/tests", + "PoP\\\\Routing\\\\": "layers/Engine/packages/routing/tests", + "PoP\\\\SPA\\\\": "layers/SiteBuilder/packages/spa/tests", + "PoP\\\\SSG\\\\": "layers/SiteBuilder/packages/static-site-generator/tests", + "PoP\\\\SiteWP\\\\": "layers/SiteBuilder/packages/site-wp/tests", + "PoP\\\\Site\\\\": "layers/SiteBuilder/packages/site/tests", + "PoP\\\\TraceTools\\\\": "layers/Engine/packages/trace-tools/tests", + "PoP\\\\TranslationWP\\\\": "layers/Engine/packages/translation-wp/tests", + "PoP\\\\Translation\\\\": "layers/Engine/packages/translation/tests" + } + }, + "extra": { + "wordpress-install-dir": "vendor/wordpress/wordpress", + "merge-plugin": { + "include": [ + "composer.local.json" + ], + "recurse": true, + "replace": false, + "ignore-duplicates": false, + "merge-dev": true, + "merge-extra": false, + "merge-extra-deep": false, + "merge-scripts": false + } + }, + "replace": { + "getpop/access-control": "self.version", + "getpop/api": "self.version", + "getpop/api-clients": "self.version", + "getpop/api-endpoints": "self.version", + "getpop/api-endpoints-for-wp": "self.version", + "getpop/api-graphql": "self.version", + "getpop/api-mirrorquery": "self.version", + "getpop/api-rest": "self.version", + "getpop/application": "self.version", + "getpop/application-wp": "self.version", + "getpop/cache-control": "self.version", + "getpop/component-model": "self.version", + "getpop/component-model-configuration": "self.version", + "getpop/configurable-schema-feedback": "self.version", + "getpop/definitionpersistence": "self.version", + "getpop/definitions": "self.version", + "getpop/definitions-base36": "self.version", + "getpop/definitions-emoji": "self.version", + "getpop/engine": "self.version", + "getpop/engine-wp": "self.version", + "getpop/engine-wp-bootloader": "self.version", + "getpop/field-query": "self.version", + "getpop/filestore": "self.version", + "getpop/function-fields": "self.version", + "getpop/guzzle-helpers": "self.version", + "getpop/hooks": "self.version", + "getpop/hooks-wp": "self.version", + "getpop/loosecontracts": "self.version", + "getpop/mandatory-directives-by-configuration": "self.version", + "getpop/migrate-api": "self.version", + "getpop/migrate-api-graphql": "self.version", + "getpop/migrate-component-model": "self.version", + "getpop/migrate-component-model-configuration": "self.version", + "getpop/migrate-engine": "self.version", + "getpop/migrate-engine-wp": "self.version", + "getpop/migrate-static-site-generator": "self.version", + "getpop/modulerouting": "self.version", + "getpop/multisite": "self.version", + "getpop/query-parsing": "self.version", + "getpop/resourceloader": "self.version", + "getpop/resources": "self.version", + "getpop/root": "self.version", + "getpop/routing": "self.version", + "getpop/routing-wp": "self.version", + "getpop/site": "self.version", + "getpop/site-wp": "self.version", + "getpop/spa": "self.version", + "getpop/static-site-generator": "self.version", + "getpop/trace-tools": "self.version", + "getpop/translation": "self.version", + "getpop/translation-wp": "self.version", + "graphql-api/convert-case-directives": "self.version", + "graphql-api/graphql-api-for-wp": "self.version", + "graphql-api/schema-feedback": "self.version", + "graphql-by-pop/graphql-clients-for-wp": "self.version", + "graphql-by-pop/graphql-endpoint-for-wp": "self.version", + "graphql-by-pop/graphql-parser": "self.version", + "graphql-by-pop/graphql-query": "self.version", + "graphql-by-pop/graphql-request": "self.version", + "graphql-by-pop/graphql-server": "self.version", + "leoloso/examples-for-pop": "self.version", + "pop-migrate-everythingelse/cssconverter": "self.version", + "pop-migrate-everythingelse/ssr": "self.version", + "pop-schema/basic-directives": "self.version", + "pop-schema/block-metadata-for-wp": "self.version", + "pop-schema/categories": "self.version", + "pop-schema/categories-wp": "self.version", + "pop-schema/cdn-directive": "self.version", + "pop-schema/comment-mutations": "self.version", + "pop-schema/comment-mutations-wp": "self.version", + "pop-schema/commentmeta": "self.version", + "pop-schema/commentmeta-wp": "self.version", + "pop-schema/comments": "self.version", + "pop-schema/comments-wp": "self.version", + "pop-schema/convert-case-directives": "self.version", + "pop-schema/custompost-mutations": "self.version", + "pop-schema/custompost-mutations-wp": "self.version", + "pop-schema/custompostmedia": "self.version", + "pop-schema/custompostmedia-mutations": "self.version", + "pop-schema/custompostmedia-mutations-wp": "self.version", + "pop-schema/custompostmedia-wp": "self.version", + "pop-schema/custompostmeta": "self.version", + "pop-schema/custompostmeta-wp": "self.version", + "pop-schema/customposts": "self.version", + "pop-schema/customposts-wp": "self.version", + "pop-schema/event-mutations": "self.version", + "pop-schema/event-mutations-wp-em": "self.version", + "pop-schema/events": "self.version", + "pop-schema/events-wp-em": "self.version", + "pop-schema/everythingelse": "self.version", + "pop-schema/everythingelse-wp": "self.version", + "pop-schema/generic-customposts": "self.version", + "pop-schema/google-translate-directive": "self.version", + "pop-schema/google-translate-directive-for-customposts": "self.version", + "pop-schema/highlights": "self.version", + "pop-schema/highlights-wp": "self.version", + "pop-schema/locationposts": "self.version", + "pop-schema/locationposts-wp": "self.version", + "pop-schema/locations": "self.version", + "pop-schema/locations-wp-em": "self.version", + "pop-schema/media": "self.version", + "pop-schema/media-wp": "self.version", + "pop-schema/menus": "self.version", + "pop-schema/menus-wp": "self.version", + "pop-schema/meta": "self.version", + "pop-schema/metaquery": "self.version", + "pop-schema/metaquery-wp": "self.version", + "pop-schema/migrate-categories": "self.version", + "pop-schema/migrate-categories-wp": "self.version", + "pop-schema/migrate-commentmeta": "self.version", + "pop-schema/migrate-commentmeta-wp": "self.version", + "pop-schema/migrate-comments": "self.version", + "pop-schema/migrate-comments-wp": "self.version", + "pop-schema/migrate-custompostmedia": "self.version", + "pop-schema/migrate-custompostmedia-wp": "self.version", + "pop-schema/migrate-custompostmeta": "self.version", + "pop-schema/migrate-custompostmeta-wp": "self.version", + "pop-schema/migrate-customposts": "self.version", + "pop-schema/migrate-customposts-wp": "self.version", + "pop-schema/migrate-events": "self.version", + "pop-schema/migrate-events-wp-em": "self.version", + "pop-schema/migrate-everythingelse": "self.version", + "pop-schema/migrate-locations": "self.version", + "pop-schema/migrate-locations-wp-em": "self.version", + "pop-schema/migrate-media": "self.version", + "pop-schema/migrate-media-wp": "self.version", + "pop-schema/migrate-meta": "self.version", + "pop-schema/migrate-metaquery": "self.version", + "pop-schema/migrate-metaquery-wp": "self.version", + "pop-schema/migrate-pages": "self.version", + "pop-schema/migrate-pages-wp": "self.version", + "pop-schema/migrate-post-tags": "self.version", + "pop-schema/migrate-post-tags-wp": "self.version", + "pop-schema/migrate-posts": "self.version", + "pop-schema/migrate-posts-wp": "self.version", + "pop-schema/migrate-queriedobject": "self.version", + "pop-schema/migrate-queriedobject-wp": "self.version", + "pop-schema/migrate-tags": "self.version", + "pop-schema/migrate-tags-wp": "self.version", + "pop-schema/migrate-taxonomies": "self.version", + "pop-schema/migrate-taxonomies-wp": "self.version", + "pop-schema/migrate-taxonomymeta": "self.version", + "pop-schema/migrate-taxonomymeta-wp": "self.version", + "pop-schema/migrate-taxonomyquery": "self.version", + "pop-schema/migrate-taxonomyquery-wp": "self.version", + "pop-schema/migrate-usermeta": "self.version", + "pop-schema/migrate-usermeta-wp": "self.version", + "pop-schema/migrate-users": "self.version", + "pop-schema/migrate-users-wp": "self.version", + "pop-schema/notifications": "self.version", + "pop-schema/notifications-wp": "self.version", + "pop-schema/pages": "self.version", + "pop-schema/pages-wp": "self.version", + "pop-schema/post-mutations": "self.version", + "pop-schema/post-tags": "self.version", + "pop-schema/post-tags-wp": "self.version", + "pop-schema/posts": "self.version", + "pop-schema/posts-wp": "self.version", + "pop-schema/queriedobject": "self.version", + "pop-schema/queriedobject-wp": "self.version", + "pop-schema/schema-commons": "self.version", + "pop-schema/stances": "self.version", + "pop-schema/stances-wp": "self.version", + "pop-schema/tags": "self.version", + "pop-schema/tags-wp": "self.version", + "pop-schema/taxonomies": "self.version", + "pop-schema/taxonomies-wp": "self.version", + "pop-schema/taxonomymeta": "self.version", + "pop-schema/taxonomymeta-wp": "self.version", + "pop-schema/taxonomyquery": "self.version", + "pop-schema/taxonomyquery-wp": "self.version", + "pop-schema/translate-directive": "self.version", + "pop-schema/translate-directive-acl": "self.version", + "pop-schema/user-roles": "self.version", + "pop-schema/user-roles-access-control": "self.version", + "pop-schema/user-roles-acl": "self.version", + "pop-schema/user-roles-wp": "self.version", + "pop-schema/user-state": "self.version", + "pop-schema/user-state-access-control": "self.version", + "pop-schema/user-state-mutations": "self.version", + "pop-schema/user-state-mutations-wp": "self.version", + "pop-schema/user-state-wp": "self.version", + "pop-schema/usermeta": "self.version", + "pop-schema/usermeta-wp": "self.version", + "pop-schema/users": "self.version", + "pop-schema/users-wp": "self.version", + "pop-sites-wassup/comment-mutations": "self.version", + "pop-sites-wassup/contactus-mutations": "self.version", + "pop-sites-wassup/contactuser-mutations": "self.version", + "pop-sites-wassup/custompost-mutations": "self.version", + "pop-sites-wassup/custompostlink-mutations": "self.version", + "pop-sites-wassup/event-mutations": "self.version", + "pop-sites-wassup/eventlink-mutations": "self.version", + "pop-sites-wassup/everythingelse-mutations": "self.version", + "pop-sites-wassup/flag-mutations": "self.version", + "pop-sites-wassup/form-mutations": "self.version", + "pop-sites-wassup/gravityforms-mutations": "self.version", + "pop-sites-wassup/highlight-mutations": "self.version", + "pop-sites-wassup/location-mutations": "self.version", + "pop-sites-wassup/locationpost-mutations": "self.version", + "pop-sites-wassup/locationpostlink-mutations": "self.version", + "pop-sites-wassup/newsletter-mutations": "self.version", + "pop-sites-wassup/notification-mutations": "self.version", + "pop-sites-wassup/post-mutations": "self.version", + "pop-sites-wassup/postlink-mutations": "self.version", + "pop-sites-wassup/share-mutations": "self.version", + "pop-sites-wassup/socialnetwork-mutations": "self.version", + "pop-sites-wassup/stance-mutations": "self.version", + "pop-sites-wassup/system-mutations": "self.version", + "pop-sites-wassup/user-state-mutations": "self.version", + "pop-sites-wassup/volunteer-mutations": "self.version", + "pop-sites-wassup/wassup": "self.version" + }, + "authors": [ + { + "name": "Leonardo Losoviz", + "email": "leo@getpop.org", + "homepage": "https://getpop.org" + } + ], + "description": "Monorepo for all the PoP packages", + "license": "GPL-2.0-or-later", + "config": { + "sort-packages": true, + "platform-check": false + }, + "repositories": [ + { + "type": "composer", + "url": "https://wpackagist.org" + }, + { + "type": "vcs", + "url": "https://github.com/leoloso/wp-muplugin-loader.git" + }, + { + "type": "vcs", + "url": "https://github.com/mcaskill/composer-merge-plugin.git" + } + ], + "scripts": { + "test": "phpunit", + "check-style": "phpcs -n src $(monorepo-builder source-packages --subfolder=src --subfolder=tests)", + "fix-style": "phpcbf -n src $(monorepo-builder source-packages --subfolder=src --subfolder=tests)", + "analyse": "ci/phpstan.sh \\". $(monorepo-builder source-packages --skip-unmigrated)\\"", + "preview-src-downgrade": "rector process $(monorepo-builder source-packages --subfolder=src) --config=rector-downgrade-code.php --ansi --dry-run || true", + "preview-vendor-downgrade": "layers/Engine/packages/root/ci/downgrade_code.sh 7.1 rector-downgrade-code.php --dry-run || true", + "preview-code-downgrade": [ + "@preview-src-downgrade", + "@preview-vendor-downgrade" + ], + "build-server": [ + "lando init --source remote --remote-url https://wordpress.org/latest.tar.gz --recipe wordpress --webroot wordpress --name graphql-api-dev", + "@start-server" + ], + "start-server": [ + "cd layers/GraphQLAPIForWP/plugins/graphql-api-for-wp && composer install", + "lando start" + ], + "rebuild-server": "lando rebuild -y", + "merge-monorepo": "monorepo-builder merge --ansi", + "propagate-monorepo": "monorepo-builder propagate --ansi", + "validate-monorepo": "monorepo-builder validate --ansi", + "release": "monorepo-builder release patch --ansi" + }, + "minimum-stability": "dev", + "prefer-stable": true +} ', $manipulator->getContents()); } } From 92b3725765195490e0af99f0acb7d1b29933403c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 11:53:41 +0100 Subject: [PATCH 15/74] Workaround issues when the additional fixed repo is in used and is an installed repo, closes #9574 --- src/Composer/Installer.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 51b7c40b6..d0cfa9d56 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -49,6 +49,7 @@ use Composer\Package\RootPackageInterface; use Composer\Repository\InstalledArrayRepository; use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\InstalledRepository; +use Composer\Repository\FilterRepository; use Composer\Repository\RootPackageRepository; use Composer\Repository\PlatformRepository; use Composer\Repository\RepositoryInterface; @@ -766,7 +767,14 @@ class Installer $repositorySet->addRepository(new RootPackageRepository($this->fixedRootPackage)); $repositorySet->addRepository($platformRepo); if ($this->additionalFixedRepository) { - $repositorySet->addRepository($this->additionalFixedRepository); + $additionalFixedRepository = $this->additionalFixedRepository; + // wrap the repository in a FilterRepository if needed to avoid warnings about installed repositories being used in the RepositorySet + // see https://github.com/composer/composer/pull/9574 + if ($additionalFixedRepository instanceof InstalledRepository || $additionalFixedRepository instanceof InstalledRepositoryInterface) { + $additionalFixedRepository = new FilterRepository($additionalFixedRepository, array()); + } + + $repositorySet->addRepository($additionalFixedRepository); } return $repositorySet; From 598beb240aeaf1cf780b064eab7c26bf75f704c6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 13:04:32 +0100 Subject: [PATCH 16/74] CS fixes --- .../Package/Archiver/BaseExcludeFilter.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseExcludeFilter.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php index a6e668bc1..29ed0ff1b 100644 --- a/src/Composer/Package/Archiver/BaseExcludeFilter.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -130,20 +130,15 @@ abstract class BaseExcludeFilter $rule = ltrim($rule, '!'); } - if ($rule !== '') { - if ($rule[0] === '/') { - $pattern = '^/'; - } else { - $first_slash_position = strpos($rule, '/'); - - if (false === $first_slash_position || strlen($rule) - 1 === $first_slash_position) { - $pattern = '/'; - } - } - - $rule = trim($rule, '/'); + $firstSlashPosition = strpos($rule, '/'); + if (0 === $firstSlashPosition) { + $pattern = '^/'; + } elseif (false === $firstSlashPosition || strlen($rule) - 1 === $firstSlashPosition) { + $pattern = '/'; } + $rule = trim($rule, '/'); + // remove delimiters as well as caret (^) and dollar sign ($) from the regex $rule = substr(Finder\Glob::toRegex($rule), 2, -2); From 94076c0bb980af15e29a72328f7bc1a41e46776f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 13:47:36 +0100 Subject: [PATCH 17/74] Make sure if plugins depend on the root package that the root package is also autoloaded correctly, refs #9530 --- src/Composer/Plugin/PluginManager.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 8aad71b8e..d16f364e4 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -21,6 +21,7 @@ use Composer\Package\RootPackage; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryInterface; use Composer\Repository\InstalledRepository; +use Composer\Repository\RootPackageRepository; use Composer\Package\PackageInterface; use Composer\Package\Link; use Composer\Semver\Constraint\Constraint; @@ -174,7 +175,8 @@ class PluginManager $localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; - $installedRepo = new InstalledRepository(array($localRepo)); + $rootPackageRepo = new RootPackageRepository($this->composer->getPackage()); + $installedRepo = new InstalledRepository(array($localRepo, $rootPackageRepo)); if ($globalRepo) { $installedRepo->addRepository($globalRepo); } @@ -185,11 +187,15 @@ class PluginManager $generator = $this->composer->getAutoloadGenerator(); $autoloads = array(); foreach ($autoloadPackages as $autoloadPackage) { - $downloadPath = $this->getInstallPath($autoloadPackage, $globalRepo && $globalRepo->hasPackage($autoloadPackage)); + if ($autoloadPackage === $this->composer->getPackage()) { + $downloadPath = ''; + } else { + $downloadPath = $this->getInstallPath($autoloadPackage, $globalRepo && $globalRepo->hasPackage($autoloadPackage)); + } $autoloads[] = array($autoloadPackage, $downloadPath); } - $map = $generator->parseAutoloads($autoloads, new RootPackage('dummy/root-package', '1.0.0.0', '1.0.0')); + $map = $generator->parseAutoloads($autoloads, $this->composer->getPackage()); $classLoader = $generator->createLoader($map); $classLoader->register(); From d94e6384613bfbd4faf6c8e55e9943c51775374c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 14:00:02 +0100 Subject: [PATCH 18/74] Fix tests and edge case --- src/Composer/Plugin/PluginManager.php | 7 ++++--- tests/Composer/Test/Plugin/PluginInstallerTest.php | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index d16f364e4..42db74412 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -175,7 +175,8 @@ class PluginManager $localRepo = $this->composer->getRepositoryManager()->getLocalRepository(); $globalRepo = $this->globalComposer ? $this->globalComposer->getRepositoryManager()->getLocalRepository() : null; - $rootPackageRepo = new RootPackageRepository($this->composer->getPackage()); + $rootPackage = clone $this->composer->getPackage(); + $rootPackageRepo = new RootPackageRepository($rootPackage); $installedRepo = new InstalledRepository(array($localRepo, $rootPackageRepo)); if ($globalRepo) { $installedRepo->addRepository($globalRepo); @@ -187,7 +188,7 @@ class PluginManager $generator = $this->composer->getAutoloadGenerator(); $autoloads = array(); foreach ($autoloadPackages as $autoloadPackage) { - if ($autoloadPackage === $this->composer->getPackage()) { + if ($autoloadPackage === $rootPackage) { $downloadPath = ''; } else { $downloadPath = $this->getInstallPath($autoloadPackage, $globalRepo && $globalRepo->hasPackage($autoloadPackage)); @@ -195,7 +196,7 @@ class PluginManager $autoloads[] = array($autoloadPackage, $downloadPath); } - $map = $generator->parseAutoloads($autoloads, $this->composer->getPackage()); + $map = $generator->parseAutoloads($autoloads, $rootPackage); $classLoader = $generator->createLoader($map); $classLoader->register(); diff --git a/tests/Composer/Test/Plugin/PluginInstallerTest.php b/tests/Composer/Test/Plugin/PluginInstallerTest.php index fd6f182bb..c5c587b3e 100644 --- a/tests/Composer/Test/Plugin/PluginInstallerTest.php +++ b/tests/Composer/Test/Plugin/PluginInstallerTest.php @@ -18,6 +18,7 @@ use Composer\Installer\PluginInstaller; use Composer\Package\CompletePackage; use Composer\Package\Loader\JsonLoader; use Composer\Package\Loader\ArrayLoader; +use Composer\Package\RootPackage; use Composer\Plugin\PluginManager; use Composer\IO\BufferIO; use Composer\EventDispatcher\EventDispatcher; @@ -111,6 +112,7 @@ class PluginInstallerTest extends TestCase $this->composer->setInstallationManager($im); $this->composer->setAutoloadGenerator($this->autoloadGenerator); $this->composer->setEventDispatcher(new EventDispatcher($this->composer, $this->io)); + $this->composer->setPackage(new RootPackage('dummy/root', '1.0.0.0', '1.0.0')); $this->pm = new PluginManager($this->io, $this->composer); $this->composer->setPluginManager($this->pm); From 492f8cb3506458abfbaac02ef9e96a8e7e498585 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 14:05:55 +0100 Subject: [PATCH 19/74] Add more possessive quantifiers --- src/Composer/Json/JsonManipulator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index b25a0e63b..ecb15878b 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -20,12 +20,12 @@ use Composer\Repository\PlatformRepository; class JsonManipulator { private static $DEFINES = '(?(DEFINE) - (? -? (?= [1-9]|0(?!\d) ) \d++ (\.\d++)? ([eE] [+-]? \d++)? ) + (? -? (?= [1-9]|0(?!\d) ) \d++ (\.\d++)? ([eE] [+-]?+ \d++)? ) (? true | false | null ) (? " ([^"\\\\]*+ | \\\\ ["\\\\bfnrt\/] | \\\\ u [0-9A-Fa-f]{4} )* " ) - (? \[ (?: (?&json) \s*+ (?: , (?&json) \s*+ )*+ )? \s*+ \] ) + (? \[ (?: (?&json) \s*+ (?: , (?&json) \s*+ )*+ )?+ \s*+ \] ) (? \s*+ (?&string) \s*+ : (?&json) \s*+ ) - (? \{ (?: (?&pair) (?: , (?&pair) )*+ )? \s*+ \} ) + (? \{ (?: (?&pair) (?: , (?&pair) )*+ )?+ \s*+ \} ) (? \s*+ (?: (?&number) | (?&boolean) | (?&string) | (?&array) | (?&object) ) ) )'; From 4a740e3e61643900291fa5c34b76f76c662b06e9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 14:55:55 +0100 Subject: [PATCH 20/74] Add warnings when running updates without curl enabled, fixes #9558 --- src/Composer/Command/InstallCommand.php | 5 +++++ src/Composer/Command/UpdateCommand.php | 5 +++++ src/Composer/Console/Application.php | 4 ++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index d3f450c50..038406a34 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -15,6 +15,7 @@ namespace Composer\Command; use Composer\Installer; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; +use Composer\Util\HttpDownloader; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; @@ -93,6 +94,10 @@ EOT $composer = $this->getComposer(true, $input->getOption('no-plugins')); + if ((!$composer->getLocker() || !$composer->getLocker()->isLocked()) && !HttpDownloader::isCurlEnabled()) { + $io->writeError('Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.'); + } + $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'install', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php index 60a4758f1..84d329ba5 100644 --- a/src/Composer/Command/UpdateCommand.php +++ b/src/Composer/Command/UpdateCommand.php @@ -19,6 +19,7 @@ use Composer\IO\IOInterface; use Composer\Plugin\CommandEvent; use Composer\Plugin\PluginEvents; use Composer\Package\Version\VersionParser; +use Composer\Util\HttpDownloader; use Composer\Semver\Constraint\MultiConstraint; use Composer\Package\Link; use Symfony\Component\Console\Helper\Table; @@ -114,6 +115,10 @@ EOT $composer = $this->getComposer(true, $input->getOption('no-plugins')); + if (!HttpDownloader::isCurlEnabled()) { + $io->writeError('Composer is operating significantly slower than normal because you do not have the PHP curl extension enabled.'); + } + $packages = $input->getArgument('packages'); $reqs = $this->formatRequirements($input->getOption('with')); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index cd78684c1..76629d9c4 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -237,11 +237,11 @@ class Application extends BaseApplication } if (extension_loaded('xdebug') && !getenv('COMPOSER_DISABLE_XDEBUG_WARN')) { - $io->writeError('You are running composer with Xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug'); + $io->writeError('Composer is operating slower than normal because you have Xdebug enabled. See https://getcomposer.org/xdebug'); } if (defined('COMPOSER_DEV_WARNING_TIME') && $commandName !== 'self-update' && $commandName !== 'selfupdate' && time() > COMPOSER_DEV_WARNING_TIME) { - $io->writeError(sprintf('Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); + $io->writeError(sprintf('Warning: This development build of Composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF'])); } if ( From d8a5db4cbb0bc050797b7a2db15f2e561ec74b5a Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 14:59:21 +0100 Subject: [PATCH 21/74] Fix tests --- tests/Composer/Test/ApplicationTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Composer/Test/ApplicationTest.php b/tests/Composer/Test/ApplicationTest.php index 7e78ee68f..7cecb5794 100644 --- a/tests/Composer/Test/ApplicationTest.php +++ b/tests/Composer/Test/ApplicationTest.php @@ -61,7 +61,7 @@ class ApplicationTest extends TestCase $outputMock->expects($this->at($index++)) ->method("write") - ->with($this->equalTo('You are running composer with Xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug')); + ->with($this->equalTo('Composer is operating slower than normal because you have Xdebug enabled. See https://getcomposer.org/xdebug')); } $outputMock->expects($this->at($index++)) @@ -70,7 +70,7 @@ class ApplicationTest extends TestCase $outputMock->expects($this->at($index++)) ->method("write") - ->with($this->equalTo(sprintf('Warning: This development build of composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF']))); + ->with($this->equalTo(sprintf('Warning: This development build of Composer is over 60 days old. It is recommended to update it by running "%s self-update" to get the latest version.', $_SERVER['PHP_SELF']))); if (!defined('COMPOSER_DEV_WARNING_TIME')) { define('COMPOSER_DEV_WARNING_TIME', time() - 1); From dc759a69b5925c23f16d074e308d6e78b8885303 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 15:30:49 +0100 Subject: [PATCH 22/74] Update deps --- composer.lock | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/composer.lock b/composer.lock index a5e510d4d..371202b49 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.2.8", + "version": "1.2.9", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "8a7ecad675253e4654ea05505233285377405215" + "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215", - "reference": "8a7ecad675253e4654ea05505233285377405215", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5", + "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5", "shasum": "" }, "require": { @@ -26,14 +26,15 @@ "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", + "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "1.x-dev" } }, "autoload": { @@ -63,7 +64,7 @@ "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.8" + "source": "https://github.com/composer/ca-bundle/tree/1.2.9" }, "funding": [ { @@ -79,7 +80,7 @@ "type": "tidelift" } ], - "time": "2020-08-23T12:54:47+00:00" + "time": "2021-01-12T12:10:35+00:00" }, { "name": "composer/semver", From 738a89ffe18e18d418c3fc0a1bb556cf55520722 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 16:19:29 +0100 Subject: [PATCH 23/74] Doc tweaks, fixes composer/getcomposer.org#178 --- doc/03-cli.md | 2 ++ doc/articles/authentication-for-private-packages.md | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 0561bebef..7aba323a7 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -943,6 +943,8 @@ The `COMPOSER_HOME` var allows you to change the Composer home directory. This is a hidden, global (per-user on the machine) directory that is shared between all projects. +Use `composer config --global home` to see the location of the home directory. + By default, it points to `C:\Users\\AppData\Roaming\Composer` on Windows and `/Users//.composer` on macOS. On \*nix systems that follow the [XDG Base Directory Specifications](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html), diff --git a/doc/articles/authentication-for-private-packages.md b/doc/articles/authentication-for-private-packages.md index 1c065cb6d..11da2f035 100644 --- a/doc/articles/authentication-for-private-packages.md +++ b/doc/articles/authentication-for-private-packages.md @@ -80,10 +80,9 @@ To fix this you need to open the file in an editor and fix the error. To find th your global `auth.json`, execute: ```sh -composer config --global --list +composer config --global home ``` -And look for the `[home]` section. (It is by default `~/.composer` or `%APPDATA%/Composer` on Windows) The folder will contain your global `auth.json` if it exists. You can open this file in your favorite editor and fix the error. From a20ee1a448337b8de157110a6cbe6da029a2c669 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 12 Jan 2021 16:31:36 +0100 Subject: [PATCH 24/74] Avoid matching .git suffix if present in private github URLs, fixes #9590 --- src/Composer/Util/Git.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 4eafc24e0..ed6da6c80 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -101,7 +101,7 @@ class Git $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) + || preg_match('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*?)(?:\.git)?$}', $url, $match) ) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); @@ -122,7 +122,7 @@ class Git $errorMsg = $this->process->getErrorOutput(); } - } elseif (preg_match('{^https://(bitbucket\.org)/(.*)(\.git)?$}U', $url, $match)) { //bitbucket oauth + } elseif (preg_match('{^https://(bitbucket\.org)/(.*)(?:\.git)?$}U', $url, $match)) { //bitbucket oauth $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process); if (!$this->io->hasAuthentication($match[1])) { From c043fe841b038d890f2f18fe54e607ccdbc790fe Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 12 Jan 2021 17:42:42 +0100 Subject: [PATCH 25/74] Remove sleeps from curl handling, select it is responsible for waiting The current sleeps mean that large files download slowly as select would return quickly when data has arrived and needs to be processed, but the sleep waits while the buffers are full. On the flipside we need to ensure that some code that would keep the CPU busy if run too often does not get run every time select returns. --- src/Composer/Util/Http/CurlDownloader.php | 47 +++++++++++++---------- src/Composer/Util/HttpDownloader.php | 2 - src/Composer/Util/Loop.php | 10 ++++- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index de4cd3d11..bcf0a7843 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -49,6 +49,7 @@ class CurlDownloader CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!'), ); + private $lastInfoUpdate = 0; private static $options = array( 'http' => array( @@ -240,6 +241,7 @@ class CurlDownloader } $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle)); + $this->lastInfoUpdate = 0; // TODO progress return (int) $curlHandle; @@ -390,30 +392,35 @@ class CurlDownloader } } - foreach ($this->jobs as $i => $curlHandle) { - if (!isset($this->jobs[$i])) { - continue; - } - $curlHandle = $this->jobs[$i]['curlHandle']; - $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo); + if (microtime(true) - $this->lastInfoUpdate > 0.1) { + $this->lastInfoUpdate = microtime(true); - if ($this->jobs[$i]['progress'] !== $progress) { - $this->jobs[$i]['progress'] = $progress; - - if (isset($this->jobs[$i]['options']['max_file_size'])) { - // Compare max_file_size with the content-length header this value will be -1 until the header is parsed - if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) { - throw new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); - } - - // Compare max_file_size with the download size in bytes - if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) { - throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); - } + foreach ($this->jobs as $i => $curlHandle) { + if (!isset($this->jobs[$i])) { + continue; } + $curlHandle = $this->jobs[$i]['curlHandle']; + $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo); - // TODO progress + if ($this->jobs[$i]['progress'] !== $progress) { + $this->jobs[$i]['progress'] = $progress; + + if (isset($this->jobs[$i]['options']['max_file_size'])) { + // Compare max_file_size with the content-length header this value will be -1 until the header is parsed + if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) { + throw new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); + } + + // Compare max_file_size with the download size in bytes + if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) { + throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); + } + } + + // TODO progress + } } + } } diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index 8f3d606d8..045e9575b 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -340,8 +340,6 @@ class HttpDownloader if (!$this->countActiveJobs($index)) { return; } - - usleep(1000); } } diff --git a/src/Composer/Util/Loop.php b/src/Composer/Util/Loop.php index a75888a8c..de2b8d2e8 100644 --- a/src/Composer/Util/Loop.php +++ b/src/Composer/Util/Loop.php @@ -85,6 +85,7 @@ class Loop $progress->start($totalJobs); } + $lastUpdate = 0; while (true) { $activeJobs = 0; @@ -95,15 +96,20 @@ class Loop $activeJobs += $this->processExecutor->countActiveJobs(); } - if ($progress) { + if ($progress && microtime(true) - $lastUpdate > 0.1) { + $lastUpdate = microtime(true); + echo "setting progress\n"; $progress->setProgress($progress->getMaxSteps() - $activeJobs); } if (!$activeJobs) { break; } + } - usleep(5000); + // as we skip progress updates if they are too quick, make sure we do one last one here at 100% + if ($progress) { + $progress->setProgress($progress->getMaxSteps()); } unset($this->currentPromises[$waitIndex]); From d6653013684ca747d4cfdabba14f4e4032cbb3ef Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 12 Jan 2021 21:04:52 +0100 Subject: [PATCH 26/74] Rewrite while if return as do while --- src/Composer/Util/HttpDownloader.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index 045e9575b..519e17c54 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -336,11 +336,9 @@ class HttpDownloader */ public function wait($index = null) { - while (true) { - if (!$this->countActiveJobs($index)) { - return; - } - } + do { + $jobCount = $this->countActiveJobs($index); + } while ($jobCount); } /** From 147d8849966beccce82b45cac2638b7c1661b9c0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 13 Jan 2021 11:38:45 +0100 Subject: [PATCH 27/74] Also avoid matching .git suffix in bitbucket URLs, refs #9590 --- 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 ed6da6c80..434a9e0d5 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -122,7 +122,7 @@ class Git $errorMsg = $this->process->getErrorOutput(); } - } elseif (preg_match('{^https://(bitbucket\.org)/(.*)(?:\.git)?$}U', $url, $match)) { //bitbucket oauth + } elseif (preg_match('{^https://(bitbucket\.org)/(.*?)(?:\.git)?$}U', $url, $match)) { //bitbucket oauth $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process); if (!$this->io->hasAuthentication($match[1])) { From de58c5499ebc4d4c0193803e006f46a9eec02fda Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 13 Jan 2021 11:45:00 +0100 Subject: [PATCH 28/74] Always make the root package autoloadable when executing plugins, fixes #9530 --- src/Composer/Plugin/PluginManager.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 42db74412..d89db97f7 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -186,13 +186,13 @@ class PluginManager $autoloadPackages = $this->collectDependencies($installedRepo, $autoloadPackages, $package); $generator = $this->composer->getAutoloadGenerator(); - $autoloads = array(); + $autoloads = array(array($rootPackage, '')); foreach ($autoloadPackages as $autoloadPackage) { if ($autoloadPackage === $rootPackage) { - $downloadPath = ''; - } else { - $downloadPath = $this->getInstallPath($autoloadPackage, $globalRepo && $globalRepo->hasPackage($autoloadPackage)); + continue; } + + $downloadPath = $this->getInstallPath($autoloadPackage, $globalRepo && $globalRepo->hasPackage($autoloadPackage)); $autoloads[] = array($autoloadPackage, $downloadPath); } From f72ad485ef4c8085e7170d7514be4785ae9bbc55 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 12 Jan 2021 17:51:26 +0100 Subject: [PATCH 29/74] Revert lastInfoUpdate change to keep full comaptibility to current behavior --- src/Composer/Util/Http/CurlDownloader.php | 43 ++++++++++------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index bcf0a7843..de4cd3d11 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -49,7 +49,6 @@ class CurlDownloader CURLM_OUT_OF_MEMORY => array('CURLM_OUT_OF_MEMORY', 'You are doomed.'), CURLM_INTERNAL_ERROR => array('CURLM_INTERNAL_ERROR', 'This can only be returned if libcurl bugs. Please report it to us!'), ); - private $lastInfoUpdate = 0; private static $options = array( 'http' => array( @@ -241,7 +240,6 @@ class CurlDownloader } $this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle)); - $this->lastInfoUpdate = 0; // TODO progress return (int) $curlHandle; @@ -392,35 +390,30 @@ class CurlDownloader } } - if (microtime(true) - $this->lastInfoUpdate > 0.1) { - $this->lastInfoUpdate = microtime(true); + foreach ($this->jobs as $i => $curlHandle) { + if (!isset($this->jobs[$i])) { + continue; + } + $curlHandle = $this->jobs[$i]['curlHandle']; + $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo); - foreach ($this->jobs as $i => $curlHandle) { - if (!isset($this->jobs[$i])) { - continue; - } - $curlHandle = $this->jobs[$i]['curlHandle']; - $progress = array_diff_key(curl_getinfo($curlHandle), self::$timeInfo); + if ($this->jobs[$i]['progress'] !== $progress) { + $this->jobs[$i]['progress'] = $progress; - if ($this->jobs[$i]['progress'] !== $progress) { - $this->jobs[$i]['progress'] = $progress; - - if (isset($this->jobs[$i]['options']['max_file_size'])) { - // Compare max_file_size with the content-length header this value will be -1 until the header is parsed - if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) { - throw new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); - } - - // Compare max_file_size with the download size in bytes - if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) { - throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); - } + if (isset($this->jobs[$i]['options']['max_file_size'])) { + // Compare max_file_size with the content-length header this value will be -1 until the header is parsed + if ($this->jobs[$i]['options']['max_file_size'] < $progress['download_content_length']) { + throw new MaxFileSizeExceededException('Maximum allowed download size reached. Content-length header indicates ' . $progress['download_content_length'] . ' bytes. Allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); } - // TODO progress + // Compare max_file_size with the download size in bytes + if ($this->jobs[$i]['options']['max_file_size'] < $progress['size_download']) { + throw new MaxFileSizeExceededException('Maximum allowed download size reached. Downloaded ' . $progress['size_download'] . ' of allowed ' . $this->jobs[$i]['options']['max_file_size'] . ' bytes'); + } } - } + // TODO progress + } } } From 8a85a79b50283f2e83481a5c17c39085e8d8d385 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Fri, 15 Jan 2021 10:10:27 +0100 Subject: [PATCH 30/74] Remove debug output --- src/Composer/Util/Loop.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/Util/Loop.php b/src/Composer/Util/Loop.php index de2b8d2e8..2e29b563f 100644 --- a/src/Composer/Util/Loop.php +++ b/src/Composer/Util/Loop.php @@ -98,7 +98,6 @@ class Loop if ($progress && microtime(true) - $lastUpdate > 0.1) { $lastUpdate = microtime(true); - echo "setting progress\n"; $progress->setProgress($progress->getMaxSteps() - $activeJobs); } From 6552b85100d96259e33d3a53bed5440e63120ecd Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 16 Jan 2021 22:03:52 +0100 Subject: [PATCH 31/74] Filesystem: use is_dir() instead of file_exists() which is faster this reproducible saves 1-2 seconds while running `COMPOSER_DISABLE_NETWORK=1 php composer/bin/composer install -vvv --profile` on the rector/rector project --- src/Composer/Util/Filesystem.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index d1200dde9..7d27b3ff1 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -131,7 +131,7 @@ class Filesystem // clear stat cache because external processes aren't tracked by the php stat cache clearstatcache(); - if ($result && !file_exists($directory)) { + if ($result && !is_dir($directory)) { return true; } @@ -600,7 +600,7 @@ class Filesystem if (!function_exists('symlink')) { return false; } - + $cwd = getcwd(); $relativePath = $this->findShortestPath($link, $target); From 8dfe45a026073d641693955efcf4c234063c32c4 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 16 Jan 2021 22:26:20 +0100 Subject: [PATCH 32/74] GitDownloader: combine checkout + reset commands into a single process use a single process instead of 3 to improve performance --- src/Composer/Downloader/GitDownloader.php | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index e43163dd0..dd8a7d873 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -454,13 +454,10 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface $command = sprintf('git checkout %s --', ProcessExecutor::escape($branch)); $fallbackCommand = sprintf('git checkout '.$force.'-B %s %s --', ProcessExecutor::escape($branch), ProcessExecutor::escape('composer/'.$branch)); - if (0 === $this->process->execute($command, $output, $path) - || 0 === $this->process->execute($fallbackCommand, $output, $path) - ) { - $command = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference)); - if (0 === $this->process->execute($command, $output, $path)) { - return null; - } + $resetCommand = sprintf('git reset --hard %s --', ProcessExecutor::escape($reference)); + + if (0 === $this->process->execute("($command || $fallbackCommand) && $resetCommand", $output, $path)) { + return null; } } From 6774e7ee291f075f3d158d71f24780c53ae7e3f6 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 16 Jan 2021 22:45:41 +0100 Subject: [PATCH 33/74] Filesystem: use fast operation first in emptyDirectory() --- src/Composer/Util/Filesystem.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index d1200dde9..0c0309c55 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -63,7 +63,7 @@ class Filesystem public function emptyDirectory($dir, $ensureDirectoryExists = true) { - if (file_exists($dir) && is_link($dir)) { + if (is_link($dir) && file_exists($dir)) { $this->unlink($dir); } @@ -600,7 +600,7 @@ class Filesystem if (!function_exists('symlink')) { return false; } - + $cwd = getcwd(); $relativePath = $this->findShortestPath($link, $target); From 62817a4cb2aa484cf275399b432763a2b036b103 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sat, 16 Jan 2021 22:50:58 +0100 Subject: [PATCH 34/74] fast operation first --- src/Composer/Util/Filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 0c0309c55..8648b41d4 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -108,7 +108,7 @@ class Filesystem return unlink($directory); } - if (!file_exists($directory) || !is_dir($directory)) { + if (!is_dir($directory) || !file_exists($directory)) { return true; } From c33f2615b5609854f838bbddb1a274eaeeeac3a9 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Sun, 17 Jan 2021 14:13:58 +0100 Subject: [PATCH 35/74] Include json response bodies in transport exceptions, fixes #9606 --- src/Composer/Util/Http/CurlDownloader.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index de4cd3d11..0584fa77d 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -515,8 +515,15 @@ class CurlDownloader @unlink($job['filename'].'~'); } - $exception = new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')', $response->getStatusCode()); + $details = ''; + if ($response->getHeader('content-type') === 'application/json') { + $details = ':'.PHP_EOL.substr($response->getBody(), 0, 200).(strlen($response->getBody()) > 200 ? '...' : ''); + } + + $exception = new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')' . $details, $response->getStatusCode()); $exception->setResponseInfo($responseInfo); + $exception->setHeaders($response->getHeaders()); + $exception->setResponse($response->getBody()); return $exception; } From 4a6f1792ea5a9f89b214f742a2b4dcfa6efb36b6 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Mon, 18 Jan 2021 10:11:08 +0100 Subject: [PATCH 36/74] adjusted test expectations --- .../Composer/Test/Downloader/GitDownloaderTest.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/Composer/Test/Downloader/GitDownloaderTest.php b/tests/Composer/Test/Downloader/GitDownloaderTest.php index 2de8f329f..c16c925b6 100644 --- a/tests/Composer/Test/Downloader/GitDownloaderTest.php +++ b/tests/Composer/Test/Downloader/GitDownloaderTest.php @@ -122,12 +122,7 @@ class GitDownloaderTest extends TestCase $processExecutor->expects($this->at(2)) ->method('execute') - ->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) - ->will($this->returnValue(0)); - - $processExecutor->expects($this->at(3)) - ->method('execute') - ->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) + ->with($this->equalTo($this->winCompat("(git checkout 'master' -- || git checkout -B 'master' 'composer/master' --) && git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); $downloader = $this->getDownloaderMock(null, null, $processExecutor); @@ -198,12 +193,7 @@ class GitDownloaderTest extends TestCase $processExecutor->expects($this->at(5)) ->method('execute') - ->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) - ->will($this->returnValue(0)); - - $processExecutor->expects($this->at(6)) - ->method('execute') - ->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) + ->with($this->equalTo($this->winCompat("(git checkout 'master' -- || git checkout -B 'master' 'composer/master' --) && git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath'))) ->will($this->returnValue(0)); $downloader = $this->getDownloaderMock(null, $config, $processExecutor); From b4cff190a89f8db9165ec5625a9b3f6281ab889c Mon Sep 17 00:00:00 2001 From: Ulrich Eckhardt Date: Thu, 21 Jan 2021 14:33:49 +0100 Subject: [PATCH 37/74] Remove dependency on wget from install docs Some people have wget, others have cURL, but we all have PHP. ;) --- doc/faqs/how-to-install-composer-programmatically.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/faqs/how-to-install-composer-programmatically.md b/doc/faqs/how-to-install-composer-programmatically.md index 3b378a5ab..6299b6d40 100644 --- a/doc/faqs/how-to-install-composer-programmatically.md +++ b/doc/faqs/how-to-install-composer-programmatically.md @@ -9,7 +9,7 @@ An alternative is to use this script which only works with UNIX utilities: ```bash #!/bin/sh -EXPECTED_CHECKSUM="$(wget -q -O - https://composer.github.io/installer.sig)" +EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" From bb393588ea4a6b2cd6efb9ee3acfdfddfecd0bcf Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Thu, 21 Jan 2021 16:11:15 +0000 Subject: [PATCH 38/74] Docs: explain how Composer gets the stability from a version --- doc/articles/versions.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/articles/versions.md b/doc/articles/versions.md index fc2f36c55..5f5515930 100644 --- a/doc/articles/versions.md +++ b/doc/articles/versions.md @@ -80,6 +80,19 @@ In the above example, if you wanted to check out the `my-feature` branch, you wo When branch names look like versions, we have to clarify for composer that we're trying to check out a branch and not a tag. In the above example, we have two version branches: `v1` and `v2`. To get Composer to check out one of these branches, you must specify a version constraint that looks like this: `v1.x-dev`. The `.x` is an arbitrary string that Composer requires to tell it that we're talking about the `v1` branch and not a `v1` tag (alternatively, you can name the branch `v1.x` instead of `v1`). In the case of a branch with a version-like name (`v1`, in this case), you append `-dev` as a suffix, rather than using `dev-` as a prefix. +### Stabilities + +Composer recognizes the following stabilities (in order of stability): dev, +alpha, beta, RC, and stable where RC stands for release candidate. The stability +of a version is defined by its suffix e.g version `v1.1-BETA` has a stability of +`beta` and `v1.1-RC1` has a stability of `rc`. If such a suffix is missing +e.g. version `v1.1` then Composer considers that version `stable`. In addition +to that Composer automatically adds a `-dev` suffix to all numeric branches and +prefixes all other branches imported from a VCS repository with `dev-`. In both +cases the stability `dev` gets assigned. + +Keeping this in mind will help you in the next section. + ### Minimum Stability There's one more thing that will affect which files are checked out of a library's VCS and added to your project: Composer allows you to specify stability constraints to limit which tags are considered valid. In the above example, note that the library released a beta and two release candidates for version `1.1` before the final official release. To receive these versions when running `composer install` or `composer update`, we have to explicitly tell Composer that we are ok with release candidates and beta releases (and alpha releases, if we want those). This can be done using either a project-wide `minimum-stability` value in `composer.json` or using "stability flags" in version constraints. Read more on the [schema page](../04-schema.md#minimum-stability). From b8f9550a2e19540cd19235cafaa05af801cf3bf6 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 25 Jan 2021 21:27:43 +0100 Subject: [PATCH 39/74] Update doc/articles/versions.md --- doc/articles/versions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/versions.md b/doc/articles/versions.md index 5f5515930..67f2c2958 100644 --- a/doc/articles/versions.md +++ b/doc/articles/versions.md @@ -85,7 +85,7 @@ When branch names look like versions, we have to clarify for composer that we're Composer recognizes the following stabilities (in order of stability): dev, alpha, beta, RC, and stable where RC stands for release candidate. The stability of a version is defined by its suffix e.g version `v1.1-BETA` has a stability of -`beta` and `v1.1-RC1` has a stability of `rc`. If such a suffix is missing +`beta` and `v1.1-RC1` has a stability of `RC`. If such a suffix is missing e.g. version `v1.1` then Composer considers that version `stable`. In addition to that Composer automatically adds a `-dev` suffix to all numeric branches and prefixes all other branches imported from a VCS repository with `dev-`. In both From 5c35f37f92694cdff563942f3ce3e5143893b853 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 Jan 2021 09:41:02 +0100 Subject: [PATCH 40/74] Properly handle installed repos as additionalFixedRepository, fixes #9574 --- src/Composer/Installer.php | 19 ++++++++++---- src/Composer/Util/Http/CurlDownloader.php | 31 ++++++++++------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index d0cfa9d56..e9f0bd74e 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -43,6 +43,7 @@ use Composer\Package\Version\VersionParser; use Composer\Package\Package; use Composer\Repository\ArrayRepository; use Composer\Repository\RepositorySet; +use Composer\Repository\CompositeRepository; use Composer\Semver\Constraint\Constraint; use Composer\Package\Locker; use Composer\Package\RootPackageInterface; @@ -767,14 +768,22 @@ class Installer $repositorySet->addRepository(new RootPackageRepository($this->fixedRootPackage)); $repositorySet->addRepository($platformRepo); if ($this->additionalFixedRepository) { - $additionalFixedRepository = $this->additionalFixedRepository; - // wrap the repository in a FilterRepository if needed to avoid warnings about installed repositories being used in the RepositorySet + // allow using installed repos if needed to avoid warnings about installed repositories being used in the RepositorySet // see https://github.com/composer/composer/pull/9574 - if ($additionalFixedRepository instanceof InstalledRepository || $additionalFixedRepository instanceof InstalledRepositoryInterface) { - $additionalFixedRepository = new FilterRepository($additionalFixedRepository, array()); + $additionalFixedRepositories = $this->additionalFixedRepository; + if ($additionalFixedRepositories instanceof CompositeRepository) { + $additionalFixedRepositories = $additionalFixedRepositories->getRepositories(); + } else { + $additionalFixedRepositories = array($additionalFixedRepositories); + } + foreach ($additionalFixedRepositories as $additionalFixedRepository) { + if ($additionalFixedRepository instanceof InstalledRepository || $additionalFixedRepository instanceof InstalledRepositoryInterface) { + $repositorySet->allowInstalledRepositories(); + break; + } } - $repositorySet->addRepository($additionalFixedRepository); + $repositorySet->addRepository($this->additionalFixedRepository); } return $repositorySet; diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index 0584fa77d..5e9494aed 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -303,9 +303,7 @@ class CurlDownloader if (!$error && function_exists('curl_strerror')) { $error = curl_strerror($errno); } - $exception = new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); - $exception->setResponseInfo($progress); - throw $exception; + throw new TransportException('curl error '.$errno.' while downloading '.Url::sanitize($progress['url']).': '.$error); } $statusCode = $progress['http_code']; rewind($job['headerHandle']); @@ -337,7 +335,7 @@ class CurlDownloader HttpDownloader::outputWarnings($this->io, $job['origin'], json_decode($response->getBody(), true)); } - $result = $this->isAuthenticatedRetryNeeded($job, $response, $progress); + $result = $this->isAuthenticatedRetryNeeded($job, $response); if ($result['retry']) { $this->restartJob($job, $job['url'], array('storeAuth' => $result['storeAuth'])); continue; @@ -345,7 +343,7 @@ class CurlDownloader // handle 3xx redirects, 304 Not Modified is excluded if ($statusCode >= 300 && $statusCode <= 399 && $statusCode !== 304 && $job['attributes']['redirects'] < $this->maxRedirects) { - $location = $this->handleRedirect($job, $response, $progress); + $location = $this->handleRedirect($job, $response); if ($location) { $this->restartJob($job, $location, array('redirects' => $job['attributes']['redirects'] + 1)); continue; @@ -354,7 +352,7 @@ class CurlDownloader // fail 4xx and 5xx responses and capture the response if ($statusCode >= 400 && $statusCode <= 599) { - throw $this->failResponse($job, $response, $response->getStatusMessage(), $progress); + throw $this->failResponse($job, $response, $response->getStatusMessage()); } if ($job['attributes']['storeAuth']) { @@ -376,6 +374,9 @@ class CurlDownloader if ($e instanceof TransportException && $response) { $e->setResponse($response->getBody()); } + if ($e instanceof TransportException && $progress) { + $e->setResponseInfo($progress); + } if (is_resource($job['headerHandle'])) { fclose($job['headerHandle']); @@ -417,7 +418,7 @@ class CurlDownloader } } - private function handleRedirect(array $job, Response $response, array $responseInfo) + private function handleRedirect(array $job, Response $response) { if ($locationHeader = $response->getHeader('location')) { if (parse_url($locationHeader, PHP_URL_SCHEME)) { @@ -445,12 +446,10 @@ class CurlDownloader return $targetUrl; } - $exception = new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')'); - $exception->setResponseInfo($responseInfo); - throw $exception; + throw new TransportException('The "'.$job['url'].'" file could not be downloaded, got redirect without Location ('.$response->getStatusMessage().')'); } - private function isAuthenticatedRetryNeeded(array $job, Response $response, array $responseInfo) + private function isAuthenticatedRetryNeeded(array $job, Response $response) { if (in_array($response->getStatusCode(), array(401, 403)) && $job['attributes']['retryAuthFailure']) { $result = $this->authHelper->promptAuthIfNeeded($job['url'], $job['origin'], $response->getStatusCode(), $response->getStatusMessage(), $response->getHeaders()); @@ -491,7 +490,7 @@ class CurlDownloader } } - throw $this->failResponse($job, $response, $needsAuthRetry, $responseInfo); + throw $this->failResponse($job, $response, $needsAuthRetry); } return array('retry' => false, 'storeAuth' => false); @@ -509,7 +508,7 @@ class CurlDownloader $this->initDownload($job['resolve'], $job['reject'], $origin, $url, $job['options'], $job['filename'], $attributes); } - private function failResponse(array $job, Response $response, $errorMessage, array $responseInfo) + private function failResponse(array $job, Response $response, $errorMessage) { if ($job['filename']) { @unlink($job['filename'].'~'); @@ -520,11 +519,7 @@ class CurlDownloader $details = ':'.PHP_EOL.substr($response->getBody(), 0, 200).(strlen($response->getBody()) > 200 ? '...' : ''); } - $exception = new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')' . $details, $response->getStatusCode()); - $exception->setResponseInfo($responseInfo); - $exception->setHeaders($response->getHeaders()); - $exception->setResponse($response->getBody()); - return $exception; + return new TransportException('The "'.$job['url'].'" file could not be downloaded ('.$errorMessage.')' . $details, $response->getStatusCode()); } private function checkCurlResult($code) From 2cb9630320370109a733b214049b6c2028712307 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 26 Jan 2021 15:54:21 +0100 Subject: [PATCH 41/74] Fix $_SERVER var not being updated when using putenv, refs b80038804ff791a9d4608d737ad937b6033b0bae and fixes #9372 --- src/Composer/EventDispatcher/EventDispatcher.php | 2 ++ src/Composer/Util/Git.php | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index 3d4542a96..52b26d98e 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -234,6 +234,8 @@ class EventDispatcher if (substr($exec, 0, 8) === '@putenv ') { putenv(substr($exec, 8)); + list($var, $value) = explode('=', substr($exec, 8), 2); + $_SERVER[$var] = $value; continue; } elseif (substr($exec, 0, 5) === '@php ') { diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index cdb559712..cd714c7e8 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -347,7 +347,7 @@ class Git // added in git 1.7.1, prevents prompting the user for username/password if (getenv('GIT_ASKPASS') !== 'echo') { putenv('GIT_ASKPASS=echo'); - unset($_SERVER['GIT_ASKPASS']); + $_SERVER['GIT_ASKPASS'] = 'echo'; } // clean up rogue git env vars in case this is running in a git hook @@ -363,6 +363,7 @@ class Git // Run processes with predictable LANGUAGE if (getenv('LANGUAGE') !== 'C') { putenv('LANGUAGE=C'); + $_SERVER['LANGUAGE'] = 'C'; } // clean up env for OSX, see https://github.com/composer/composer/issues/2146#issuecomment-35478940 From 8eaae9f50a953a4a7f38c9571e3925ddfadff05d Mon Sep 17 00:00:00 2001 From: Ben Beckford Date: Tue, 26 Jan 2021 20:48:19 +0000 Subject: [PATCH 42/74] Suppress error when chdir($oldWorkingDir); unsuccessful resolves #9649 --- src/Composer/Console/Application.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 76629d9c4..8684ea3ed 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -309,8 +309,11 @@ class Application extends BaseApplication $result = parent::doRun($input, $output); + // chdir back to $oldWorkingDir if set if (isset($oldWorkingDir)) { - chdir($oldWorkingDir); + Silencer::call(function () use ($oldWorkingDir) { + chdir($oldWorkingDir); + }); } if (isset($startTime)) { From 4e0b8c108618ea5e260a61ab13108829e6792413 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 Jan 2021 09:17:25 +0100 Subject: [PATCH 43/74] Update src/Composer/Console/Application.php --- src/Composer/Console/Application.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 8684ea3ed..9d3557e27 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -311,9 +311,7 @@ class Application extends BaseApplication // chdir back to $oldWorkingDir if set if (isset($oldWorkingDir)) { - Silencer::call(function () use ($oldWorkingDir) { - chdir($oldWorkingDir); - }); + Silencer::call('chdir', $oldWorkingDir); } if (isset($startTime)) { From ba94445bb9401528ca048213252a514d6aab800d Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 21 Jan 2021 15:06:39 +0100 Subject: [PATCH 44/74] Ensure InstalledVersions reports info about all currently registered class loaders --- .github/workflows/phpstan.yml | 2 +- src/Composer/Autoload/AutoloadGenerator.php | 2 +- src/Composer/Autoload/ClassLoader.php | 32 ++++ src/Composer/InstalledVersions.php | 147 +++++++++++++----- .../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 +- 10 files changed, 146 insertions(+), 49 deletions(-) diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml index 538868318..60b9afaf2 100644 --- a/.github/workflows/phpstan.yml +++ b/.github/workflows/phpstan.yml @@ -52,5 +52,5 @@ jobs: - name: Run PHPStan # Locked to phpunit 7.5 here as newer ones have void return types which break inheritance run: | - bin/composer require --dev phpstan/phpstan:^0.12.42 phpunit/phpunit:^7.5.20 --with-all-dependencies ${{ env.COMPOSER_FLAGS }} + bin/composer require --dev phpstan/phpstan:^0.12.69 phpunit/phpunit:^7.5.20 --with-all-dependencies ${{ env.COMPOSER_FLAGS }} vendor/bin/phpstan analyse --configuration=phpstan/config.neon diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 6ad5d94b0..21dc88df0 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -838,7 +838,7 @@ PLATFORM_CHECK; $file .= <<vendorDir = $vendorDir; + } + public function getPrefixes() { if (!empty($this->prefixesPsr0)) { @@ -300,6 +309,15 @@ class ClassLoader public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + //no-op + } elseif ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } } /** @@ -308,6 +326,10 @@ class ClassLoader public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } } /** @@ -367,6 +389,16 @@ class ClassLoader return $file; } + /** + * Returns the currently registered loaders indexed by their corresponding vendor directories. + * + * @return self[] + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + private function findFileWithExtension($class, $ext) { // PSR-4 lookup diff --git a/src/Composer/InstalledVersions.php b/src/Composer/InstalledVersions.php index e492f988a..e6909e934 100644 --- a/src/Composer/InstalledVersions.php +++ b/src/Composer/InstalledVersions.php @@ -12,6 +12,7 @@ namespace Composer; +use Composer\Autoload\ClassLoader; use Composer\Semver\VersionParser; /** @@ -22,6 +23,8 @@ use Composer\Semver\VersionParser; class InstalledVersions { private static $installed; + private static $canGetVendors; + private static $installedByVendor = array(); /** * Returns a list of all package names which are present, either by being installed, replaced or provided @@ -31,7 +34,17 @@ class InstalledVersions */ public static function getInstalledPackages() { - return array_keys(self::$installed['versions']); + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); } /** @@ -44,7 +57,13 @@ class InstalledVersions */ public static function isInstalled($packageName) { - return isset(self::$installed['versions'][$packageName]); + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return true; + } + } + + return false; } /** @@ -79,25 +98,29 @@ class InstalledVersions */ public static function getVersionRanges($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); } - $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); + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -106,15 +129,19 @@ class InstalledVersions */ public static function getVersion($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; } - if (!isset(self::$installed['versions'][$packageName]['version'])) { - return null; - } - - return self::$installed['versions'][$packageName]['version']; + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -123,15 +150,19 @@ class InstalledVersions */ public static function getPrettyVersion($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; } - if (!isset(self::$installed['versions'][$packageName]['pretty_version'])) { - return null; - } - - return self::$installed['versions'][$packageName]['pretty_version']; + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -140,15 +171,19 @@ class InstalledVersions */ public static function getReference($packageName) { - if (!isset(self::$installed['versions'][$packageName])) { - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; } - if (!isset(self::$installed['versions'][$packageName]['reference'])) { - return null; - } - - return self::$installed['versions'][$packageName]['reference']; + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); } /** @@ -157,7 +192,9 @@ class InstalledVersions */ public static function getRootPackage() { - return self::$installed['root']; + $installed = self::getInstalled(); + + return $installed[0]['root']; } /** @@ -192,5 +229,33 @@ class InstalledVersions public static function reload($data) { self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + // @phpstan-ignore-next-line + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; + } + } + } + + $installed[] = self::$installed; + + return $installed; } } 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 fdff2bd42..390bff3bd 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 @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoloadOrder } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoloadOrder', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php index 67ce8ffe2..71397e2f5 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_functions.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoload } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 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 790029096..47bb48f7f 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 @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoload } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); $includePaths = require __DIR__ . '/include_paths.php'; 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 7b7898df1..965542d83 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 @@ -23,7 +23,7 @@ class ComposerAutoloaderInitFilesAutoload } spl_autoload_register(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitFilesAutoload', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 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 2747dbc0a..1bbf29cb2 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_include_path.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitIncludePath } spl_autoload_register(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitIncludePath', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 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 8ec8cdd47..7b6b58bf9 100644 --- a/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_real_target_dir.php @@ -23,7 +23,7 @@ class ComposerAutoloaderInitTargetDir } spl_autoload_register(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__))); spl_autoload_unregister(array('ComposerAutoloaderInitTargetDir', 'loadClassLoader')); $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); From 51371944e1c95b0fa3bc4ed921a366f553938ef3 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 Jan 2021 11:05:53 +0100 Subject: [PATCH 45/74] Add comment about not removing the static $installed data, refs #9635 --- src/Composer/Repository/FilesystemRepository.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Repository/FilesystemRepository.php b/src/Composer/Repository/FilesystemRepository.php index 88b292ffb..2823e6ec9 100644 --- a/src/Composer/Repository/FilesystemRepository.php +++ b/src/Composer/Repository/FilesystemRepository.php @@ -204,6 +204,9 @@ class FilesystemRepository extends WritableArrayRepository $fs->filePutContentsIfModified($repoDir.'/installed.php', 'filePutContentsIfModified($repoDir.'/InstalledVersions.php', $installedVersionsClass); From 079e501ac8676db4b793bff18da84388daeff55e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 27 Jan 2021 14:03:44 +0100 Subject: [PATCH 46/74] Revert "Merge pull request #9273 from nicolas-grekas/dev-version" This reverts commit d2d606ced201722dc56b759933bec4c688e7d994, reversing changes made to 4a8dbcd1451b7fd1e3537dee2302549587db8e2c. --- doc/05-repositories.md | 11 --------- .../Package/Loader/RootPackageLoader.php | 6 ++--- src/Composer/Repository/PathRepository.php | 5 ---- .../Package/Loader/RootPackageLoaderTest.php | 24 ------------------- .../path/with-branch-version/composer.json | 6 ----- .../Test/Repository/PathRepositoryTest.php | 24 ++----------------- 6 files changed, 4 insertions(+), 72 deletions(-) delete mode 100644 tests/Composer/Test/Repository/Fixtures/path/with-branch-version/composer.json diff --git a/doc/05-repositories.md b/doc/05-repositories.md index cd3266285..95881b078 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -658,17 +658,6 @@ the branch or tag that is currently checked out. Otherwise, the version should be explicitly defined in the package's `composer.json` file. If the version cannot be resolved by these means, it is assumed to be `dev-master`. -When the version cannot be inferred from the local VCS repository, you should use -the special `branch-version` entry under `extra` instead of `version`: - -```json -{ - "extra": { - "branch-version": "4.2-dev" - } -} -``` - The local package will be symlinked if possible, in which case the output in the console will read `Symlinking from ../../packages/my-package`. If symlinking is _not_ possible the package will be copied. In that case, the console will diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 5eceec97d..32118b113 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -81,10 +81,8 @@ class RootPackageLoader extends ArrayLoader if (!isset($config['version'])) { $commit = null; - if (isset($config['extra']['branch-version'])) { - $config['version'] = preg_replace('{(\.x)?(-dev)?$}', '', $config['extra']['branch-version']).'.x-dev'; - } elseif (getenv('COMPOSER_ROOT_VERSION')) { - // override with env var if available + // override with env var if available + if (getenv('COMPOSER_ROOT_VERSION')) { $config['version'] = getenv('COMPOSER_ROOT_VERSION'); } else { $versionData = $this->versionGuesser->guessVersion($config, $cwd ?: getcwd()); diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index c3076e2e7..a0a6835b8 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -165,11 +165,6 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn ); $package['transport-options'] = $this->options; - // use the branch-version as the package version if available - if (!isset($package['version']) && isset($package['extra']['branch-version'])) { - $package['version'] = preg_replace('{(\.x)?(-dev)?$}', '', $package['extra']['branch-version']).'.x-dev'; - } - // carry over the root package version if this path repo is in the same git repository as root package if (!isset($package['version']) && ($rootVersion = getenv('COMPOSER_ROOT_VERSION'))) { if ( diff --git a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php index c50cc77e0..858e95231 100644 --- a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php @@ -201,28 +201,4 @@ class RootPackageLoaderTest extends TestCase $this->assertEquals("dev-latest-production", $package->getPrettyVersion()); } - - /** - * @dataProvider provideExtraBranchVersion - */ - public function testLoadExtraBranchVersion($branchVersion) - { - $package = $this->loadPackage(array( - 'extra' => array( - 'branch-version' => $branchVersion, - ), - )); - - $this->assertEquals('1.2.x-dev', $package->getPrettyVersion()); - } - - public function provideExtraBranchVersion() - { - return array( - array('1.2'), - array('1.2.x'), - array('1.2-dev'), - array('1.2.x-dev'), - ); - } } diff --git a/tests/Composer/Test/Repository/Fixtures/path/with-branch-version/composer.json b/tests/Composer/Test/Repository/Fixtures/path/with-branch-version/composer.json deleted file mode 100644 index 75545d5f9..000000000 --- a/tests/Composer/Test/Repository/Fixtures/path/with-branch-version/composer.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "test/path-branch-versioned", - "extra": { - "branch-version": "1.2" - } -} diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php index 628c3320d..159d8cc97 100644 --- a/tests/Composer/Test/Repository/PathRepositoryTest.php +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -72,23 +72,6 @@ class PathRepositoryTest extends TestCase $this->assertNotEmpty($packageVersion); } - public function testLoadPackageFromFileSystemWithExtraBranchVersion() - { - $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') - ->getMock(); - - $config = new \Composer\Config(); - $versionGuesser = null; - - $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', 'with-branch-version')); - $repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config); - $packages = $repository->getPackages(); - - $this->assertEquals(1, $repository->count()); - - $this->assertTrue($repository->hasPackage($this->getPackage('test/path-branch-versioned', '1.2.x-dev'))); - } - public function testLoadPackageFromFileSystemWithWildcard() { $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') @@ -102,7 +85,7 @@ class PathRepositoryTest extends TestCase $packages = $repository->getPackages(); $names = array(); - $this->assertEquals(3, $repository->count()); + $this->assertEquals(2, $repository->count()); $package = $packages[0]; $names[] = $package->getName(); @@ -110,11 +93,8 @@ class PathRepositoryTest extends TestCase $package = $packages[1]; $names[] = $package->getName(); - $package = $packages[2]; - $names[] = $package->getName(); - sort($names); - $this->assertEquals(array('test/path-branch-versioned', 'test/path-unversioned', 'test/path-versioned'), $names); + $this->assertEquals(array('test/path-unversioned', 'test/path-versioned'), $names); } /** From 725b33ee5a8e72559a53d4ee7e6d8482bec28c66 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 21 Jan 2021 20:39:55 +0100 Subject: [PATCH 47/74] Handle "versions" option in PathRepository, remove support for "branch-version" --- doc/05-repositories.md | 20 ++++++++++++ src/Composer/Repository/PathRepository.php | 6 ++++ .../Test/Repository/PathRepositoryTest.php | 31 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/doc/05-repositories.md b/doc/05-repositories.md index 95881b078..4c1c7e000 100644 --- a/doc/05-repositories.md +++ b/doc/05-repositories.md @@ -658,6 +658,26 @@ the branch or tag that is currently checked out. Otherwise, the version should be explicitly defined in the package's `composer.json` file. If the version cannot be resolved by these means, it is assumed to be `dev-master`. +When the version cannot be inferred from the local VCS repository, or when you +want to override the version, you can use the `versions` option when declaring +the repository: + +```json +{ + "repositories": [ + { + "type": "path", + "url": "../../packages/my-package", + "options": { + "versions": { + "my/package": "4.2-dev" + } + } + } + ] +} +``` + The local package will be symlinked if possible, in which case the output in the console will read `Symlinking from ../../packages/my-package`. If symlinking is _not_ possible the package will be copied. In that case, the console will diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index a0a6835b8..b6601f883 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -164,6 +164,12 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn 'reference' => sha1($json . serialize($this->options)), ); $package['transport-options'] = $this->options; + unset($package['transport-options']['versions']); + + // use the version provided as option if available + if (isset($package['name'], $this->options['versions'][$package['name']])) { + $package['version'] = $this->options['versions'][$package['name']]; + } // carry over the root package version if this path repo is in the same git repository as root package if (!isset($package['version']) && ($rootVersion = getenv('COMPOSER_ROOT_VERSION'))) { diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php index 159d8cc97..f2c8a9192 100644 --- a/tests/Composer/Test/Repository/PathRepositoryTest.php +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -97,6 +97,37 @@ class PathRepositoryTest extends TestCase $this->assertEquals(array('test/path-unversioned', 'test/path-versioned'), $names); } + public function testLoadPackageWithExplicitVersions() + { + $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') + ->getMock(); + + $config = new \Composer\Config(); + $versionGuesser = null; + + $options = array( + 'versions' => array( + 'test/path-unversioned' => '4.3.2.1', + 'test/path-versioned' => '3.2.1.0', + ), + ); + $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', '*')); + $repository = new PathRepository(array('url' => $repositoryUrl, 'options' => $options), $ioInterface, $config); + $packages = $repository->getPackages(); + $versions = array(); + + $this->assertEquals(2, $repository->count()); + + $package = $packages[0]; + $versions[$package->getName()] = $package->getVersion(); + + $package = $packages[1]; + $versions[$package->getName()] = $package->getVersion(); + + ksort($versions); + $this->assertSame(array('test/path-unversioned' => '4.3.2.1', 'test/path-versioned' => '3.2.1.0'), $versions); + } + /** * Verify relative repository URLs remain relative, see #4439 */ From bab210777ec26619e56037615c8d4a68c5b601f2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 Jan 2021 15:01:26 +0100 Subject: [PATCH 48/74] Update deps --- composer.lock | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index e33b5471d..6dd6c3844 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "composer/ca-bundle", - "version": "1.2.8", + "version": "1.2.9", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "8a7ecad675253e4654ea05505233285377405215" + "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215", - "reference": "8a7ecad675253e4654ea05505233285377405215", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5", + "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5", "shasum": "" }, "require": { @@ -26,14 +26,15 @@ "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8", + "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.x-dev" + "dev-main": "1.x-dev" } }, "autoload": { @@ -60,11 +61,6 @@ "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.8" - }, "funding": [ { "url": "https://packagist.com", @@ -79,7 +75,7 @@ "type": "tidelift" } ], - "time": "2020-08-23T12:54:47+00:00" + "time": "2021-01-12T12:10:35+00:00" }, { "name": "composer/semver", From 92313447d6234af021a43643bb4ad00d19fe5b42 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 Jan 2021 15:02:19 +0100 Subject: [PATCH 49/74] Filter out exclude-from-classmap rules to avoid generating very long regexes, fixes #9487 --- src/Composer/Autoload/AutoloadGenerator.php | 27 +++++++++++++++++-- src/Composer/Autoload/ClassMapGenerator.php | 2 +- .../Test/Autoload/AutoloadGeneratorTest.php | 10 +++++-- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 863ceac43..88d7046c6 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -231,7 +231,7 @@ EOF; $excluded = null; if (!empty($autoloads['exclude-from-classmap'])) { - $excluded = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}'; + $excluded = $autoloads['exclude-from-classmap']; } $classMap = array(); @@ -350,8 +350,31 @@ EOF; return $classMap; } + /** + * @param ?array $excluded + */ private function generateClassMap($dir, $excluded, $namespaceFilter, $autoloadType, $showAmbiguousWarning, array &$scannedFiles) { + if ($excluded) { + // filter excluded patterns here to only use those matching $dir + // exclude-from-classmap patterns are all realpath'd so we can only filter them if $dir exists so that realpath($dir) will work + // if $dir does not exist, it should anyway not find anything there so no trouble + if (file_exists($dir)) { + // transform $dir in the same way that exclude-from-classmap patterns are transformed so we can match them against each other + $dirMatch = preg_quote(strtr(realpath($dir), '\\', '/')); + foreach ($excluded as $index => $pattern) { + // extract the constant string prefix of the pattern here, until we reach a non-escaped regex special character + $pattern = preg_replace('{^(([^.+*?\[^\]$(){}=!<>|:\\\\#-]+|\\\\[.+*?\[^\]$(){}=!<>|:#-])*).*}', '$1', $pattern); + // if the pattern is not a subset or superset of $dir, it is unrelated and we skip it + if (0 !== strpos($pattern, $dirMatch) && 0 !== strpos($dirMatch, $pattern)) { + unset($excluded[$index]); + } + } + } + + $excluded = $excluded ? '{(' . implode('|', $excluded) . ')}' : null; + } + return ClassMapGenerator::createMap($dir, $excluded, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType, $scannedFiles); } @@ -458,7 +481,7 @@ EOF; if (isset($autoloads['classmap'])) { $excluded = null; if (!empty($autoloads['exclude-from-classmap'])) { - $excluded = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}'; + $excluded = $autoloads['exclude-from-classmap']; } $scannedFiles = array(); diff --git a/src/Composer/Autoload/ClassMapGenerator.php b/src/Composer/Autoload/ClassMapGenerator.php index 4adbcc8be..0724d5dd6 100644 --- a/src/Composer/Autoload/ClassMapGenerator.php +++ b/src/Composer/Autoload/ClassMapGenerator.php @@ -51,7 +51,7 @@ class ClassMapGenerator * Iterate over all files in the given directory searching for classes * * @param \Iterator|string $path The path to search in or an iterator - * @param string $excluded Regex that matches against the file path that exclude from the classmap. + * @param string $excluded Regex that matches file paths to be excluded from the classmap * @param IOInterface $io IO object * @param string $namespace Optional namespace prefix to filter by * @param string $autoloadType psr-0|psr-4 Optional autoload standard to use mapping rules diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 3cd8f7ff2..1811d06c0 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -1377,9 +1377,9 @@ EOF; $package->setAutoload(array( 'psr-0' => array('Foo' => '../path/../src'), 'psr-4' => array('Acme\Foo\\' => '../path/../src-psr4'), - 'classmap' => array('../classmap'), + 'classmap' => array('../classmap', '../classmap2/subdir', 'classmap3', 'classmap4'), 'files' => array('../test.php'), - 'exclude-from-classmap' => array('./../classmap/excluded'), + 'exclude-from-classmap' => array('./../classmap/excluded', '../classmap2', 'classmap3/classes.php', 'classmap4/*/classes.php'), )); $this->repository->expects($this->once()) @@ -1388,9 +1388,15 @@ EOF; $this->fs->ensureDirectoryExists($this->workingDir.'/src/Foo'); $this->fs->ensureDirectoryExists($this->workingDir.'/classmap/excluded'); + $this->fs->ensureDirectoryExists($this->workingDir.'/classmap2/subdir'); + $this->fs->ensureDirectoryExists($this->workingDir.'/working-dir/classmap3'); + $this->fs->ensureDirectoryExists($this->workingDir.'/working-dir/classmap4/foo/'); file_put_contents($this->workingDir.'/src/Foo/Bar.php', 'workingDir.'/classmap/classes.php', 'workingDir.'/classmap/excluded/classes.php', 'workingDir.'/classmap2/subdir/classes.php', 'workingDir.'/working-dir/classmap3/classes.php', 'workingDir.'/working-dir/classmap4/foo/classes.php', 'workingDir.'/test.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_14'); From 4d2ae787032afb7adcfbba60298f5718039ec1fa Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 Jan 2021 15:40:59 +0100 Subject: [PATCH 50/74] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8caad32b6..96f476eec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### [1.10.20] 2021-01-27 + + * Fixed exclude-from-classmap causing regex issues when having too many paths + * Fixed compatibility issue with Symfony 4/5 + ### [1.10.19] 2020-12-04 * Fixed regression on PHP 8.0 @@ -924,6 +929,7 @@ * Initial release +[1.10.20]: https://github.com/composer/composer/compare/1.10.19...1.10.20 [1.10.19]: https://github.com/composer/composer/compare/1.10.18...1.10.19 [1.10.18]: https://github.com/composer/composer/compare/1.10.17...1.10.18 [1.10.17]: https://github.com/composer/composer/compare/1.10.16...1.10.17 From 0adea1efbd97b00729643a27483e716970155139 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 27 Jan 2021 16:09:01 +0100 Subject: [PATCH 51/74] Update changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65079f34f..6db4259db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +### [2.0.9] 2021-01-27 + + * Added warning if the curl extension is not enabled as it significantly degrades performance + * Fixed InstalledVersions to report all packages when several vendor dirs are present in the same runtime + * Fixed download speed when downloading large files + * Fixed `archive` and path repo copies mishandling some .gitignore paths + * Fixed root package classes not being available to the plugins/scripts during the initial install + * Fixed cache writes to be atomic and better support multiple Composer processes running in parallel + * Fixed preg jit issues when `config` or `require` modifies large composer.json files + * Fixed compatibility with envs having open_basedir restrictions + * Fixed exclude-from-classmap causing regex issues when having too many paths + * Fixed compatibility issue with Symfony 4/5 + * Several small performance and debug output improvements + ### [2.0.8] 2020-12-03 * Fixed packages with aliases not matching conflicts which match the alias @@ -1081,6 +1095,7 @@ * Initial release +[2.0.9]: https://github.com/composer/composer/compare/2.0.8...2.0.9 [2.0.8]: https://github.com/composer/composer/compare/2.0.7...2.0.8 [2.0.7]: https://github.com/composer/composer/compare/2.0.6...2.0.7 [2.0.6]: https://github.com/composer/composer/compare/2.0.5...2.0.6 From 1a0e578b14a25c9dde8df4b20e8a5050cdf186ae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 29 Jan 2021 09:43:47 +0100 Subject: [PATCH 52/74] Remove ungreedy modifier and make a few domain matches case insensitive, fixes #9656 --- src/Composer/Util/Git.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 653969305..28d051245 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -101,7 +101,7 @@ class Git $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) . '/(.*?)(?:\.git)?$}', $url, $match) + || preg_match('{^https?://' . self::getGitHubDomainsRegex($this->config) . '/(.*?)(?:\.git)?$}i', $url, $match) ) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); @@ -122,7 +122,7 @@ class Git $errorMsg = $this->process->getErrorOutput(); } - } elseif (preg_match('{^https://(bitbucket\.org)/(.*?)(?:\.git)?$}U', $url, $match)) { //bitbucket oauth + } elseif (preg_match('{^https://(bitbucket\.org)/(.*?)(?:\.git)?$}i', $url, $match)) { //bitbucket oauth $bitbucketUtil = new Bitbucket($this->io, $this->config, $this->process); if (!$this->io->hasAuthentication($match[1])) { @@ -167,7 +167,7 @@ class Git } } elseif ( preg_match('{^(git)@' . self::getGitLabDomainsRegex($this->config) . ':(.+?\.git)$}i', $url, $match) - || preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}', $url, $match) + || preg_match('{^(https?)://' . self::getGitLabDomainsRegex($this->config) . '/(.*)}i', $url, $match) ) { if ($match[1] === 'git') { $match[1] = 'https'; From cee34b06b4ee072915564533ab778b8c32ccdecf Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 29 Jan 2021 09:46:21 +0100 Subject: [PATCH 53/74] No need to ignore phpstan error now that getRegisteredLoaders is in a release --- src/Composer/InstalledVersions.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Composer/InstalledVersions.php b/src/Composer/InstalledVersions.php index e6909e934..bd5647882 100644 --- a/src/Composer/InstalledVersions.php +++ b/src/Composer/InstalledVersions.php @@ -244,7 +244,6 @@ class InstalledVersions $installed = array(); if (self::$canGetVendors) { - // @phpstan-ignore-next-line foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { if (isset(self::$installedByVendor[$vendorDir])) { $installed[] = self::$installedByVendor[$vendorDir]; From 980aac6eb2fe25cd303d734da9cd5e630124aa5f Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Fri, 29 Jan 2021 20:10:18 +0700 Subject: [PATCH 54/74] Minor improvement to ClassLoader::register no-op block A recent change in the ClassAutoloader (#9635) added support for specifying a vendorDir, and `\Composer\Autoload\ClassLoader::register` now has a if-elseif-else chain. The first block has a `// no-op` comment, followed by an `elseif` block. It's more readable to `return;`, and remove the `elseif` for readability. --- src/Composer/Autoload/ClassLoader.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Composer/Autoload/ClassLoader.php b/src/Composer/Autoload/ClassLoader.php index 4d989a212..247294d66 100644 --- a/src/Composer/Autoload/ClassLoader.php +++ b/src/Composer/Autoload/ClassLoader.php @@ -311,8 +311,10 @@ class ClassLoader spl_autoload_register(array($this, 'loadClass'), true, $prepend); if (null === $this->vendorDir) { - //no-op - } elseif ($prepend) { + return; + } + + if ($prepend) { self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; } else { unset(self::$registeredLoaders[$this->vendorDir]); From 4ade9bd96097992d3986c98199ef63e4348decc0 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Feb 2021 12:55:55 +0100 Subject: [PATCH 55/74] Avoid accessing undefined indices, fixes #9654 --- src/Composer/Util/Http/ProxyManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/Http/ProxyManager.php b/src/Composer/Util/Http/ProxyManager.php index 693ef92ad..edd486ef3 100644 --- a/src/Composer/Util/Http/ProxyManager.php +++ b/src/Composer/Util/Http/ProxyManager.php @@ -87,7 +87,7 @@ class ProxyManager $options = array(); $formattedProxyUrl = ''; - if ($this->hasProxy && $this->fullProxy[$scheme]) { + if ($this->hasProxy && in_array($scheme, array('http', 'https'), true) && $this->fullProxy[$scheme]) { if ($this->noProxy($requestUrl)) { $formattedProxyUrl = 'excluded by no_proxy'; } else { From 5e956afa2f3a2a0a1bba1a255304fb4d7f7d75dd Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 1 Feb 2021 13:32:03 +0100 Subject: [PATCH 56/74] Try fixing the build --- tests/Composer/Test/InstalledVersionsTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Composer/Test/InstalledVersionsTest.php b/tests/Composer/Test/InstalledVersionsTest.php index c1f4bb122..9070a4d67 100644 --- a/tests/Composer/Test/InstalledVersionsTest.php +++ b/tests/Composer/Test/InstalledVersionsTest.php @@ -17,6 +17,15 @@ use Composer\Semver\VersionParser; class InstalledVersionsTest extends TestCase { + public static function setUpBeforeClass() + { + // disable multiple-ClassLoader-based checks of InstalledVersions by making it seem like no + // class loaders are registered + $prop = new \ReflectionProperty('Composer\Autoload\ClassLoader', 'registeredLoaders'); + $prop->setAccessible(true); + $prop->setValue(array()); + } + public function setUp() { InstalledVersions::reload(require __DIR__.'/Repository/Fixtures/installed.php'); From ea6b0836c4bd5313656ab632557f579a863a01e3 Mon Sep 17 00:00:00 2001 From: Yanick Witschi Date: Mon, 1 Feb 2021 15:17:02 +0100 Subject: [PATCH 57/74] Allow to override Installer::createPlatformRepo --- 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 e9f0bd74e..1a7c5e694 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -708,7 +708,7 @@ class Installer return 0; } - private function createPlatformRepo($forUpdate) + protected function createPlatformRepo($forUpdate) { if ($forUpdate) { $platformOverrides = $this->config->get('platform') ?: array(); From 7402ef33fccd4eef52f820c9065f8cfd3163c95d Mon Sep 17 00:00:00 2001 From: Arnaud Vanwambeke Date: Wed, 3 Feb 2021 23:43:35 -0500 Subject: [PATCH 58/74] Dont recommend to use with all dependencies option when it is already used --- .../SolverProblemsException.php | 2 +- ...ncies-option-dont-recommend-to-use-it.test | 50 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/Composer/Test/Fixtures/installer/conflict-with-all-dependencies-option-dont-recommend-to-use-it.test diff --git a/src/Composer/DependencyResolver/SolverProblemsException.php b/src/Composer/DependencyResolver/SolverProblemsException.php index 85998e5a0..c6844e25c 100644 --- a/src/Composer/DependencyResolver/SolverProblemsException.php +++ b/src/Composer/DependencyResolver/SolverProblemsException.php @@ -63,7 +63,7 @@ class SolverProblemsException extends \RuntimeException $hints[] = $this->createExtensionHint(); } - if ($isCausedByLock && !$isDevExtraction) { + if ($isCausedByLock && !$isDevExtraction && !$request->getUpdateAllowTransitiveRootDependencies()) { $hints[] = "Use the option --with-all-dependencies (-W) to allow upgrades, downgrades and removals for packages currently locked to specific versions."; } diff --git a/tests/Composer/Test/Fixtures/installer/conflict-with-all-dependencies-option-dont-recommend-to-use-it.test b/tests/Composer/Test/Fixtures/installer/conflict-with-all-dependencies-option-dont-recommend-to-use-it.test new file mode 100644 index 000000000..d1003fd9d --- /dev/null +++ b/tests/Composer/Test/Fixtures/installer/conflict-with-all-dependencies-option-dont-recommend-to-use-it.test @@ -0,0 +1,50 @@ +--TEST-- +Verify that a conflict with all dependencies option enabled don't recommend to use the option +--COMPOSER-- +{ + "repositories": [ + { + "type": "package", + "package": [ + { "name": "locked/pkg", "version": "dev-master", "require": {"locked/dependency": "1.0.0"}, "default-branch": true } + ] + } + ], + "require": { + "locked/pkg": "*@dev" + } +} + +--LOCK-- +{ + "packages": [ + { "name": "locked/pkg", "version": "dev-master", "require": {"locked/dependency": "1.0.0"}, "default-branch": true }, + { "name": "locked/dependency", "version": "1.0.0" } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} + +--RUN-- +update locked/dependency --with-all-dependencies + +--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 + - 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. + - locked/pkg is locked to version dev-master and an update of this package was not requested. + +--EXPECT-- + From 2d914524e9b58b552198bf6fae6dfc0144c82d99 Mon Sep 17 00:00:00 2001 From: adlacruzes Date: Fri, 5 Feb 2021 17:26:17 +0100 Subject: [PATCH 59/74] JsonFile: add missing ParsingException throws annotations --- src/Composer/Json/JsonFile.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Composer/Json/JsonFile.php b/src/Composer/Json/JsonFile.php index e794fe63a..f4c595b39 100644 --- a/src/Composer/Json/JsonFile.php +++ b/src/Composer/Json/JsonFile.php @@ -80,6 +80,7 @@ class JsonFile /** * Reads json file. * + * @throws ParsingException * @throws \RuntimeException * @return mixed */ @@ -172,6 +173,7 @@ class JsonFile * @param int $schema a JsonFile::*_SCHEMA constant * @param string|null $schemaFile a path to the schema file * @throws JsonValidationException + * @throws ParsingException * @return bool true on success */ public function validateSchema($schema = self::STRICT_SCHEMA, $schemaFile = null) @@ -289,6 +291,7 @@ class JsonFile * @param string $json json string * @param string $file the json file * + * @throws ParsingException * @return mixed */ public static function parseJson($json, $file = null) From 66ec8b3d92c0ecf15563159b0107fdd1743a65ff Mon Sep 17 00:00:00 2001 From: sink Date: Mon, 8 Feb 2021 16:56:57 +0800 Subject: [PATCH 60/74] docs: fix bad URL --- doc/articles/authentication-for-private-packages.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/articles/authentication-for-private-packages.md b/doc/articles/authentication-for-private-packages.md index 11da2f035..b722df236 100644 --- a/doc/articles/authentication-for-private-packages.md +++ b/doc/articles/authentication-for-private-packages.md @@ -106,7 +106,7 @@ section or directly in the repository definition. The final option to supply Composer with credentials is to use the `COMPOSER_AUTH` environment variable. These variables can be either passed as command line variables or set in actual environment variables. -Read more about the usage of this environment variable [here](../03-cli.md#COMPOSER_AUTH). +Read more about the usage of this environment variable [here](../03-cli.md#composer-auth). # Authentication methods From 78d7792eb810319f86421071bf795ce41eca24f2 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 10:03:44 +0100 Subject: [PATCH 61/74] Fix handling of promises for uninstall step when updating to a different install source --- src/Composer/Installer/InstallationManager.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Composer/Installer/InstallationManager.php b/src/Composer/Installer/InstallationManager.php index f8a0e5708..01148583a 100644 --- a/src/Composer/Installer/InstallationManager.php +++ b/src/Composer/Installer/InstallationManager.php @@ -492,9 +492,15 @@ class InstallationManager $promise = $installer->update($repo, $initial, $target); $this->markForNotification($target); } else { - $this->getInstaller($initialType)->uninstall($repo, $initial); + $promise = $this->getInstaller($initialType)->uninstall($repo, $initial); + if (!$promise instanceof PromiseInterface) { + $promise = \React\Promise\resolve(); + } + $installer = $this->getInstaller($targetType); - $promise = $installer->install($repo, $target); + $promise->then(function () use ($installer, $repo, $target) { + return $installer->install($repo, $target); + }); } return $promise; From 6a869ede77d4d79b7743234b907124185c385763 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 10:47:37 +0100 Subject: [PATCH 62/74] Fail early if git/hg/svn can not be found on the system, so that download can be retried from dist, fixes #9681 --- src/Composer/Downloader/GitDownloader.php | 4 ++++ src/Composer/Downloader/HgDownloader.php | 3 +++ src/Composer/Downloader/SvnDownloader.php | 5 +++++ src/Composer/Util/Git.php | 5 +---- src/Composer/Util/Hg.php | 19 ++++++++++++++++++- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index dd8a7d873..53d2b3ca9 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -71,6 +71,10 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface if ($this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref) && is_dir($cachePath)) { $this->cachedPackages[$package->getId()][$ref] = true; } + } else { + if (null === GitUtil::getVersion($this->process)) { + throw new \RuntimeException('git was not found in your PATH, skipping source download'); + } } } diff --git a/src/Composer/Downloader/HgDownloader.php b/src/Composer/Downloader/HgDownloader.php index 59ced0193..737dd8758 100644 --- a/src/Composer/Downloader/HgDownloader.php +++ b/src/Composer/Downloader/HgDownloader.php @@ -26,6 +26,9 @@ class HgDownloader extends VcsDownloader */ protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) { + if (null === HgUtils::getVersion($this->process)) { + throw new \RuntimeException('hg was not found in your PATH, skipping source download'); + } } /** diff --git a/src/Composer/Downloader/SvnDownloader.php b/src/Composer/Downloader/SvnDownloader.php index 2f253b4bd..80454f855 100644 --- a/src/Composer/Downloader/SvnDownloader.php +++ b/src/Composer/Downloader/SvnDownloader.php @@ -30,6 +30,11 @@ class SvnDownloader extends VcsDownloader */ protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null) { + SvnUtil::cleanEnv(); + $util = new SvnUtil($url, $this->io, $this->config, $this->process); + if (null === $util->binaryVersion()) { + throw new \RuntimeException('svn was not found in your PATH, skipping source download'); + } } /** diff --git a/src/Composer/Util/Git.php b/src/Composer/Util/Git.php index 28d051245..dd95f2062 100644 --- a/src/Composer/Util/Git.php +++ b/src/Composer/Util/Git.php @@ -403,15 +403,12 @@ class Git /** * Retrieves the current git version. * - * @return string|null The git version number. + * @return string|null The git version number, if present. */ public static function getVersion(ProcessExecutor $process) { if (false === self::$version) { self::$version = null; - if (!$process) { - $process = new ProcessExecutor; - } if (0 === $process->execute('git --version', $output) && preg_match('/^git version (\d+(?:\.\d+)+)/m', $output, $matches)) { self::$version = $matches[1]; } diff --git a/src/Composer/Util/Hg.php b/src/Composer/Util/Hg.php index d0b7fe79f..ecd01b144 100644 --- a/src/Composer/Util/Hg.php +++ b/src/Composer/Util/Hg.php @@ -74,10 +74,27 @@ class Hg private function throwException($message, $url) { - if (0 !== $this->process->execute('hg --version', $ignoredOutput)) { + if (null === self::getVersion($this->process)) { throw new \RuntimeException(Url::sanitize('Failed to clone ' . $url . ', hg was not found, check that it is installed and in your PATH env.' . "\n\n" . $this->process->getErrorOutput())); } throw new \RuntimeException(Url::sanitize($message)); } + + /** + * Retrieves the current hg version. + * + * @return string|null The hg version number, if present. + */ + public static function getVersion(ProcessExecutor $process) + { + if (false === self::$version) { + self::$version = null; + if (0 === $this->process->execute('hg --version', $output) && preg_match('/version (\d+(?:\.\d+)+)/m', $output, $matches)) { + self::$version = $matches[1]; + } + } + + return self::$version; + } } From 902174d2a7b7fb6a0c15f3a743767876f276483c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 11:24:07 +0100 Subject: [PATCH 63/74] Improve error reporting when failing to load a package, fixes #9680 --- .../Repository/ComposerRepository.php | 45 ++++++++++++------- .../Repository/ComposerRepositoryTest.php | 2 +- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index cf0d501c3..1f6837400 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -532,6 +532,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito */ private function whatProvides($name, array $acceptableStabilities = null, array $stabilityFlags = null, array $alreadyLoaded = array()) { + $packagesSource = null; if (!$this->hasPartialPackages() || !isset($this->partialPackagesByName[$name])) { // skip platform packages, root package and composer-plugin-api if (PlatformRepository::isPlatformPackage($name) || '__root__' === $name) { @@ -565,15 +566,18 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if ($cacheKey) { if (!$useLastModifiedCheck && $hash && $this->cache->sha256($cacheKey) === $hash) { $packages = json_decode($this->cache->read($cacheKey), true); + $packagesSource = 'cached file ('.$cacheKey.' originating from '.$url.')'; } elseif ($useLastModifiedCheck) { if ($contents = $this->cache->read($cacheKey)) { $contents = json_decode($contents, true); // we already loaded some packages from this file, so assume it is fresh and avoid fetching it again if (isset($alreadyLoaded[$name])) { $packages = $contents; + $packagesSource = 'cached file ('.$cacheKey.' originating from '.$url.')'; } elseif (isset($contents['last-modified'])) { $response = $this->fetchFileIfLastModified($url, $cacheKey, $contents['last-modified']); $packages = true === $response ? $contents : $response; + $packagesSource = true === $response ? 'cached file ('.$cacheKey.' originating from '.$url.')' : 'downloaded file ('.$url.')'; } } } @@ -582,10 +586,12 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if (!$packages) { try { $packages = $this->fetchFile($url, $cacheKey, $hash, $useLastModifiedCheck); + $packagesSource = 'downloaded file ('.$url.')'; } catch (TransportException $e) { // 404s are acceptable for lazy provider repos if ($this->lazyProvidersUrl && in_array($e->getStatusCode(), array(404, 499), true)) { $packages = array('packages' => array()); + $packagesSource = 'not-found file ('.$url.')'; if ($e->getStatusCode() === 499) { $this->io->error('' . $e->getMessage() . ''); } @@ -598,6 +604,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $loadingPartialPackage = false; } else { $packages = array('packages' => array('versions' => $this->partialPackagesByName[$name])); + $packagesSource = 'root file ('.$this->getPackagesJsonUrl().')'; $loadingPartialPackage = true; } @@ -637,7 +644,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } // load acceptable packages in the providers - $loadedPackages = $this->createPackages($versionsToLoad); + $loadedPackages = $this->createPackages($versionsToLoad, $packagesSource); $uids = array_keys($versionsToLoad); foreach ($loadedPackages as $index => $package) { @@ -667,7 +674,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $repoData = $this->loadDataFromServer(); - foreach ($this->createPackages($repoData) as $package) { + foreach ($this->createPackages($repoData, 'root file ('.$this->getPackagesJsonUrl().')') as $package) { $this->addPackage($package); } } @@ -729,8 +736,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } $promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified) - ->then(function ($response) use (&$packages, &$namesFound, $contents, $realName, $constraint, $repo, $acceptableStabilities, $stabilityFlags, $alreadyLoaded) { + ->then(function ($response) use (&$packages, &$namesFound, $url, $cacheKey, $contents, $realName, $constraint, $repo, $acceptableStabilities, $stabilityFlags, $alreadyLoaded) { + $packagesSource = 'downloaded file ('.$url.')'; + if (true === $response) { + $packagesSource = 'cached file ('.$cacheKey.' originating from '.$url.')'; $response = $contents; } @@ -764,7 +774,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } } - $loadedPackages = $repo->createPackages($versionsToLoad); + $loadedPackages = $repo->createPackages($versionsToLoad, $packagesSource); foreach ($loadedPackages as $package) { $package->setRepository($repo); $packages[spl_object_hash($package)] = $package; @@ -812,6 +822,17 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return false; } + private function getPackagesJsonUrl() + { + $jsonUrlParts = parse_url($this->url); + + if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '.json')) { + return $this->url; + } + + return $this->url . '/packages.json'; + } + protected function loadRootServerFile() { if (null !== $this->rootData) { @@ -822,15 +843,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito throw new \RuntimeException('You must enable the openssl extension in your php.ini to load information from '.$this->url); } - $jsonUrlParts = parse_url($this->url); - - if (isset($jsonUrlParts['path']) && false !== strpos($jsonUrlParts['path'], '.json')) { - $jsonUrl = $this->url; - } else { - $jsonUrl = $this->url . '/packages.json'; - } - - $data = $this->fetchFile($jsonUrl, 'packages.json'); + $data = $this->fetchFile($this->getPackagesJsonUrl(), 'packages.json'); if (!empty($data['notify-batch'])) { $this->notifyUrl = $this->canonicalizeUrl($data['notify-batch']); @@ -1027,7 +1040,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito * * @private */ - public function createPackages(array $packages, $class = 'Composer\Package\CompletePackage') + public function createPackages(array $packages, $source = null) { if (!$packages) { return array(); @@ -1040,7 +1053,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } } - $packages = $this->loader->loadPackages($packages, $class); + $packages = $this->loader->loadPackages($packages, 'Composer\Package\CompletePackage'); foreach ($packages as $package) { if (isset($this->sourceMirrors[$package->getSourceType()])) { @@ -1052,7 +1065,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return $packages; } catch (\Exception $e) { - throw new \RuntimeException('Could not load packages '.(isset($packages[0]['name']) ? $packages[0]['name'] : json_encode($packages)).' in '.$this->url.': ['.get_class($e).'] '.$e->getMessage(), 0, $e); + throw new \RuntimeException('Could not load packages '.(isset($packages[0]['name']) ? $packages[0]['name'] : json_encode($packages)).' in '.$this->getRepoName().($source ? ' from '.$source : '').': ['.get_class($e).'] '.$e->getMessage(), 0, $e); } } diff --git a/tests/Composer/Test/Repository/ComposerRepositoryTest.php b/tests/Composer/Test/Repository/ComposerRepositoryTest.php index b000088a2..d7e2ab1f7 100644 --- a/tests/Composer/Test/Repository/ComposerRepositoryTest.php +++ b/tests/Composer/Test/Repository/ComposerRepositoryTest.php @@ -55,7 +55,7 @@ class ComposerRepositoryTest extends TestCase $repository ->expects($this->at(2)) ->method('createPackages') - ->with($this->identicalTo($expected), $this->equalTo('Composer\Package\CompletePackage')) + ->with($this->identicalTo($expected), $this->equalTo('root file (http://example.org/packages.json)')) ->will($this->returnValue($stubs)); // Triggers initialization From 8d94e25c2f5e2f8d54cb409b6facedf13dae2394 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 11:27:21 +0100 Subject: [PATCH 64/74] Minor fixes --- src/Composer/Downloader/GitDownloader.php | 6 ++---- src/Composer/Util/Hg.php | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Composer/Downloader/GitDownloader.php b/src/Composer/Downloader/GitDownloader.php index 53d2b3ca9..b5bd73db6 100644 --- a/src/Composer/Downloader/GitDownloader.php +++ b/src/Composer/Downloader/GitDownloader.php @@ -71,10 +71,8 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface if ($this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref) && is_dir($cachePath)) { $this->cachedPackages[$package->getId()][$ref] = true; } - } else { - if (null === GitUtil::getVersion($this->process)) { - throw new \RuntimeException('git was not found in your PATH, skipping source download'); - } + } elseif (null === $gitVersion) { + throw new \RuntimeException('git was not found in your PATH, skipping source download'); } } diff --git a/src/Composer/Util/Hg.php b/src/Composer/Util/Hg.php index ecd01b144..a0ebac01d 100644 --- a/src/Composer/Util/Hg.php +++ b/src/Composer/Util/Hg.php @@ -20,6 +20,8 @@ use Composer\IO\IOInterface; */ class Hg { + private static $version = false; + /** * @var \Composer\IO\IOInterface */ @@ -90,7 +92,7 @@ class Hg { if (false === self::$version) { self::$version = null; - if (0 === $this->process->execute('hg --version', $output) && preg_match('/version (\d+(?:\.\d+)+)/m', $output, $matches)) { + if (0 === $process->execute('hg --version', $output) && preg_match('/version (\d+(?:\.\d+)+)/m', $output, $matches)) { self::$version = $matches[1]; } } From a6d92e1eee38ad78181ef6548942126461818475 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 12:46:57 +0100 Subject: [PATCH 65/74] Sanitize URLs --- src/Composer/Repository/ComposerRepository.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 1f6837400..441e90c20 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -566,18 +566,18 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if ($cacheKey) { if (!$useLastModifiedCheck && $hash && $this->cache->sha256($cacheKey) === $hash) { $packages = json_decode($this->cache->read($cacheKey), true); - $packagesSource = 'cached file ('.$cacheKey.' originating from '.$url.')'; + $packagesSource = 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')'; } elseif ($useLastModifiedCheck) { if ($contents = $this->cache->read($cacheKey)) { $contents = json_decode($contents, true); // we already loaded some packages from this file, so assume it is fresh and avoid fetching it again if (isset($alreadyLoaded[$name])) { $packages = $contents; - $packagesSource = 'cached file ('.$cacheKey.' originating from '.$url.')'; + $packagesSource = 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')'; } elseif (isset($contents['last-modified'])) { $response = $this->fetchFileIfLastModified($url, $cacheKey, $contents['last-modified']); $packages = true === $response ? $contents : $response; - $packagesSource = true === $response ? 'cached file ('.$cacheKey.' originating from '.$url.')' : 'downloaded file ('.$url.')'; + $packagesSource = true === $response ? 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')' : 'downloaded file ('.Url::sanitize($url).')'; } } } @@ -586,12 +586,12 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if (!$packages) { try { $packages = $this->fetchFile($url, $cacheKey, $hash, $useLastModifiedCheck); - $packagesSource = 'downloaded file ('.$url.')'; + $packagesSource = 'downloaded file ('.Url::sanitize($url).')'; } catch (TransportException $e) { // 404s are acceptable for lazy provider repos if ($this->lazyProvidersUrl && in_array($e->getStatusCode(), array(404, 499), true)) { $packages = array('packages' => array()); - $packagesSource = 'not-found file ('.$url.')'; + $packagesSource = 'not-found file ('.Url::sanitize($url).')'; if ($e->getStatusCode() === 499) { $this->io->error('' . $e->getMessage() . ''); } @@ -604,7 +604,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $loadingPartialPackage = false; } else { $packages = array('packages' => array('versions' => $this->partialPackagesByName[$name])); - $packagesSource = 'root file ('.$this->getPackagesJsonUrl().')'; + $packagesSource = 'root file ('.Url::sanitize($this->getPackagesJsonUrl()).')'; $loadingPartialPackage = true; } @@ -674,7 +674,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $repoData = $this->loadDataFromServer(); - foreach ($this->createPackages($repoData, 'root file ('.$this->getPackagesJsonUrl().')') as $package) { + foreach ($this->createPackages($repoData, 'root file ('.Url::sanitize($this->getPackagesJsonUrl()).')') as $package) { $this->addPackage($package); } } @@ -737,10 +737,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified) ->then(function ($response) use (&$packages, &$namesFound, $url, $cacheKey, $contents, $realName, $constraint, $repo, $acceptableStabilities, $stabilityFlags, $alreadyLoaded) { - $packagesSource = 'downloaded file ('.$url.')'; + $packagesSource = 'downloaded file ('.Url::sanitize($url).')'; if (true === $response) { - $packagesSource = 'cached file ('.$cacheKey.' originating from '.$url.')'; + $packagesSource = 'cached file ('.$cacheKey.' originating from '.Url::sanitize($url).')'; $response = $contents; } From 40095b20dc58772b7b630e1ca41f024fda7eae44 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 14:34:59 +0100 Subject: [PATCH 66/74] Allow tweaking the max parallel http requests via env var, fixes #9671 --- doc/03-cli.md | 7 +++++++ src/Composer/Util/HttpDownloader.php | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/doc/03-cli.md b/doc/03-cli.md index 7aba323a7..99d4272b8 100644 --- a/doc/03-cli.md +++ b/doc/03-cli.md @@ -1014,6 +1014,13 @@ similar use case), and need to support proxies, please provide the `CGI_HTTP_PRO environment variable instead. See [httpoxy.org](https://httpoxy.org/) for further details. +### COMPOSER_MAX_PARALLEL_HTTP + +Set to an integer to configure how many files can be downloaded in parallel. This +defaults to 12 and must be between 1 and 50. If your proxy has issues with +concurrency maybe you want to lower this. Increasing it should generally not result +in performance gains. + ### HTTP_PROXY_REQUEST_FULLURI If you use a proxy, but it does not support the request_fulluri flag, then you diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index 519e17c54..757e5f3c6 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -37,7 +37,7 @@ class HttpDownloader private $jobs = array(); private $options = array(); private $runningJobs = 0; - private $maxJobs = 10; + private $maxJobs = 12; private $curl; private $rfs; private $idGen = 0; @@ -71,6 +71,10 @@ class HttpDownloader } $this->rfs = new RemoteFilesystem($io, $config, $options, $disableTls); + + if (is_numeric($maxJobs = getenv('COMPOSER_MAX_PARALLEL_HTTP'))) { + $this->maxJobs = max(1, min(50, (int) $maxJobs)); + } } /** From b724adde107f28357ab72da6ce3d875e151c257f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 16:01:45 +0100 Subject: [PATCH 67/74] Link source from docs --- doc/07-runtime.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/07-runtime.md b/doc/07-runtime.md index 7cbbd63a2..ab90a57b3 100644 --- a/doc/07-runtime.md +++ b/doc/07-runtime.md @@ -87,7 +87,7 @@ possible for safety. ---- A few other methods are available for more complex usages, please refer to the -source/docblocks of the class itself. +source/docblocks of [the class itself](https://github.com/composer/composer/blob/master/src/Composer/InstalledVersions.php). ## Platform check From f37f3dab04cbcfc8d5936d22cfc5de20b600f82e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 10 Feb 2021 17:34:15 +0100 Subject: [PATCH 68/74] Fix abort of downloads and zip extraction to happen immediately, fixes #9390 --- src/Composer/Downloader/FileDownloader.php | 5 +++++ src/Composer/Util/HttpDownloader.php | 8 +++++--- src/Composer/Util/ProcessExecutor.php | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 3ca7eb9e4..a99ea79a0 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -16,6 +16,7 @@ use Composer\Config; use Composer\Cache; use Composer\IO\IOInterface; use Composer\IO\NullIO; +use Composer\Exception\IrrecoverableDownloadException; use Composer\Package\Comparer\Comparer; use Composer\DependencyResolver\Operation\UpdateOperation; use Composer\DependencyResolver\Operation\InstallOperation; @@ -219,6 +220,10 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface } $self->clearLastCacheWrite($package); + if ($e instanceof IrrecoverableDownloadException) { + throw $e; + } + if ($e instanceof TransportException) { // if we got an http response with a proper code, then requesting again will probably not help, abort if ((0 !== $e->getCode() && !in_array($e->getCode(), array(500, 502, 503, 504))) || !$retries) { diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index 757e5f3c6..ba7a49798 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -19,6 +19,7 @@ use Composer\Util\Http\Response; use Composer\Composer; use Composer\Package\Version\VersionParser; use Composer\Semver\Constraint\Constraint; +use Composer\Exception\IrrecoverableDownloadException; use React\Promise\Promise; /** @@ -257,10 +258,11 @@ class HttpDownloader if (isset($job['curl_id'])) { $curl->abortRequest($job['curl_id']); } + throw new IrrecoverableDownloadException('Download of ' . Url::sanitize($job['request']['url']) . ' canceled'); }; $promise = new Promise($resolver, $canceler); - $promise->then(function ($response) use (&$job, $downloader) { + $promise = $promise->then(function ($response) use (&$job, $downloader) { $job['status'] = HttpDownloader::STATUS_COMPLETED; $job['response'] = $response; @@ -306,7 +308,7 @@ class HttpDownloader if (isset($job['request']['options']['http']['header']) && false !== stripos(implode('', $job['request']['options']['http']['header']), 'if-modified-since')) { $resolve(new Response(array('url' => $url), 304, array(), '')); } else { - $e = new TransportException('Network disabled, request canceled: '.$url, 499); + $e = new TransportException('Network disabled, request canceled: '.Url::sanitize($url), 499); $e->setStatusCode(499); $reject($e); } @@ -429,7 +431,7 @@ class HttpDownloader } } - $io->writeError('<'.$type.'>'.ucfirst($type).' from '.$url.': '.$data[$type].''); + $io->writeError('<'.$type.'>'.ucfirst($type).' from '.Url::sanitize($url).': '.$data[$type].''); } } diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index f940f49a9..f2f286916 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -177,6 +177,8 @@ class ProcessExecutor // signal can throw in various conditions, but we don't care if it fails } $job['process']->stop(1); + + throw new \RuntimeException('Aborted process'); }; $promise = new Promise($resolver, $canceler); From 4130d388fe6d5dcf79971a18f4216a099855babe Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Feb 2021 10:41:17 +0100 Subject: [PATCH 69/74] Auto-detect packagist.org default repo replacements and deactivate it if it is redefined --- src/Composer/Config.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 436de79f2..7d904d8bd 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -78,8 +78,7 @@ class Config public static $defaultRepositories = array( 'packagist.org' => array( 'type' => 'composer', - 'url' => 'https?://repo.packagist.org', - 'allow_ssl_downgrade' => true, + 'url' => 'https://repo.packagist.org', ), ); @@ -182,6 +181,10 @@ class Config // store repo if (is_int($name)) { + // auto-deactivate the default packagist.org repo if it gets redefined + if (isset($repository['type'], $repository['url']) && $repository['type'] === 'composer' && preg_match('{^https?://(?:[a-z0-9-.]+\.)?packagist.org(/|$)}', $repository['url'])) { + $this->disableRepoByName('packagist.org'); + } $this->repositories[] = $repository; } else { if ($name === 'packagist') { // BC support for default "packagist" named repo From 52d7c6c383e8b9ed9c8d12f1c52f15e9e92c3c68 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Feb 2021 11:13:58 +0100 Subject: [PATCH 70/74] Fix/add tests and also handle case where a json object is used --- src/Composer/Config.php | 9 +++++---- tests/Composer/Test/ConfigTest.php | 26 ++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 7d904d8bd..e62886122 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -179,12 +179,13 @@ class Config continue; } + // auto-deactivate the default packagist.org repo if it gets redefined + if (isset($repository['type'], $repository['url']) && $repository['type'] === 'composer' && preg_match('{^https?://(?:[a-z0-9-.]+\.)?packagist.org(/|$)}', $repository['url'])) { + $this->disableRepoByName('packagist.org'); + } + // store repo if (is_int($name)) { - // auto-deactivate the default packagist.org repo if it gets redefined - if (isset($repository['type'], $repository['url']) && $repository['type'] === 'composer' && preg_match('{^https?://(?:[a-z0-9-.]+\.)?packagist.org(/|$)}', $repository['url'])) { - $this->disableRepoByName('packagist.org'); - } $this->repositories[] = $repository; } else { if ($name === 'packagist') { // BC support for default "packagist" named repo diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index 4a4bf011b..91d4dde07 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -35,7 +35,7 @@ class ConfigTest extends TestCase $data = array(); $data['local config inherits system defaults'] = array( array( - 'packagist.org' => array('type' => 'composer', 'url' => 'https?://repo.packagist.org', 'allow_ssl_downgrade' => true), + 'packagist.org' => array('type' => 'composer', 'url' => 'https://repo.packagist.org'), ), array(), ); @@ -58,7 +58,7 @@ class ConfigTest extends TestCase array( 1 => array('type' => 'vcs', 'url' => 'git://github.com/composer/composer.git'), 0 => array('type' => 'pear', 'url' => 'http://pear.composer.org'), - 'packagist.org' => array('type' => 'composer', 'url' => 'https?://repo.packagist.org', 'allow_ssl_downgrade' => true), + 'packagist.org' => array('type' => 'composer', 'url' => 'https://repo.packagist.org'), ), array( array('type' => 'vcs', 'url' => 'git://github.com/composer/composer.git'), @@ -69,7 +69,7 @@ class ConfigTest extends TestCase $data['system config adds above core defaults'] = array( array( 'example.com' => array('type' => 'composer', 'url' => 'http://example.com'), - 'packagist.org' => array('type' => 'composer', 'url' => 'https?://repo.packagist.org', 'allow_ssl_downgrade' => true), + 'packagist.org' => array('type' => 'composer', 'url' => 'https://repo.packagist.org'), ), array(), array( @@ -104,9 +104,27 @@ class ConfigTest extends TestCase ), ); + $data['local config redefining packagist.org by URL override it if no named keys are used'] = array( + array( + array('type' => 'composer', 'url' => 'https://repo.packagist.org'), + ), + array( + array('type' => 'composer', 'url' => 'https://repo.packagist.org'), + ), + ); + + $data['local config redefining packagist.org by URL override it also with named keys'] = array( + array( + 'example' => array('type' => 'composer', 'url' => 'https://repo.packagist.org'), + ), + array( + 'example' => array('type' => 'composer', 'url' => 'https://repo.packagist.org'), + ), + ); + $data['incorrect local config does not cause ErrorException'] = array( array( - 'packagist.org' => array('type' => 'composer', 'url' => 'https?://repo.packagist.org', 'allow_ssl_downgrade' => true), + 'packagist.org' => array('type' => 'composer', 'url' => 'https://repo.packagist.org'), 'type' => 'vcs', 'url' => 'http://example.com', ), From abcf9e993bb2787a1ce59154a406341eb33d379f Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Feb 2021 12:43:43 +0100 Subject: [PATCH 71/74] Fix processes silently ignoring the CWD when it does not exist, refs #9694 --- src/Composer/Repository/Vcs/GitDriver.php | 3 +++ src/Composer/Util/ProcessExecutor.php | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/Composer/Repository/Vcs/GitDriver.php b/src/Composer/Repository/Vcs/GitDriver.php index 8c6902298..2c7782c98 100644 --- a/src/Composer/Repository/Vcs/GitDriver.php +++ b/src/Composer/Repository/Vcs/GitDriver.php @@ -38,6 +38,9 @@ class GitDriver extends VcsDriver { if (Filesystem::isLocalPath($this->url)) { $this->url = preg_replace('{[\\/]\.git/?$}', '', $this->url); + if (!is_dir($this->url)) { + throw new \RuntimeException('Failed to read package information from '.$this->url.' as the path does not exist'); + } $this->repoDir = $this->url; $cacheUrl = realpath($this->url); } else { diff --git a/src/Composer/Util/ProcessExecutor.php b/src/Composer/Util/ProcessExecutor.php index 83f19cf2d..cb935e0eb 100644 --- a/src/Composer/Util/ProcessExecutor.php +++ b/src/Composer/Util/ProcessExecutor.php @@ -61,6 +61,10 @@ class ProcessExecutor $cwd = realpath(getcwd()); } + if (null !== $cwd && !is_dir($cwd)) { + throw new \RuntimeException('The given CWD for the process does not exist: '.$cwd); + } + $this->captureOutput = func_num_args() > 1; $this->errorOutput = null; From 4c8149a33d9da7dd2bc3dbf90ebed2a2a8f68a8b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Feb 2021 14:55:03 +0100 Subject: [PATCH 72/74] Only load dev requirements for the root package when collecting autoload dependencies during plugin activation, fixes #9683 --- src/Composer/Plugin/PluginManager.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index d89db97f7..058c03ecd 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -23,6 +23,7 @@ use Composer\Repository\RepositoryInterface; use Composer\Repository\InstalledRepository; use Composer\Repository\RootPackageRepository; use Composer\Package\PackageInterface; +use Composer\Package\RootPackageInterface; use Composer\Package\Link; use Composer\Semver\Constraint\Constraint; use Composer\Plugin\Capability\Capability; @@ -183,7 +184,7 @@ class PluginManager } $autoloadPackages = array($package->getName() => $package); - $autoloadPackages = $this->collectDependencies($installedRepo, $autoloadPackages, $package); + $autoloadPackages = $this->collectDependencies($installedRepo, $autoloadPackages, $package, $rootPackage); $generator = $this->composer->getAutoloadGenerator(); $autoloads = array(array($rootPackage, '')); @@ -408,18 +409,18 @@ class PluginManager * * @return array Map of package names to packages */ - private function collectDependencies(InstalledRepository $installedRepo, array $collected, PackageInterface $package) + private function collectDependencies(InstalledRepository $installedRepo, array $collected, PackageInterface $package, RootPackageInterface $rootPackage) { - $requires = array_merge( - $package->getRequires(), - $package->getDevRequires() - ); + $requires = $package->getRequires(); + if ($rootPackage->getName() === $package->getName()) { + $requires = array_merge($requires, $package->getDevRequires()); + } foreach ($requires as $requireLink) { foreach ($installedRepo->findPackagesWithReplacersAndProviders($requireLink->getTarget()) as $requiredPackage) { if (!isset($collected[$requiredPackage->getName()])) { $collected[$requiredPackage->getName()] = $requiredPackage; - $collected = $this->collectDependencies($installedRepo, $collected, $requiredPackage); + $collected = $this->collectDependencies($installedRepo, $collected, $requiredPackage, $rootPackage); } } } From 1c715b5821dd3b58c022be381a09aaaf8905b0ae Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Thu, 11 Feb 2021 16:05:37 +0100 Subject: [PATCH 73/74] Never load dev requirements, refs #9683 --- src/Composer/Plugin/PluginManager.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Composer/Plugin/PluginManager.php b/src/Composer/Plugin/PluginManager.php index 058c03ecd..4c5d4d9bf 100644 --- a/src/Composer/Plugin/PluginManager.php +++ b/src/Composer/Plugin/PluginManager.php @@ -184,7 +184,7 @@ class PluginManager } $autoloadPackages = array($package->getName() => $package); - $autoloadPackages = $this->collectDependencies($installedRepo, $autoloadPackages, $package, $rootPackage); + $autoloadPackages = $this->collectDependencies($installedRepo, $autoloadPackages, $package); $generator = $this->composer->getAutoloadGenerator(); $autoloads = array(array($rootPackage, '')); @@ -409,18 +409,13 @@ class PluginManager * * @return array Map of package names to packages */ - private function collectDependencies(InstalledRepository $installedRepo, array $collected, PackageInterface $package, RootPackageInterface $rootPackage) + private function collectDependencies(InstalledRepository $installedRepo, array $collected, PackageInterface $package) { - $requires = $package->getRequires(); - if ($rootPackage->getName() === $package->getName()) { - $requires = array_merge($requires, $package->getDevRequires()); - } - - foreach ($requires as $requireLink) { + foreach ($package->getRequires() as $requireLink) { foreach ($installedRepo->findPackagesWithReplacersAndProviders($requireLink->getTarget()) as $requiredPackage) { if (!isset($collected[$requiredPackage->getName()])) { $collected[$requiredPackage->getName()] = $requiredPackage; - $collected = $this->collectDependencies($installedRepo, $collected, $requiredPackage, $rootPackage); + $collected = $this->collectDependencies($installedRepo, $collected, $requiredPackage); } } } From d3ba447b79e45d1130e5523224700b97541b16de Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Fri, 12 Feb 2021 11:05:13 +0100 Subject: [PATCH 74/74] Full functional test-bed improvements --- tests/Composer/Test/AllFunctionalTest.php | 102 +++++++++++------- .../functional/create-project-command.test | 6 +- ...ject-shows-full-hash-for-dev-packages.test | 6 +- .../packages.json | 7 +- tests/Composer/Test/InstalledVersionsTest.php | 5 + 5 files changed, 79 insertions(+), 47 deletions(-) rename tests/Composer/Test/Fixtures/functional/{ => create-project-shows-full-hash-for-dev-packages}/packages.json (90%) diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index a38eda193..cc950b053 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -85,7 +85,7 @@ class AllFunctionalTest extends TestCase } } - $proc = new Process('php '.escapeshellarg('./bin/compile'), $target); + $proc = new Process('php -dphar.readonly=0 '.escapeshellarg('./bin/compile'), $target); $exitcode = $proc->run(); if ($exitcode !== 0 || trim($proc->getOutput())) { @@ -102,26 +102,73 @@ class AllFunctionalTest extends TestCase public function testIntegration($testFile) { $testData = $this->parseTestFile($testFile); + $this->testDir = self::getUniqueTmpDirectory(); + + // if a dir is present with the name of the .test file (without .test), we + // copy all its contents in the $testDir to be used to run the test with + $testFileSetupDir = substr($testFile, 0, -5); + if (is_dir($testFileSetupDir)) { + $fs = new Filesystem(); + $fs->copy($testFileSetupDir, $this->testDir); + } $this->oldenv = getenv('COMPOSER_HOME'); $_SERVER['COMPOSER_HOME'] = $this->testDir.'home'; putenv('COMPOSER_HOME='.$_SERVER['COMPOSER_HOME']); $cmd = 'php '.escapeshellarg(self::$pharPath).' --no-ansi '.$testData['RUN']; - $proc = new Process($cmd, __DIR__.'/Fixtures/functional', null, null, 300); - $exitcode = $proc->run(); + $proc = new Process($cmd, $this->testDir, null, null, 300); + $output = ''; + + $exitcode = $proc->run(function ($type, $buffer) use (&$output) { + $output .= $buffer; + }); if (isset($testData['EXPECT'])) { - $this->assertEquals($testData['EXPECT'], $this->cleanOutput($proc->getOutput()), 'Error Output: '.$proc->getErrorOutput()); + $output = trim($this->cleanOutput($output)); + $expected = $testData['EXPECT']; + + $line = 1; + for ($i = 0, $j = 0; $i < strlen($expected); ) { + if ($expected[$i] === "\n") { + $line++; + } + if ($expected[$i] === '%') { + preg_match('{%(.+?)%}', substr($expected, $i), $match); + $regex = $match[1]; + + if (preg_match('{'.$regex.'}', substr($output, $j), $match)) { + $i += strlen($regex) + 2; + $j += strlen($match[0]); + continue; + } else { + $this->fail( + 'Failed to match pattern '.$regex.' at line '.$line.' / abs offset '.$i.': ' + .substr($output, $j, min(strpos($output, "\n", $j)-$j, 100)).PHP_EOL.PHP_EOL. + 'Output:'.PHP_EOL.$output + ); + } + } + if ($expected[$i] !== $output[$j]) { + $this->fail( + 'Output does not match expectation at line '.$line.' / abs offset '.$i.': '.PHP_EOL + .'-'.substr($expected, $i, min(strpos($expected, "\n", $i)-$i, 100)).PHP_EOL + .'+'.substr($output, $j, min(strpos($output, "\n", $j)-$j, 100)).PHP_EOL.PHP_EOL + .'Output:'.PHP_EOL.$output + ); + } + $i++; + $j++; + } } if (isset($testData['EXPECT-REGEX'])) { - $this->assertRegExp($testData['EXPECT-REGEX'], $this->cleanOutput($proc->getOutput()), 'Error Output: '.$proc->getErrorOutput()); + $this->assertRegExp($testData['EXPECT-REGEX'], $this->cleanOutput($output)); } - if (isset($testData['EXPECT-ERROR'])) { - $this->assertEquals($testData['EXPECT-ERROR'], $this->cleanOutput($proc->getErrorOutput())); - } - if (isset($testData['EXPECT-ERROR-REGEX'])) { - $this->assertRegExp($testData['EXPECT-ERROR-REGEX'], $this->cleanOutput($proc->getErrorOutput())); + if (isset($testData['EXPECT-REGEXES'])) { + $cleanOutput = $this->cleanOutput($output); + foreach (explode("\n", $testData['EXPECT-REGEXES']) as $regex) { + $this->assertRegExp($regex, $cleanOutput, 'Output: '.$output); + } } if (isset($testData['EXPECT-EXIT-CODE'])) { $this->assertSame($testData['EXPECT-EXIT-CODE'], $exitcode); @@ -132,7 +179,7 @@ class AllFunctionalTest extends TestCase { $tests = array(); foreach (Finder::create()->in(__DIR__.'/Fixtures/functional')->name('*.test')->files() as $file) { - $tests[] = array($file->getRealPath()); + $tests[basename($file)] = array($file->getRealPath()); } return $tests; @@ -144,23 +191,6 @@ class AllFunctionalTest extends TestCase $data = array(); $section = null; - $testDir = self::getUniqueTmpDirectory(); - $this->testDir = $testDir; - $varRegex = '#%([a-zA-Z_-]+)%#'; - $variableReplacer = function ($match) use (&$data, $testDir) { - list(, $var) = $match; - - switch ($var) { - case 'testDir': - $data['test_dir'] = $testDir; - - return $testDir; - - default: - throw new \InvalidArgumentException(sprintf('Unknown variable "%s". Supported variables: "testDir"', $var)); - } - }; - foreach ($tokens as $token) { if ('' === $token && null === $section) { continue; @@ -176,24 +206,20 @@ class AllFunctionalTest extends TestCase // Allow sections to validate, or modify their section data. switch ($section) { - case 'RUN': - $sectionData = preg_replace_callback($varRegex, $variableReplacer, $sectionData); - break; - case 'EXPECT-EXIT-CODE': $sectionData = (int) $sectionData; break; + case 'RUN': case 'EXPECT': case 'EXPECT-REGEX': - case 'EXPECT-ERROR': - case 'EXPECT-ERROR-REGEX': - $sectionData = preg_replace_callback($varRegex, $variableReplacer, $sectionData); + case 'EXPECT-REGEXES': + $sectionData = trim($sectionData); break; default: throw new \RuntimeException(sprintf( - 'Unknown section "%s". Allowed sections: "RUN", "EXPECT", "EXPECT-ERROR", "EXPECT-EXIT-CODE", "EXPECT-REGEX", "EXPECT-ERROR-REGEX". ' + 'Unknown section "%s". Allowed sections: "RUN", "EXPECT", "EXPECT-EXIT-CODE", "EXPECT-REGEX", "EXPECT-REGEXES". ' .'Section headers must be written as "--HEADER_NAME--".', $section )); @@ -207,8 +233,8 @@ class AllFunctionalTest extends TestCase if (!isset($data['RUN'])) { throw new \RuntimeException('The test file must have a section named "RUN".'); } - if (!isset($data['EXPECT']) && !isset($data['EXPECT-ERROR']) && !isset($data['EXPECT-REGEX']) && !isset($data['EXPECT-ERROR-REGEX'])) { - throw new \RuntimeException('The test file must have a section named "EXPECT", "EXPECT-ERROR", "EXPECT-REGEX", or "EXPECT-ERROR-REGEX".'); + if (!isset($data['EXPECT']) && !isset($data['EXPECT-REGEX']) && !isset($data['EXPECT-REGEXES'])) { + throw new \RuntimeException('The test file must have a section named "EXPECT", "EXPECT-REGEX", or "EXPECT-REGEXES".'); } return $data; diff --git a/tests/Composer/Test/Fixtures/functional/create-project-command.test b/tests/Composer/Test/Fixtures/functional/create-project-command.test index 1c4813446..31ce946c3 100644 --- a/tests/Composer/Test/Fixtures/functional/create-project-command.test +++ b/tests/Composer/Test/Fixtures/functional/create-project-command.test @@ -1,4 +1,4 @@ --RUN-- -create-project seld/jsonlint %testDir% 1.0.0 --prefer-source -n ---EXPECT-ERROR-REGEX-- -{Installing seld/jsonlint \(1.0.0\): Cloning [a-f0-9]{10}( from cache)?} +create-project seld/jsonlint foo 1.0.0 --prefer-source -n +--EXPECT-REGEX-- +{- Installing seld/jsonlint \(1.0.0\): Cloning [a-f0-9]{10}( from cache)?} diff --git a/tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages.test b/tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages.test index 357f51619..993f96e1a 100644 --- a/tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages.test +++ b/tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages.test @@ -1,4 +1,4 @@ --RUN-- -create-project --repository=packages.json -v seld/jsonlint %testDir% dev-master ---EXPECT-ERROR-REGEX-- -{^Installing seld/jsonlint \(dev-master [a-f0-9]{40}\)}m +create-project --repository=packages.json -v seld/jsonlint foo dev-main +--EXPECT-REGEX-- +{^Installing seld/jsonlint \(dev-main [a-f0-9]{40}\)}m diff --git a/tests/Composer/Test/Fixtures/functional/packages.json b/tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages/packages.json similarity index 90% rename from tests/Composer/Test/Fixtures/functional/packages.json rename to tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages/packages.json index 11dfe2d8e..1069f6581 100644 --- a/tests/Composer/Test/Fixtures/functional/packages.json +++ b/tests/Composer/Test/Fixtures/functional/create-project-shows-full-hash-for-dev-packages/packages.json @@ -9,8 +9,9 @@ "validator" ], "homepage": "", - "version": "dev-master", - "version_normalized": "9999999-dev", + "version": "dev-main", + "version_normalized": "dev-main", + "default-branch": true, "license": [ "MIT" ], @@ -41,4 +42,4 @@ "php": ">=5.3.0" } } -] \ No newline at end of file +] diff --git a/tests/Composer/Test/InstalledVersionsTest.php b/tests/Composer/Test/InstalledVersionsTest.php index 9070a4d67..2d8799727 100644 --- a/tests/Composer/Test/InstalledVersionsTest.php +++ b/tests/Composer/Test/InstalledVersionsTest.php @@ -26,6 +26,11 @@ class InstalledVersionsTest extends TestCase $prop->setValue(array()); } + public static function tearDownAfterClass() + { + self::setUpBeforeClass(); + } + public function setUp() { InstalledVersions::reload(require __DIR__.'/Repository/Fixtures/installed.php');