1
0
Fork 0

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.
pull/9197/head
Mike Baynton 2020-09-09 21:15:36 -05:00
parent 0408207e35
commit 66a767c065
1 changed files with 50 additions and 6 deletions

View File

@ -12,6 +12,7 @@
namespace Composer\Repository; namespace Composer\Repository;
use Composer\Package\BasePackage;
use Composer\Package\Loader\ArrayLoader; use Composer\Package\Loader\ArrayLoader;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage; use Composer\Package\AliasPackage;
@ -55,7 +56,10 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
protected $hasProviders = false; protected $hasProviders = false;
protected $providersUrl; protected $providersUrl;
protected $listUrl; 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 $availablePackages;
protected $availablePackageRegexes;
protected $lazyProvidersUrl; protected $lazyProvidersUrl;
protected $providerListing; protected $providerListing;
protected $loader; protected $loader;
@ -162,7 +166,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
return $this->filterPackages($this->whatProvides($name), $constraint, true); return $this->filterPackages($this->whatProvides($name), $constraint, true);
} }
if (is_array($this->availablePackages) && !isset($this->availablePackages[$name])) { if ($this->lazyProvidersRepoDoesNotContain($name)) {
return; return;
} }
@ -202,7 +206,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
return $this->filterPackages($this->whatProvides($name), $constraint); return $this->filterPackages($this->whatProvides($name), $constraint);
} }
if (is_array($this->availablePackages) && !isset($this->availablePackages[$name])) { if ($this->lazyProvidersRepoDoesNotContain($name)) {
return array(); return array();
} }
@ -260,7 +264,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$hasProviders = $this->hasProviders(); $hasProviders = $this->hasProviders();
if ($this->lazyProvidersUrl) { if ($this->lazyProvidersUrl) {
if (is_array($this->availablePackages)) { if (is_array($this->availablePackages) && count($this->availablePackageRegexes) == 0) {
$packageMap = array(); $packageMap = array();
foreach ($this->availablePackages as $name) { foreach ($this->availablePackages as $name) {
$packageMap[$name] = new MatchAllConstraint(); $packageMap[$name] = new MatchAllConstraint();
@ -388,10 +392,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
} }
if ($this->lazyProvidersUrl && count($packageNameMap)) { if ($this->lazyProvidersUrl && count($packageNameMap)) {
if (is_array($this->availablePackages)) { if ($this->hasPredefinedPackageCoverage) {
$availPackages = $this->availablePackages;
foreach ($packageNameMap as $name => $constraint) { foreach ($packageNameMap as $name => $constraint) {
if (!isset($availPackages[strtolower($name)])) { if ($this->lazyProvidersRepoDoesNotContain(strtolower($name))) {
unset($packageNameMap[$name]); unset($packageNameMap[$name]);
} }
} }
@ -876,6 +879,15 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
if (!empty($data['available-packages'])) { if (!empty($data['available-packages'])) {
$availPackages = array_map('strtolower', $data['available-packages']); $availPackages = array_map('strtolower', $data['available-packages']);
$this->availablePackages = array_combine($availPackages, $availPackages); $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 // 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 // wipe rootData as it is fully consumed at this point and this saves some memory
$this->rootData = true; $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;
}
} }