From 86008940036738138e5770a59abfc9db7fee4884 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 11 Oct 2022 16:51:47 +0200 Subject: [PATCH] Update deps, constrain a few types to non-empty-strings --- composer.lock | 74 +++++++++---------- src/Composer/Command/DiagnoseCommand.php | 2 +- src/Composer/Downloader/FileDownloader.php | 7 +- src/Composer/Package/Package.php | 18 ++--- src/Composer/Package/PackageInterface.php | 12 +-- src/Composer/Plugin/PreFileDownloadEvent.php | 11 ++- .../Repository/ComposerRepository.php | 47 ++++++++---- src/Composer/Util/ComposerMirror.php | 17 ++++- src/Composer/Util/Http/CurlDownloader.php | 64 ++++++++-------- src/Composer/Util/Http/ProxyManager.php | 2 + src/Composer/Util/Http/Response.php | 4 +- src/Composer/Util/HttpDownloader.php | 18 ++++- src/Composer/Util/RemoteFilesystem.php | 8 +- src/Composer/Util/StreamContextFactory.php | 3 +- src/Composer/Util/Url.php | 11 ++- .../installer/install-aliased-alias.test | 4 +- .../Fixtures/installer/install-reference.test | 2 +- .../update-dev-ignores-providers.test | 6 +- ...pdate-dev-to-new-ref-picks-up-changes.test | 6 +- .../update-downgrades-unstable-packages.test | 16 ++-- .../update-installed-alias-dry-run.test | 8 +- .../installer/update-installed-alias.test | 8 +- .../update-installed-reference-dry-run.test | 6 +- .../installer/update-installed-reference.test | 6 +- .../update-reference-picks-latest.test | 6 +- .../Fixtures/installer/update-reference.test | 4 +- ...dating-dev-from-lock-removes-old-deps.test | 6 +- .../Composer/Test/Mock/HttpDownloaderMock.php | 25 +++---- .../Test/Mock/ProcessExecutorMock.php | 16 +--- .../Repository/ComposerRepositoryTest.php | 2 + .../Test/Repository/Vcs/GitLabDriverTest.php | 6 ++ .../Test/Util/Http/ProxyManagerTest.php | 1 + .../Test/Util/RemoteFilesystemTest.php | 2 + tests/Composer/Test/Util/UrlTest.php | 1 + 34 files changed, 246 insertions(+), 183 deletions(-) diff --git a/composer.lock b/composer.lock index c1a7b05ae..42093a779 100644 --- a/composer.lock +++ b/composer.lock @@ -941,16 +941,16 @@ }, { "name": "symfony/console", - "version": "v5.4.12", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1" + "reference": "3f97f6c7b7e26848a90c0c0cfb91eeb2bb8618be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c072aa8f724c3af64e2c7a96b796a4863d24dba1", - "reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1", + "url": "https://api.github.com/repos/symfony/console/zipball/3f97f6c7b7e26848a90c0c0cfb91eeb2bb8618be", + "reference": "3f97f6c7b7e26848a90c0c0cfb91eeb2bb8618be", "shasum": "" }, "require": { @@ -1020,7 +1020,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.12" + "source": "https://github.com/symfony/console/tree/v5.4.13" }, "funding": [ { @@ -1036,7 +1036,7 @@ "type": "tidelift" } ], - "time": "2022-08-17T13:18:05+00:00" + "time": "2022-08-26T13:50:20+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1107,16 +1107,16 @@ }, { "name": "symfony/filesystem", - "version": "v5.4.12", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "2d67c1f9a1937406a9be3171b4b22250c0a11447" + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/2d67c1f9a1937406a9be3171b4b22250c0a11447", - "reference": "2d67c1f9a1937406a9be3171b4b22250c0a11447", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/ac09569844a9109a5966b9438fc29113ce77cf51", + "reference": "ac09569844a9109a5966b9438fc29113ce77cf51", "shasum": "" }, "require": { @@ -1151,7 +1151,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.12" + "source": "https://github.com/symfony/filesystem/tree/v5.4.13" }, "funding": [ { @@ -1167,7 +1167,7 @@ "type": "tidelift" } ], - "time": "2022-08-02T13:48:16+00:00" + "time": "2022-09-21T19:53:16+00:00" }, { "name": "symfony/finder", @@ -1871,16 +1871,16 @@ }, { "name": "symfony/string", - "version": "v5.4.12", + "version": "v5.4.13", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "2fc515e512d721bf31ea76bd02fe23ada4640058" + "reference": "2900c668a32138a34118740de3e4d5a701801f53" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/2fc515e512d721bf31ea76bd02fe23ada4640058", - "reference": "2fc515e512d721bf31ea76bd02fe23ada4640058", + "url": "https://api.github.com/repos/symfony/string/zipball/2900c668a32138a34118740de3e4d5a701801f53", + "reference": "2900c668a32138a34118740de3e4d5a701801f53", "shasum": "" }, "require": { @@ -1937,7 +1937,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.12" + "source": "https://github.com/symfony/string/tree/v5.4.13" }, "funding": [ { @@ -1953,22 +1953,22 @@ "type": "tidelift" } ], - "time": "2022-08-12T17:03:11+00:00" + "time": "2022-09-01T01:52:16+00:00" } ], "packages-dev": [ { "name": "phpstan/phpstan", - "version": "1.8.5", + "version": "1.8.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20" + "reference": "08310ce271984587e2a4cda94e1ac66510a6ea07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f6598a5ff12ca4499a836815e08b4d77a2ddeb20", - "reference": "f6598a5ff12ca4499a836815e08b4d77a2ddeb20", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/08310ce271984587e2a4cda94e1ac66510a6ea07", + "reference": "08310ce271984587e2a4cda94e1ac66510a6ea07", "shasum": "" }, "require": { @@ -1998,7 +1998,7 @@ ], "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/1.8.5" + "source": "https://github.com/phpstan/phpstan/tree/1.8.8" }, "funding": [ { @@ -2014,7 +2014,7 @@ "type": "tidelift" } ], - "time": "2022-09-07T16:05:32+00:00" + "time": "2022-10-06T12:51:57+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -2120,21 +2120,21 @@ }, { "name": "phpstan/phpstan-strict-rules", - "version": "1.4.3", + "version": "1.4.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-strict-rules.git", - "reference": "431b3d6e8040075de196680cd5bc95735987b4ae" + "reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/431b3d6e8040075de196680cd5bc95735987b4ae", - "reference": "431b3d6e8040075de196680cd5bc95735987b4ae", + "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", + "reference": "23e5f377ee6395a1a04842d3d6ed4bd25e7b44a6", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.8.3" + "phpstan/phpstan": "^1.8.6" }, "require-dev": { "nikic/php-parser": "^4.13.0", @@ -2162,22 +2162,22 @@ "description": "Extra strict and opinionated rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-strict-rules/issues", - "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.3" + "source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.4.4" }, - "time": "2022-08-26T15:05:46+00:00" + "time": "2022-09-21T11:38:17+00:00" }, { "name": "phpstan/phpstan-symfony", - "version": "1.2.13", + "version": "1.2.14", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "016e441a19a2af79ca0c60920ba0d61747b4e855" + "reference": "f7dd737329504115adaa987697a759a66dd2ee8a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/016e441a19a2af79ca0c60920ba0d61747b4e855", - "reference": "016e441a19a2af79ca0c60920ba0d61747b4e855", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/f7dd737329504115adaa987697a759a66dd2ee8a", + "reference": "f7dd737329504115adaa987697a759a66dd2ee8a", "shasum": "" }, "require": { @@ -2233,9 +2233,9 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.13" + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.2.14" }, - "time": "2022-08-28T13:34:45+00:00" + "time": "2022-10-05T11:19:29+00:00" }, { "name": "symfony/phpunit-bridge", diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 8fccbfe92..6024ebb9a 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -722,7 +722,7 @@ EOT $out($iniMessage, 'comment'); } - return !$warnings && !$errors ? true : $output; + return count($warnings) === 0 && count($errors) === 0 ? true : $output; } /** diff --git a/src/Composer/Downloader/FileDownloader.php b/src/Composer/Downloader/FileDownloader.php index 04d672c63..b8907553a 100644 --- a/src/Composer/Downloader/FileDownloader.php +++ b/src/Composer/Downloader/FileDownloader.php @@ -123,7 +123,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface $retries = 3; $distUrls = $package->getDistUrls(); - /** @var array $urls */ + /** @var non-empty-array $urls */ $urls = []; foreach ($distUrls as $index => $url) { $processedUrl = $this->processUrl($package, $url); @@ -151,7 +151,6 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface $accept = null; $reject = null; $download = function () use ($io, $output, $httpDownloader, $cache, $cacheKeyGenerator, $eventDispatcher, $package, $fileName, &$urls, &$accept, &$reject) { - /** @var array{base: string, processed: string, cacheKey: string} $url */ $url = reset($urls); $index = key($urls); @@ -435,9 +434,9 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface * Process the download url * * @param PackageInterface $package package the url is coming from - * @param string $url download url + * @param non-empty-string $url download url * @throws \RuntimeException If any problem with the url - * @return string url + * @return non-empty-string url */ protected function processUrl(PackageInterface $package, string $url): string { diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index 76adb92bc..e7b94766f 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -38,17 +38,17 @@ class Package extends BasePackage protected $sourceUrl; /** @var ?string */ protected $sourceReference; - /** @var ?array */ + /** @var ?array */ protected $sourceMirrors; - /** @var ?string */ + /** @var ?non-empty-string */ protected $distType; - /** @var ?string */ + /** @var ?non-empty-string */ protected $distUrl; /** @var ?string */ protected $distReference; /** @var ?string */ protected $distSha1Checksum; - /** @var ?array */ + /** @var ?array */ protected $distMirrors; /** @var string */ protected $version; @@ -276,7 +276,7 @@ class Package extends BasePackage */ public function setDistType(?string $type): void { - $this->distType = $type; + $this->distType = $type === '' ? null : $type; } /** @@ -288,11 +288,11 @@ class Package extends BasePackage } /** - * @param string $url + * @param string|null $url */ public function setDistUrl(?string $url): void { - $this->distUrl = $url; + $this->distUrl = $url === '' ? null : $url; } /** @@ -658,9 +658,9 @@ class Package extends BasePackage /** * @param mixed[]|null $mirrors * - * @return string[] + * @return non-empty-string[] * - * @phpstan-param list|null $mirrors + * @phpstan-param list|null $mirrors */ protected function getUrls(?string $url, ?array $mirrors, ?string $ref, ?string $type, string $urlType): array { diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index b65d570a1..fc2470e2c 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -141,12 +141,12 @@ interface PackageInterface /** * Returns the source mirrors of this package * - * @return ?array + * @return ?array */ public function getSourceMirrors(): ?array; /** - * @param null|array $mirrors + * @param null|array $mirrors */ public function setSourceMirrors(?array $mirrors): void; @@ -160,14 +160,14 @@ interface PackageInterface /** * Returns the url of the distribution archive of this version * - * @return ?string + * @return ?non-empty-string */ public function getDistUrl(): ?string; /** * Returns the urls of the distribution archive of this version, including mirrors * - * @return string[] + * @return non-empty-string[] */ public function getDistUrls(): array; @@ -188,12 +188,12 @@ interface PackageInterface /** * Returns the dist mirrors of this package * - * @return ?array + * @return ?array */ public function getDistMirrors(): ?array; /** - * @param null|array $mirrors + * @param null|array $mirrors */ public function setDistMirrors(?array $mirrors): void; diff --git a/src/Composer/Plugin/PreFileDownloadEvent.php b/src/Composer/Plugin/PreFileDownloadEvent.php index bacf041dd..be368826c 100644 --- a/src/Composer/Plugin/PreFileDownloadEvent.php +++ b/src/Composer/Plugin/PreFileDownloadEvent.php @@ -28,7 +28,7 @@ class PreFileDownloadEvent extends Event private $httpDownloader; /** - * @var string + * @var non-empty-string */ private $processedUrl; @@ -55,8 +55,9 @@ class PreFileDownloadEvent extends Event /** * Constructor. * - * @param string $name The event name - * @param mixed $context + * @param string $name The event name + * @param mixed $context + * @param non-empty-string $processedUrl */ public function __construct(string $name, HttpDownloader $httpDownloader, string $processedUrl, string $type, $context = null) { @@ -74,6 +75,8 @@ class PreFileDownloadEvent extends Event /** * Retrieves the processed URL that will be downloaded. + * + * @return non-empty-string */ public function getProcessedUrl(): string { @@ -83,7 +86,7 @@ class PreFileDownloadEvent extends Event /** * Sets the processed URL that will be downloaded. * - * @param string $processedUrl New processed URL + * @param non-empty-string $processedUrl New processed URL */ public function setProcessedUrl(string $processedUrl): void { diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 057e923fd..dc0435973 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -55,9 +55,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito private $repoConfig; /** @var mixed[] */ private $options; - /** @var string */ + /** @var non-empty-string */ private $url; - /** @var string */ + /** @var non-empty-string */ private $baseUrl; /** @var IOInterface */ private $io; @@ -67,17 +67,17 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito private $loop; /** @var Cache */ protected $cache; - /** @var ?string */ + /** @var ?non-empty-string */ protected $notifyUrl = null; - /** @var ?string */ + /** @var ?non-empty-string */ protected $searchUrl = null; - /** @var ?string a URL containing %package% which can be queried to get providers of a given name */ + /** @var ?non-empty-string a URL containing %package% which can be queried to get providers of a given name */ protected $providersApiUrl = null; /** @var bool */ protected $hasProviders = false; - /** @var ?string */ + /** @var ?non-empty-string */ protected $providersUrl = null; - /** @var ?string */ + /** @var ?non-empty-string */ protected $listUrl = null; /** @var bool Indicates whether a comprehensive list of packages this repository might provide is expressed in the repository root. **/ protected $hasAvailablePackageList = false; @@ -85,7 +85,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito protected $availablePackages = null; /** @var ?array */ protected $availablePackagePatterns = null; - /** @var ?string */ + /** @var ?non-empty-string */ protected $lazyProvidersUrl = null; /** @var ?array */ protected $providerListing; @@ -95,9 +95,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito private $allowSslDowngrade = false; /** @var ?EventDispatcher */ private $eventDispatcher; - /** @var ?array> */ + /** @var ?array> */ private $sourceMirrors; - /** @var ?array */ + /** @var ?array */ private $distMirrors; /** @var bool */ private $degradedMode = false; @@ -133,7 +133,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito /** * @param array $repoConfig - * @phpstan-param array{url: string, options?: mixed[], type?: 'composer', allow_ssl_downgrade?: bool} $repoConfig + * @phpstan-param array{url: non-empty-string, options?: mixed[], type?: 'composer', allow_ssl_downgrade?: bool} $repoConfig */ public function __construct(array $repoConfig, IOInterface $io, Config $config, HttpDownloader $httpDownloader, ?EventDispatcher $eventDispatcher = null) { @@ -143,8 +143,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $repoConfig['url'] = 'http://'.$repoConfig['url']; } $repoConfig['url'] = rtrim($repoConfig['url'], '/'); + if ($repoConfig['url'] === '') { + throw new \InvalidArgumentException('The repository url must not be an empty string'); + } - if (strpos($repoConfig['url'], 'https?') === 0) { + if (str_starts_with($repoConfig['url'], 'https?')) { $repoConfig['url'] = (extension_loaded('openssl') ? 'https' : 'http') . substr($repoConfig['url'], 6); } @@ -168,7 +171,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $this->url = $match['proto'].'://repo.packagist.org'; } - $this->baseUrl = rtrim(Preg::replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/'); + $baseUrl = rtrim(Preg::replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/'); + assert($baseUrl !== ''); + $this->baseUrl = $baseUrl; $this->io = $io; $this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.Preg::replace('{[^a-z0-9.]}i', '-', Url::sanitize($this->url)), 'a-z0-9.$~'); $this->cache->setReadOnly($config->get('cache-read-only')); @@ -1265,6 +1270,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return $this->rootData = $data; } + /** + * @param non-empty-string $url + * @return non-empty-string + */ private function canonicalizeUrl(string $url): string { if ('/' === $url[0]) { @@ -1416,6 +1425,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito */ protected function fetchFile(string $filename, ?string $cacheKey = null, ?string $sha256 = null, bool $storeLastModifiedTime = false) { + if ('' === $filename) { + throw new \InvalidArgumentException('$filename should not be an empty string'); + } + if (null === $cacheKey) { $cacheKey = $filename; $filename = $this->baseUrl.'/'.$filename; @@ -1519,6 +1532,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito */ private function fetchFileIfLastModified(string $filename, string $cacheKey, string $lastModifiedTime) { + if ('' === $filename) { + throw new \InvalidArgumentException('$filename should not be an empty string'); + } + try { $options = $this->options; if ($this->eventDispatcher) { @@ -1578,6 +1595,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito private function asyncFetchFile(string $filename, string $cacheKey, ?string $lastModifiedTime = null): PromiseInterface { + if ('' === $filename) { + throw new \InvalidArgumentException('$filename should not be an empty string'); + } + if (isset($this->packagesNotFoundCache[$filename])) { return \React\Promise\resolve(['packages' => []]); } diff --git a/src/Composer/Util/ComposerMirror.php b/src/Composer/Util/ComposerMirror.php index 6f23c22c3..106e76c41 100644 --- a/src/Composer/Util/ComposerMirror.php +++ b/src/Composer/Util/ComposerMirror.php @@ -21,6 +21,10 @@ use Composer\Pcre\Preg; */ class ComposerMirror { + /** + * @param non-empty-string $mirrorUrl + * @return non-empty-string + */ public static function processUrl(string $mirrorUrl, string $packageName, string $version, ?string $reference, ?string $type, ?string $prettyVersion = null): string { if ($reference) { @@ -35,9 +39,16 @@ class ComposerMirror $to[] = $prettyVersion; } - return str_replace($from, $to, $mirrorUrl); + $url = str_replace($from, $to, $mirrorUrl); + assert($url !== ''); + + return $url; } + /** + * @param non-empty-string $mirrorUrl + * @return string + */ public static function processGitUrl(string $mirrorUrl, string $packageName, string $url, ?string $type): string { if (Preg::isMatch('#^(?:(?:https?|git)://github\.com/|git@github\.com:)([^/]+)/(.+?)(?:\.git)?$#', $url, $match)) { @@ -55,6 +66,10 @@ class ComposerMirror ); } + /** + * @param non-empty-string $mirrorUrl + * @return string + */ public static function processHgUrl(string $mirrorUrl, string $packageName, string $url, string $type): string { return self::processGitUrl($mirrorUrl, $packageName, $url, $type); diff --git a/src/Composer/Util/Http/CurlDownloader.php b/src/Composer/Util/Http/CurlDownloader.php index bb81ac654..aaba93627 100644 --- a/src/Composer/Util/Http/CurlDownloader.php +++ b/src/Composer/Util/Http/CurlDownloader.php @@ -29,7 +29,7 @@ use React\Promise\Promise; * @author Jordi Boggiano * @author Nicolas Grekas * @phpstan-type Attributes array{retryAuthFailure: bool, redirects: int<0, max>, retries: int<0, max>, storeAuth: 'prompt'|bool} - * @phpstan-type Job array{url: string, origin: string, attributes: Attributes, options: mixed[], progress: mixed[], curlHandle: resource, filename: string|null, headerHandle: resource, bodyHandle: resource, resolve: callable, reject: callable} + * @phpstan-type Job array{url: non-empty-string, origin: string, attributes: Attributes, options: mixed[], progress: mixed[], curlHandle: \CurlHandle, filename: string|null, headerHandle: resource, bodyHandle: resource, resolve: callable, reject: callable} */ class CurlDownloader { @@ -125,6 +125,7 @@ class CurlDownloader /** * @param mixed[] $options + * @param non-empty-string $url * * @return int internal job id */ @@ -143,16 +144,18 @@ class CurlDownloader * @param mixed[] $options * * @param array{retryAuthFailure?: bool, redirects?: int<0, max>, retries?: int<0, max>, storeAuth?: 'prompt'|bool} $attributes + * @param non-empty-string $url * * @return int internal job id */ private function initDownload(callable $resolve, callable $reject, string $origin, string $url, array $options, ?string $copyTo = null, array $attributes = []): int { - // set defaults in a PHPStan-happy way (array_merge is not well supported) - $attributes['retryAuthFailure'] = $attributes['retryAuthFailure'] ?? true; - $attributes['redirects'] = $attributes['redirects'] ?? 0; - $attributes['retries'] = $attributes['retries'] ?? 0; - $attributes['storeAuth'] = $attributes['storeAuth'] ?? false; + $attributes = array_merge([ + 'retryAuthFailure' => true, + 'redirects' => 0, + 'retries' => 0, + 'storeAuth' => false, + ], $attributes); $originalOptions = $options; @@ -167,22 +170,24 @@ class CurlDownloader throw new \RuntimeException('Failed to open a temp stream to store curl headers'); } - if ($copyTo) { - $errorMessage = ''; - // @phpstan-ignore-next-line - set_error_handler(static function ($code, $msg) use (&$errorMessage): void { - if ($errorMessage) { - $errorMessage .= "\n"; - } - $errorMessage .= Preg::replace('{^fopen\(.*?\): }', '', $msg); - }); - $bodyHandle = fopen($copyTo.'~', 'w+b'); - restore_error_handler(); - if (!$bodyHandle) { - throw new TransportException('The "'.$url.'" file could not be written to '.$copyTo.': '.$errorMessage); - } + if ($copyTo !== null) { + $bodyTarget = $copyTo.'~'; } else { - $bodyHandle = @fopen('php://temp/maxmemory:524288', 'w+b'); + $bodyTarget = 'php://temp/maxmemory:524288'; + } + + $errorMessage = ''; + // @phpstan-ignore-next-line + set_error_handler(static function ($code, $msg) use (&$errorMessage): void { + if ($errorMessage) { + $errorMessage .= "\n"; + } + $errorMessage .= Preg::replace('{^fopen\(.*?\): }', '', $msg); + }); + $bodyHandle = fopen($bodyTarget, 'w+b'); + restore_error_handler(); + if (false === $bodyHandle) { + throw new TransportException('The "'.$url.'" file could not be written to '.($copyTo ?? 'a temporary file').': '.$errorMessage); } curl_setopt($curlHandle, CURLOPT_URL, $url); @@ -229,7 +234,9 @@ class CurlDownloader // Always set CURLOPT_PROXY to enable/disable proxy handling // Any proxy authorization is included in the proxy url $proxy = $this->proxyManager->getProxyForRequest($url); - curl_setopt($curlHandle, CURLOPT_PROXY, $proxy->getUrl()); + if ($proxy->getUrl() !== '') { + curl_setopt($curlHandle, CURLOPT_PROXY, $proxy->getUrl()); + } // Curl needs certificate locations for secure proxies. // CURLOPT_PROXY_SSL_VERIFY_PEER/HOST are enabled by default @@ -373,8 +380,8 @@ class CurlDownloader rewind($job['bodyHandle']); $contents = stream_get_contents($job['bodyHandle']); } - $response = new CurlResponse(['url' => $progress['url']], $statusCode, $headers, $contents, $progress); - $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); + $response = new CurlResponse(['url' => $job['url']], $statusCode, $headers, $contents, $progress); + $this->io->writeError('['.$statusCode.'] '.Url::sanitize($job['url']), true, IOInterface::DEBUG); } else { $maxFileSize = $job['options']['max_file_size'] ?? null; rewind($job['bodyHandle']); @@ -389,8 +396,8 @@ class CurlDownloader $contents = stream_get_contents($job['bodyHandle']); } - $response = new CurlResponse(['url' => $progress['url']], $statusCode, $headers, $contents, $progress); - $this->io->writeError('['.$statusCode.'] '.Url::sanitize($progress['url']), true, IOInterface::DEBUG); + $response = new CurlResponse(['url' => $job['url']], $statusCode, $headers, $contents, $progress); + $this->io->writeError('['.$statusCode.'] '.Url::sanitize($job['url']), true, IOInterface::DEBUG); } fclose($job['bodyHandle']); @@ -456,9 +463,6 @@ 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); @@ -569,6 +573,7 @@ class CurlDownloader /** * @param Job $job + * @param non-empty-string $url * * @param array{retryAuthFailure?: bool, redirects?: int<0, max>, storeAuth?: 'prompt'|bool, retries?: int<1, max>} $attributes */ @@ -586,6 +591,7 @@ class CurlDownloader /** * @param Job $job + * @param non-empty-string $url * * @param array{retryAuthFailure?: bool, redirects?: int<0, max>, storeAuth?: 'prompt'|bool, retries: int<1, max>} $attributes */ diff --git a/src/Composer/Util/Http/ProxyManager.php b/src/Composer/Util/Http/ProxyManager.php index 2f8764f82..5af53ad0c 100644 --- a/src/Composer/Util/Http/ProxyManager.php +++ b/src/Composer/Util/Http/ProxyManager.php @@ -74,6 +74,8 @@ class ProxyManager /** * Returns a RequestProxy instance for the request url + * + * @param non-empty-string $requestUrl */ public function getProxyForRequest(string $requestUrl): RequestProxy { diff --git a/src/Composer/Util/Http/Response.php b/src/Composer/Util/Http/Response.php index 53afb67ba..5e5d18a2e 100644 --- a/src/Composer/Util/Http/Response.php +++ b/src/Composer/Util/Http/Response.php @@ -17,7 +17,7 @@ use Composer\Pcre\Preg; use Composer\Util\HttpDownloader; /** - * @phpstan-import-type Request from HttpDownloader + * @phpstan-type Request array{url: non-empty-string, options?: mixed[], copyTo?: string|null} */ class Response { @@ -31,7 +31,7 @@ class Response private $body; /** - * @param Request $request + * @param Request $request * @param list $headers */ public function __construct(array $request, ?int $code, array $headers, ?string $body) diff --git a/src/Composer/Util/HttpDownloader.php b/src/Composer/Util/HttpDownloader.php index 9e2303974..5e6e0c56b 100644 --- a/src/Composer/Util/HttpDownloader.php +++ b/src/Composer/Util/HttpDownloader.php @@ -27,7 +27,7 @@ use React\Promise\PromiseInterface; /** * @author Jordi Boggiano - * @phpstan-type Request array{url: string, options?: mixed[], copyTo?: ?string} + * @phpstan-type Request array{url: non-empty-string, options: mixed[], copyTo: string|null} * @phpstan-type Job array{id: int, status: int, request: Request, sync: bool, origin: string, resolve?: callable, reject?: callable, curl_id?: int, response?: Response, exception?: TransportException} */ class HttpDownloader @@ -104,6 +104,9 @@ class HttpDownloader */ public function get(string $url, array $options = []) { + if ('' === $url) { + throw new \InvalidArgumentException('$url must not be an empty string'); + } [$job] = $this->addJob(['url' => $url, 'options' => $options, 'copyTo' => null], true); $this->wait($job['id']); @@ -123,6 +126,9 @@ class HttpDownloader */ public function add(string $url, array $options = []) { + if ('' === $url) { + throw new \InvalidArgumentException('$url must not be an empty string'); + } [, $promise] = $this->addJob(['url' => $url, 'options' => $options, 'copyTo' => null]); return $promise; @@ -140,6 +146,9 @@ class HttpDownloader */ public function copy(string $url, string $to, array $options = []) { + if ('' === $url) { + throw new \InvalidArgumentException('$url must not be an empty string'); + } [$job] = $this->addJob(['url' => $url, 'options' => $options, 'copyTo' => $to], true); $this->wait($job['id']); @@ -158,6 +167,9 @@ class HttpDownloader */ public function addCopy(string $url, string $to, array $options = []) { + if ('' === $url) { + throw new \InvalidArgumentException('$url must not be an empty string'); + } [, $promise] = $this->addJob(['url' => $url, 'options' => $options, 'copyTo' => $to]); return $promise; @@ -295,6 +307,9 @@ class HttpDownloader $job['status'] = self::STATUS_STARTED; $this->runningJobs++; + assert(isset($job['resolve'])); + assert(isset($job['reject'])); + $resolve = $job['resolve']; $reject = $job['reject']; $url = $job['request']['url']; @@ -397,6 +412,7 @@ class HttpDownloader } if ($this->jobs[$index]['status'] === self::STATUS_FAILED) { + assert(isset($this->jobs[$index]['exception'])); throw $this->jobs[$index]['exception']; } diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 3ff6f2e40..a490c966f 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -38,7 +38,7 @@ class RemoteFilesystem private $bytesMax; /** @var string */ private $originUrl; - /** @var string */ + /** @var non-empty-string */ private $fileUrl; /** @var ?string */ private $fileName; @@ -98,7 +98,7 @@ class RemoteFilesystem * Copy the remote file in local. * * @param string $originUrl The origin URL - * @param string $fileUrl The file URL + * @param non-empty-string $fileUrl The file URL * @param string $fileName the local filename * @param bool $progress Display the progression * @param mixed[] $options Additional context options @@ -114,7 +114,7 @@ class RemoteFilesystem * Get the content. * * @param string $originUrl The origin URL - * @param string $fileUrl The file URL + * @param non-empty-string $fileUrl The file URL * @param bool $progress Display the progression * @param mixed[] $options Additional context options * @@ -206,7 +206,7 @@ class RemoteFilesystem * Get file content or copy action. * * @param string $originUrl The origin URL - * @param string $fileUrl The file URL + * @param non-empty-string $fileUrl The file URL * @param mixed[] $additionalOptions context options * @param string $fileName the local filename * @param bool $progress Display the progression diff --git a/src/Composer/Util/StreamContextFactory.php b/src/Composer/Util/StreamContextFactory.php index 18425d7ad..57fbe0f0e 100644 --- a/src/Composer/Util/StreamContextFactory.php +++ b/src/Composer/Util/StreamContextFactory.php @@ -30,7 +30,7 @@ final class StreamContextFactory /** * Creates a context supporting HTTP proxies * - * @param string $url URL the context is to be used for + * @param non-empty-string $url URL the context is to be used for * @phpstan-param array{http?: array{follow_location?: int, max_redirects?: int, header?: string|array}} $defaultOptions * @param mixed[] $defaultOptions Options to merge with the default * @param mixed[] $defaultParams Parameters to specify on the context @@ -57,6 +57,7 @@ final class StreamContextFactory } /** + * @param non-empty-string $url * @param mixed[] $options * @param bool $forCurl When true, will not add proxy values as these are handled separately * @phpstan-return array{http: array{header: string[], proxy?: string, request_fulluri: bool}, ssl?: mixed[]} diff --git a/src/Composer/Util/Url.php b/src/Composer/Util/Url.php index 143b19f48..2372bbfc3 100644 --- a/src/Composer/Util/Url.php +++ b/src/Composer/Util/Url.php @@ -21,7 +21,8 @@ use Composer\Pcre\Preg; class Url { /** - * @return string the updated URL + * @param non-empty-string $url + * @return non-empty-string the updated URL */ public static function updateDistReference(Config $config, string $url, string $ref): string { @@ -54,9 +55,15 @@ class Url $url = Preg::replace('{(/api/v[34]/projects/[^/]+/repository/archive\.(?:zip|tar\.gz|tar\.bz2|tar)\?sha=).+$}i', '${1}'.$ref, $url); } + assert($url !== ''); + return $url; } + /** + * @param non-empty-string $url + * @return non-empty-string + */ public static function getOrigin(Config $config, string $url): string { if (0 === strpos($url, 'file://')) { @@ -87,7 +94,7 @@ class Url && !in_array($origin, $config->get('gitlab-domains'), true) ) { foreach ($config->get('gitlab-domains') as $gitlabDomain) { - if (0 === strpos($gitlabDomain, $origin)) { + if ($gitlabDomain !== '' && str_starts_with($gitlabDomain, $origin)) { return $gitlabDomain; } } diff --git a/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test b/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test index fd2e905a4..522abcb63 100644 --- a/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test +++ b/tests/Composer/Test/Fixtures/installer/install-aliased-alias.test @@ -8,7 +8,7 @@ Installing double aliased package "package": [ { "name": "a/a", "version": "dev-master", - "dist": { "type": "file", "url": "" }, + "dist": { "type": "file", "url": "https://example.org" }, "require": { "b/b": "dev-master" }, @@ -17,7 +17,7 @@ Installing double aliased package { "name": "b/b", "version": "dev-foo", "extra": { "branch-alias": { "dev-foo": "1.0.x-dev" } }, - "dist": { "type": "file", "url": "" } + "dist": { "type": "file", "url": "https://example.org" } } ] } diff --git a/tests/Composer/Test/Fixtures/installer/install-reference.test b/tests/Composer/Test/Fixtures/installer/install-reference.test index 33e7f5946..49197cf1a 100644 --- a/tests/Composer/Test/Fixtures/installer/install-reference.test +++ b/tests/Composer/Test/Fixtures/installer/install-reference.test @@ -8,7 +8,7 @@ Installs a dev package forcing it's reference "package": [ { "name": "a/a", "version": "dev-main", - "source": { "reference": "abc123", "url": "", "type": "git" }, + "source": { "reference": "abc123", "url": "https://example.org", "type": "git" }, "default-branch": true } ] diff --git a/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test b/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test index 68c02006d..4c840558c 100644 --- a/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test +++ b/tests/Composer/Test/Fixtures/installer/update-dev-ignores-providers.test @@ -8,14 +8,14 @@ Updating a dev package selects its newest version but no providers "package": [ { "name": "a/replacer", "version": "dev-master", - "source": { "reference": "wrong", "url": "", "type": "git" }, + "source": { "reference": "wrong", "url": "https://example.org", "type": "git" }, "replace": { "a/installed": "dev-master" } }, { "name": "a/installed", "version": "dev-master", - "source": { "reference": "newref", "url": "", "type": "git" } + "source": { "reference": "newref", "url": "https://example.org", "type": "git" } } ] } @@ -29,7 +29,7 @@ Updating a dev package selects its newest version but no providers [ { "name": "a/installed", "version": "dev-master", - "source": { "reference": "oldref", "url": "", "type": "git" } + "source": { "reference": "oldref", "url": "https://example.org", "type": "git" } } ] --RUN-- diff --git a/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test b/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test index 7985ec7fa..7c7a3c812 100644 --- a/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test +++ b/tests/Composer/Test/Fixtures/installer/update-dev-to-new-ref-picks-up-changes.test @@ -8,7 +8,7 @@ Updating a dev package to its latest ref should pick up new dependencies "package": [ { "name": "a/devpackage", "version": "dev-main", - "source": { "reference": "newref", "url": "", "type": "git" }, + "source": { "reference": "newref", "url": "https://example.org", "type": "git" }, "require": { "a/dependency": "*" }, @@ -16,7 +16,7 @@ Updating a dev package to its latest ref should pick up new dependencies }, { "name": "a/dependency", "version": "dev-main", - "source": { "reference": "ref", "url": "", "type": "git" }, + "source": { "reference": "ref", "url": "https://example.org", "type": "git" }, "require": {}, "default-branch": true } @@ -32,7 +32,7 @@ Updating a dev package to its latest ref should pick up new dependencies [ { "name": "a/devpackage", "version": "dev-main", - "source": { "reference": "oldref", "url": "", "type": "git" }, + "source": { "reference": "oldref", "url": "https://example.org", "type": "git" }, "require": {}, "default-branch": true } diff --git a/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test b/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test index 7b0a87e59..95ac4cc70 100644 --- a/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test +++ b/tests/Composer/Test/Fixtures/installer/update-downgrades-unstable-packages.test @@ -8,23 +8,23 @@ Downgrading from unstable to more stable package should work even if already ins "package": [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abcd", "url": "", "type": "git" }, + "source": { "reference": "abcd", "url": "https://example.org", "type": "git" }, "default-branch": true }, { "name": "a/a", "version": "1.0.0", - "source": { "reference": "1.0.0", "url": "", "type": "git" }, - "dist": { "reference": "1.0.0", "url": "", "type": "zip", "shasum": "" } + "source": { "reference": "1.0.0", "url": "https://example.org", "type": "git" }, + "dist": { "reference": "1.0.0", "url": "https://example.org", "type": "zip", "shasum": "" } }, { "name": "b/b", "version": "dev-master", - "source": { "reference": "abcd", "url": "", "type": "git" }, + "source": { "reference": "abcd", "url": "https://example.org", "type": "git" }, "default-branch": true }, { "name": "b/b", "version": "1.0.0", - "source": { "reference": "1.0.0", "url": "", "type": "git" }, - "dist": { "reference": "1.0.0", "url": "", "type": "zip", "shasum": "" } + "source": { "reference": "1.0.0", "url": "https://example.org", "type": "git" }, + "dist": { "reference": "1.0.0", "url": "https://example.org", "type": "zip", "shasum": "" } } ] } @@ -38,12 +38,12 @@ Downgrading from unstable to more stable package should work even if already ins [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abcd", "url": "", "type": "git" }, + "source": { "reference": "abcd", "url": "https://example.org", "type": "git" }, "default-branch": true }, { "name": "b/b", "version": "dev-master", - "source": { "reference": "abcd", "url": "", "type": "git" }, + "source": { "reference": "abcd", "url": "https://example.org", "type": "git" }, "default-branch": true } ] diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-alias-dry-run.test b/tests/Composer/Test/Fixtures/installer/update-installed-alias-dry-run.test index b53287a32..87cfd45b6 100644 --- a/tests/Composer/Test/Fixtures/installer/update-installed-alias-dry-run.test +++ b/tests/Composer/Test/Fixtures/installer/update-installed-alias-dry-run.test @@ -9,12 +9,12 @@ Updates installed alias packages in dry-run mode { "name": "a/a", "version": "dev-master", "require": { "b/b": "2.0.*" }, - "source": { "reference": "abcdef", "url": "", "type": "git" }, + "source": { "reference": "abcdef", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } } }, { "name": "b/b", "version": "dev-master", - "source": { "reference": "123456", "url": "", "type": "git" }, + "source": { "reference": "123456", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } } } ] @@ -30,12 +30,12 @@ Updates installed alias packages in dry-run mode { "name": "a/a", "version": "dev-master", "require": { "b/b": "2.0.*" }, - "source": { "reference": "abcdef", "url": "", "type": "git" }, + "source": { "reference": "abcdef", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } } }, { "name": "b/b", "version": "dev-master", - "source": { "reference": "123456", "url": "", "type": "git" }, + "source": { "reference": "123456", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } } } ] diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-alias.test b/tests/Composer/Test/Fixtures/installer/update-installed-alias.test index f5b7e0549..fab135fa4 100644 --- a/tests/Composer/Test/Fixtures/installer/update-installed-alias.test +++ b/tests/Composer/Test/Fixtures/installer/update-installed-alias.test @@ -9,12 +9,12 @@ Updates installed alias packages { "name": "a/a", "version": "dev-master", "require": { "b/b": "2.0.*" }, - "source": { "reference": "abcdef", "url": "", "type": "git" }, + "source": { "reference": "abcdef", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } } }, { "name": "b/b", "version": "dev-master", - "source": { "reference": "123456", "url": "", "type": "git" }, + "source": { "reference": "123456", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } } } ] @@ -30,12 +30,12 @@ Updates installed alias packages { "name": "a/a", "version": "dev-master", "require": { "b/b": "2.0.*" }, - "source": { "reference": "abcdef", "url": "", "type": "git" }, + "source": { "reference": "abcdef", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "1.0.x-dev" } } }, { "name": "b/b", "version": "dev-master", - "source": { "reference": "123456", "url": "", "type": "git" }, + "source": { "reference": "123456", "url": "https://example.org", "type": "git" }, "extra": { "branch-alias": { "dev-master": "2.0.x-dev" } } } ] diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test b/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test index 3c9036be4..d3444c1c3 100644 --- a/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test +++ b/tests/Composer/Test/Fixtures/installer/update-installed-reference-dry-run.test @@ -8,7 +8,7 @@ Updating a dev package forcing it's reference, using dry run, should not do anyt "package": [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abc123", "url": "", "type": "git" } + "source": { "reference": "abc123", "url": "https://example.org", "type": "git" } } ] } @@ -21,8 +21,8 @@ Updating a dev package forcing it's reference, using dry run, should not do anyt [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "def000", "url": "", "type": "git" }, - "dist": { "reference": "def000", "url": "", "type": "zip", "shasum": "" } + "source": { "reference": "def000", "url": "https://example.org", "type": "git" }, + "dist": { "reference": "def000", "url": "https://example.org", "type": "zip", "shasum": "" } } ] --RUN-- diff --git a/tests/Composer/Test/Fixtures/installer/update-installed-reference.test b/tests/Composer/Test/Fixtures/installer/update-installed-reference.test index 9f91322eb..3d2cd9d2d 100644 --- a/tests/Composer/Test/Fixtures/installer/update-installed-reference.test +++ b/tests/Composer/Test/Fixtures/installer/update-installed-reference.test @@ -8,7 +8,7 @@ Updating a dev package forcing it's reference should not do anything if the refe "package": [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abc123", "url": "", "type": "git" } + "source": { "reference": "abc123", "url": "https://example.org", "type": "git" } } ] } @@ -21,8 +21,8 @@ Updating a dev package forcing it's reference should not do anything if the refe [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "def000", "url": "", "type": "git" }, - "dist": { "reference": "def000", "url": "", "type": "zip", "shasum": "" } + "source": { "reference": "def000", "url": "https://example.org", "type": "git" }, + "dist": { "reference": "def000", "url": "https://example.org", "type": "zip", "shasum": "" } } ] --RUN-- diff --git a/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test b/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test index 734315eb5..416ae9b15 100644 --- a/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test +++ b/tests/Composer/Test/Fixtures/installer/update-reference-picks-latest.test @@ -8,7 +8,7 @@ Updating a dev package should update to the latest available reference "package": [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abc123", "url": "", "type": "git" } + "source": { "reference": "abc123", "url": "https://example.org", "type": "git" } } ] } @@ -21,8 +21,8 @@ Updating a dev package should update to the latest available reference [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "def000", "url": "", "type": "git" }, - "dist": { "reference": "def000", "url": "", "type": "zip", "shasum": "" } + "source": { "reference": "def000", "url": "https://example.org", "type": "git" }, + "dist": { "reference": "def000", "url": "https://example.org", "type": "zip", "shasum": "" } } ] --RUN-- diff --git a/tests/Composer/Test/Fixtures/installer/update-reference.test b/tests/Composer/Test/Fixtures/installer/update-reference.test index 185b72013..35383ca6f 100644 --- a/tests/Composer/Test/Fixtures/installer/update-reference.test +++ b/tests/Composer/Test/Fixtures/installer/update-reference.test @@ -8,7 +8,7 @@ Updates a dev package forcing its reference "package": [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abc123", "url": "", "type": "git" } + "source": { "reference": "abc123", "url": "https://example.org", "type": "git" } } ] } @@ -21,7 +21,7 @@ Updates a dev package forcing its reference [ { "name": "a/a", "version": "dev-master", - "source": { "reference": "abc123", "url": "", "type": "git" } + "source": { "reference": "abc123", "url": "https://example.org", "type": "git" } } ] --RUN-- diff --git a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test index d5613a648..545f92fe9 100644 --- a/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test +++ b/tests/Composer/Test/Fixtures/installer/updating-dev-from-lock-removes-old-deps.test @@ -12,7 +12,7 @@ Installing locked dev packages should remove old dependencies "packages": [ { "name": "a/devpackage", "version": "dev-master", - "source": { "reference": "newref", "url": "", "type": "git" }, + "source": { "reference": "newref", "url": "https://example.org", "type": "git" }, "require": {}, "default-branch": true } @@ -28,7 +28,7 @@ Installing locked dev packages should remove old dependencies [ { "name": "a/devpackage", "version": "dev-master", - "source": { "reference": "oldref", "url": "", "type": "git" }, + "source": { "reference": "oldref", "url": "https://example.org", "type": "git" }, "require": { "a/dependency": "*" }, @@ -36,7 +36,7 @@ Installing locked dev packages should remove old dependencies }, { "name": "a/dependency", "version": "dev-master", - "source": { "reference": "ref", "url": "", "type": "git" }, + "source": { "reference": "ref", "url": "https://example.org", "type": "git" }, "require": {}, "default-branch": true } diff --git a/tests/Composer/Test/Mock/HttpDownloaderMock.php b/tests/Composer/Test/Mock/HttpDownloaderMock.php index 32168c3f0..5583b1325 100644 --- a/tests/Composer/Test/Mock/HttpDownloaderMock.php +++ b/tests/Composer/Test/Mock/HttpDownloaderMock.php @@ -24,7 +24,7 @@ use PHPUnit\Framework\AssertionFailedError; class HttpDownloaderMock extends HttpDownloader { /** - * @var array|null, status: int, body: string, headers: list}>|null + * @var array|null, status: int, body: string, headers: list}>|null */ private $expectations = null; /** @@ -52,7 +52,7 @@ class HttpDownloaderMock extends HttpDownloader } /** - * @param array, status?: int, body?: string, headers?: array}> $expectations + * @param array, status?: int, body?: string, headers?: array}> $expectations * @param bool $strict set to true if you want to provide *all* expected http requests, and not just a subset you are interested in testing * @param array{status?: int, body?: string, headers?: array} $defaultHandler default URL handler for undefined requests if not in strict mode */ @@ -64,23 +64,11 @@ class HttpDownloaderMock extends HttpDownloader throw new \UnexpectedValueException('Unexpected keys in process execution step: '.implode(', ', array_keys($diff))); } - // set defaults in a PHPStan-happy way (array_merge is not well supported) - $expect['url'] = $expect['url'] ?? $default['url']; - $expect['options'] = $expect['options'] ?? $default['options']; - $expect['status'] = $expect['status'] ?? $default['status']; - $expect['body'] = $expect['body'] ?? $default['body']; - $expect['headers'] = $expect['headers'] ?? $default['headers']; - - return $expect; + return array_merge($default, $expect); }, $expectations); $this->strict = $strict; - // set defaults in a PHPStan-happy way (array_merge is not well supported) - $defaultHandler['status'] = $defaultHandler['status'] ?? $this->defaultHandler['status']; - $defaultHandler['body'] = $defaultHandler['body'] ?? $this->defaultHandler['body']; - $defaultHandler['headers'] = $defaultHandler['headers'] ?? $this->defaultHandler['headers']; - - $this->defaultHandler = $defaultHandler; + $this->defaultHandler = array_merge($this->defaultHandler, $defaultHandler); } public function assertComplete(): void @@ -107,6 +95,10 @@ class HttpDownloaderMock extends HttpDownloader public function get($fileUrl, $options = []): Response { + if ('' === $fileUrl) { + throw new \LogicException('url cannot be an empty string'); + } + $this->log[] = $fileUrl; if (is_array($this->expectations) && count($this->expectations) > 0 && $fileUrl === $this->expectations[0]['url'] && ($this->expectations[0]['options'] === null || $options === $this->expectations[0]['options'])) { @@ -128,6 +120,7 @@ class HttpDownloaderMock extends HttpDownloader /** * @param string[] $headers + * @param non-empty-string $url */ private function respond(string $url, int $status, array $headers, string $body): Response { diff --git a/tests/Composer/Test/Mock/ProcessExecutorMock.php b/tests/Composer/Test/Mock/ProcessExecutorMock.php index 713fab82c..96a4a9915 100644 --- a/tests/Composer/Test/Mock/ProcessExecutorMock.php +++ b/tests/Composer/Test/Mock/ProcessExecutorMock.php @@ -74,23 +74,11 @@ class ProcessExecutorMock extends ProcessExecutor throw new \UnexpectedValueException('Unexpected keys in process execution step: '.implode(', ', array_keys($diff))); } - // set defaults in a PHPStan-happy way (array_merge is not well supported) - $expect['cmd'] = $expect['cmd'] ?? $default['cmd']; - $expect['return'] = $expect['return'] ?? $default['return']; - $expect['stdout'] = $expect['stdout'] ?? $default['stdout']; - $expect['stderr'] = $expect['stderr'] ?? $default['stderr']; - $expect['callback'] = $expect['callback'] ?? $default['callback']; - - return $expect; + return array_merge($default, $expect); }, $expectations); $this->strict = $strict; - // set defaults in a PHPStan-happy way (array_merge is not well supported) - $defaultHandler['return'] = $defaultHandler['return'] ?? $this->defaultHandler['return']; - $defaultHandler['stdout'] = $defaultHandler['stdout'] ?? $this->defaultHandler['stdout']; - $defaultHandler['stderr'] = $defaultHandler['stderr'] ?? $this->defaultHandler['stderr']; - - $this->defaultHandler = $defaultHandler; + $this->defaultHandler = array_merge($this->defaultHandler, $defaultHandler); } public function assertComplete(): void diff --git a/tests/Composer/Test/Repository/ComposerRepositoryTest.php b/tests/Composer/Test/Repository/ComposerRepositoryTest.php index 52fd99cda..ea91c77cc 100644 --- a/tests/Composer/Test/Repository/ComposerRepositoryTest.php +++ b/tests/Composer/Test/Repository/ComposerRepositoryTest.php @@ -274,6 +274,8 @@ class ComposerRepositoryTest extends TestCase /** * @dataProvider provideCanonicalizeUrlTestCases + * @param non-empty-string $url + * @param non-empty-string $repositoryUrl */ public function testCanonicalizeUrl(string $expected, string $url, string $repositoryUrl): void { diff --git a/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php index 4f5f0d637..c23994c5f 100644 --- a/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitLabDriverTest.php @@ -86,6 +86,8 @@ class GitLabDriverTest extends TestCase /** * @dataProvider provideInitializeUrls + * @param non-empty-string $url + * @param non-empty-string $apiUrl */ public function testInitialize(string $url, string $apiUrl): GitLabDriver { @@ -126,6 +128,8 @@ JSON; /** * @dataProvider provideInitializeUrls + * @param non-empty-string $url + * @param non-empty-string $apiUrl */ public function testInitializePublicProject(string $url, string $apiUrl): GitLabDriver { @@ -164,6 +168,8 @@ JSON; /** * @dataProvider provideInitializeUrls + * @param non-empty-string $url + * @param non-empty-string $apiUrl */ public function testInitializePublicProjectAsAnonymous(string $url, string $apiUrl): GitLabDriver { diff --git a/tests/Composer/Test/Util/Http/ProxyManagerTest.php b/tests/Composer/Test/Util/Http/ProxyManagerTest.php index 8602ab0c0..f15c4bcf5 100644 --- a/tests/Composer/Test/Util/Http/ProxyManagerTest.php +++ b/tests/Composer/Test/Util/Http/ProxyManagerTest.php @@ -72,6 +72,7 @@ class ProxyManagerTest extends TestCase * * @param array $server * @param mixed[] $expectedOptions + * @param non-empty-string $url */ public function testGetProxyForRequest(array $server, string $url, string $expectedUrl, array $expectedOptions, bool $expectedSecure, string $expectedMessage): void { diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index d7326b149..d0d1f3cf3 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/tests/Composer/Test/Util/RemoteFilesystemTest.php @@ -276,6 +276,7 @@ class RemoteFilesystemTest extends TestCase * Tests that a BitBucket public download is correctly retrieved. * * @dataProvider provideBitbucketPublicDownloadUrls + * @param non-empty-string $url */ public function testBitBucketPublicDownload(string $url, string $contents): void { @@ -297,6 +298,7 @@ class RemoteFilesystemTest extends TestCase * Tests that a BitBucket public download is correctly retrieved when `bitbucket-oauth` is configured. * * @dataProvider provideBitbucketPublicDownloadUrls + * @param non-empty-string $url */ public function testBitBucketPublicDownloadWithAuthConfigured(string $url, string $contents): void { diff --git a/tests/Composer/Test/Util/UrlTest.php b/tests/Composer/Test/Util/UrlTest.php index d2c4e59a6..7b1b4bc3a 100644 --- a/tests/Composer/Test/Util/UrlTest.php +++ b/tests/Composer/Test/Util/UrlTest.php @@ -22,6 +22,7 @@ class UrlTest extends TestCase * @dataProvider distRefsProvider * * @param array $conf + * @param non-empty-string $url */ public function testUpdateDistReference(string $url, string $expectedUrl, array $conf = [], string $ref = 'newref'): void {