From 66a767c065abb12e179f16c4fc618127e83f29e6 Mon Sep 17 00:00:00 2001 From: Mike Baynton Date: Wed, 9 Sep 2020 21:15:36 -0500 Subject: [PATCH] specify covered names with regexes in 2.x repos This supplements the available-packages list so that repositories may rule themselves out of a given name (and thus not be probed with lazy load requests) by regex, as well as by exact name match. The use case is sizeable and varying supplemental Composer repositories such as packages.drupal.org, which otherwise must either maintain a list of over 10k package names in their root packages.json or accept lots of lazy-load requests for unrelated packages that will 404. --- .../Repository/ComposerRepository.php | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index 77f0288bc..8fba9f84d 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -12,6 +12,7 @@ namespace Composer\Repository; +use Composer\Package\BasePackage; use Composer\Package\Loader\ArrayLoader; use Composer\Package\PackageInterface; use Composer\Package\AliasPackage; @@ -55,7 +56,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito protected $hasProviders = false; protected $providersUrl; protected $listUrl; + /** @var bool Indicates whether a comprehensive list of packages this repository might provide is expressed in the repository root. **/ + protected $hasPredefinedPackageCoverage = false; protected $availablePackages; + protected $availablePackageRegexes; protected $lazyProvidersUrl; protected $providerListing; protected $loader; @@ -162,7 +166,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return $this->filterPackages($this->whatProvides($name), $constraint, true); } - if (is_array($this->availablePackages) && !isset($this->availablePackages[$name])) { + if ($this->lazyProvidersRepoDoesNotContain($name)) { return; } @@ -202,7 +206,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito return $this->filterPackages($this->whatProvides($name), $constraint); } - if (is_array($this->availablePackages) && !isset($this->availablePackages[$name])) { + if ($this->lazyProvidersRepoDoesNotContain($name)) { return array(); } @@ -260,7 +264,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $hasProviders = $this->hasProviders(); if ($this->lazyProvidersUrl) { - if (is_array($this->availablePackages)) { + if (is_array($this->availablePackages) && count($this->availablePackageRegexes) == 0) { $packageMap = array(); foreach ($this->availablePackages as $name) { $packageMap[$name] = new MatchAllConstraint(); @@ -388,10 +392,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito } if ($this->lazyProvidersUrl && count($packageNameMap)) { - if (is_array($this->availablePackages)) { - $availPackages = $this->availablePackages; + if ($this->hasPredefinedPackageCoverage) { foreach ($packageNameMap as $name => $constraint) { - if (!isset($availPackages[strtolower($name)])) { + if ($this->lazyProvidersRepoDoesNotContain(strtolower($name))) { unset($packageNameMap[$name]); } } @@ -876,6 +879,15 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito if (!empty($data['available-packages'])) { $availPackages = array_map('strtolower', $data['available-packages']); $this->availablePackages = array_combine($availPackages, $availPackages); + $this->hasPredefinedPackageCoverage = true; + } + + // Provides a list of package name regexes that are available in this repo + // Disables lazy-provider behavior as with available-packages, but may allow much more compact expression of packages covered by this repository. + // Over-specifying covered packages is safe, but may result in increased traffic to your repository. + if (!empty($data['available-package-regexes'])) { + $this->availablePackageRegexes = $data['available-package-regexes']; + $this->hasPredefinedPackageCoverage = true; } // Remove legacy keys as most repos need to be compatible with Composer v1 @@ -1306,4 +1318,36 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito // wipe rootData as it is fully consumed at this point and this saves some memory $this->rootData = true; } + + /** + * Returns true only if: + * - this repository uses lazy providers + * - this repository has predefined package coverage + * - the name passed is not among the known provider names + * + * @param $name + * @return bool + */ + protected function lazyProvidersRepoDoesNotContain($name) { + if (! $this->hasPredefinedPackageCoverage) { + return false; + } + + $ruledOutByExactMatch = $ruledOutByRegex = true; + + if (is_array($this->availablePackages)) { + $ruledOutByExactMatch = !isset($this->availablePackages[$name]); + } + + if (is_array($this->availablePackageRegexes)) { + foreach ($this->availablePackageRegexes as $providerRegex) { + if (preg_match(BasePackage::packageNameToRegexp($providerRegex), $name) == 1) { + $ruledOutByRegex = false; + break; + } + } + } + + return $ruledOutByExactMatch && $ruledOutByRegex; + } }