1
0
Fork 0

Remove callback and pass stabilities all the way instead

This allows optimizing the loading of ~dev files, and cleans up a few things
pull/8533/head
Jordi Boggiano 2020-01-16 07:58:50 +01:00
parent 98860b8619
commit 304753ff69
No known key found for this signature in database
GPG Key ID: 7BBD42C429EC80BC
8 changed files with 94 additions and 73 deletions

View File

@ -25,10 +25,11 @@ use Composer\Semver\Constraint\MultiConstraint;
*/ */
class PoolBuilder class PoolBuilder
{ {
private $isPackageAcceptableCallable; private $acceptableStabilities;
private $rootRequires; private $stabilityFlags;
private $rootAliases; private $rootAliases;
private $rootReferences; private $rootReferences;
private $rootRequires;
private $aliasMap = array(); private $aliasMap = array();
private $nameConstraints = array(); private $nameConstraints = array();
@ -37,17 +38,18 @@ class PoolBuilder
private $packages = array(); private $packages = array();
public function __construct($isPackageAcceptableCallable, array $rootRequires = array()) public function __construct(array $acceptableStabilities, array $stabilityFlags, array $rootAliases, array $rootReferences, array $rootRequires = array())
{ {
$this->isPackageAcceptableCallable = $isPackageAcceptableCallable; $this->acceptableStabilities = $acceptableStabilities;
$this->stabilityFlags = $stabilityFlags;
$this->rootAliases = $rootAliases;
$this->rootReferences = $rootReferences;
$this->rootRequires = $rootRequires; $this->rootRequires = $rootRequires;
} }
public function buildPool(array $repositories, array $rootAliases, array $rootReferences, Request $request) public function buildPool(array $repositories, Request $request)
{ {
$pool = new Pool(); $pool = new Pool();
$this->rootAliases = $rootAliases;
$this->rootReferences = $rootReferences;
// TODO do we really want the request here? kind of want a root requirements thingy instead // TODO do we really want the request here? kind of want a root requirements thingy instead
$loadNames = array(); $loadNames = array();
@ -87,17 +89,14 @@ class PoolBuilder
continue; continue;
} }
// TODO should we really pass the callable into here? $result = $repository->loadPackages($loadNames, $this->acceptableStabilities, $this->stabilityFlags);
$result = $repository->loadPackages($loadNames, $this->isPackageAcceptableCallable);
foreach ($result['namesFound'] as $name) { foreach ($result['namesFound'] as $name) {
// avoid loading the same package again from other repositories once it has been found // avoid loading the same package again from other repositories once it has been found
unset($loadNames[$name]); unset($loadNames[$name]);
} }
foreach ($result['packages'] as $package) { foreach ($result['packages'] as $package) {
if (call_user_func($this->isPackageAcceptableCallable, $package->getNames(), $package->getStability())) { $newLoadNames += $this->loadPackage($request, $package);
$newLoadNames += $this->loadPackage($request, $package);
}
} }
} }

View File

@ -364,19 +364,6 @@ class Installer
$request = $this->createRequest($this->fixedRootPackage, $platformRepo, $lockedRepository); $request = $this->createRequest($this->fixedRootPackage, $platformRepo, $lockedRepository);
if ($lockedRepository) {
// TODO do we really always need this? Maybe only to skip fix() in updateWhitelist case cause these packages get removed on full update automatically?
foreach ($lockedRepository->getPackages() as $lockedPackage) {
if (!$repositorySet->isPackageAcceptable($lockedPackage->getNames(), $lockedPackage->getStability())) {
$constraint = new Constraint('=', $lockedPackage->getVersion());
$constraint->setPrettyString('(stability not acceptable)');
// if we can get rid of this remove() here, we can generally get rid of remove support in the request
$request->remove($lockedPackage->getName(), $constraint);
}
}
}
$this->io->writeError('<info>Updating dependencies</info>'); $this->io->writeError('<info>Updating dependencies</info>');
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); $links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
@ -393,10 +380,11 @@ class Installer
} }
// if the updateWhitelist is enabled, packages not in it are also fixed // if the updateWhitelist is enabled, packages not in it are also fixed
// to the version specified in the lock // to the version specified in the lock, except if their stability is not
if ($this->updateWhitelist) { // acceptable anymore, to make sure that they get updated/downgraded to
// a working version
if ($this->updateWhitelist && $lockedRepository) {
foreach ($lockedRepository->getPackages() as $lockedPackage) { foreach ($lockedRepository->getPackages() as $lockedPackage) {
// TODO should this really be checking acceptability here?
if (!$this->isUpdateable($lockedPackage) && $repositorySet->isPackageAcceptable($lockedPackage->getNames(), $lockedPackage->getStability())) { if (!$this->isUpdateable($lockedPackage) && $repositorySet->isPackageAcceptable($lockedPackage->getNames(), $lockedPackage->getStability())) {
// TODO add reason for fix? // TODO add reason for fix?
$request->fixPackage($lockedPackage); $request->fixPackage($lockedPackage);

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Package\Version;
use Composer\Package\BasePackage;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class StabilityFilter
{
/**
* Checks if any of the provided package names in the given stability match the configured acceptable stability and flags
*
* @return bool true if any package name is acceptable
*/
public static function isPackageAcceptable(array $acceptableStabilities, array $stabilityFlags, $names, $stability)
{
foreach ($names as $name) {
// allow if package matches the package-specific stability flag
if (isset($stabilityFlags[$name])) {
if (BasePackage::$stabilities[$stability] <= $stabilityFlags[$name]) {
return true;
}
} elseif (isset($acceptableStabilities[$stability])) {
// allow if package matches the global stability requirement and has no exception
return true;
}
}
return false;
}
}

View File

@ -16,6 +16,7 @@ use Composer\Package\AliasPackage;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Package\CompletePackageInterface; use Composer\Package\CompletePackageInterface;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Package\Version\StabilityFilter;
use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Semver\Constraint\Constraint; use Composer\Semver\Constraint\Constraint;
@ -44,7 +45,7 @@ class ArrayRepository extends BaseRepository
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function loadPackages(array $packageMap, $isPackageAcceptableCallable) public function loadPackages(array $packageMap, array $acceptableStabilities, array $stabilityFlags)
{ {
$packages = $this->getPackages(); $packages = $this->getPackages();
@ -54,7 +55,7 @@ class ArrayRepository extends BaseRepository
if (array_key_exists($package->getName(), $packageMap)) { if (array_key_exists($package->getName(), $packageMap)) {
if ( if (
(!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion()))) (!$packageMap[$package->getName()] || $packageMap[$package->getName()]->matches(new Constraint('==', $package->getVersion())))
&& call_user_func($isPackageAcceptableCallable, $package->getNames(), $package->getStability()) && StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, $package->getNames(), $package->getStability())
) { ) {
$result[spl_object_hash($package)] = $package; $result[spl_object_hash($package)] = $package;
if ($package instanceof AliasPackage && !isset($result[spl_object_hash($package->getAliasOf())])) { if ($package instanceof AliasPackage && !isset($result[spl_object_hash($package->getAliasOf())])) {

View File

@ -16,6 +16,7 @@ use Composer\Package\Loader\ArrayLoader;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Package\AliasPackage; use Composer\Package\AliasPackage;
use Composer\Package\Version\VersionParser; use Composer\Package\Version\VersionParser;
use Composer\Package\Version\StabilityFilter;
use Composer\Json\JsonFile; use Composer\Json\JsonFile;
use Composer\Cache; use Composer\Cache;
use Composer\Config; use Composer\Config;
@ -292,13 +293,13 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
return $names; return $names;
} }
public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable) public function loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags)
{ {
// this call initializes loadRootServerFile which is needed for the rest below to work // this call initializes loadRootServerFile which is needed for the rest below to work
$hasProviders = $this->hasProviders(); $hasProviders = $this->hasProviders();
if (!$hasProviders && !$this->hasPartialPackages() && !$this->lazyProvidersUrl) { if (!$hasProviders && !$this->hasPartialPackages() && !$this->lazyProvidersUrl) {
return parent::loadPackages($packageNameMap, $isPackageAcceptableCallable); return parent::loadPackages($packageNameMap, $acceptableStabilities, $stabilityFlags);
} }
$packages = array(); $packages = array();
@ -314,7 +315,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
continue; continue;
} }
$candidates = $this->whatProvides($name, $isPackageAcceptableCallable); $candidates = $this->whatProvides($name, $acceptableStabilities, $stabilityFlags);
foreach ($candidates as $candidate) { foreach ($candidates as $candidate) {
if ($candidate->getName() !== $name) { if ($candidate->getName() !== $name) {
throw new \LogicException('whatProvides should never return a package with a different name than the requested one'); throw new \LogicException('whatProvides should never return a package with a different name than the requested one');
@ -350,7 +351,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
}, ARRAY_FILTER_USE_KEY); }, ARRAY_FILTER_USE_KEY);
} }
$result = $this->loadAsyncPackages($packageNameMap, $isPackageAcceptableCallable); $result = $this->loadAsyncPackages($packageNameMap, $acceptableStabilities, $stabilityFlags);
$packages = array_merge($packages, $result['packages']); $packages = array_merge($packages, $result['packages']);
$namesFound = array_merge($namesFound, $result['namesFound']); $namesFound = array_merge($namesFound, $result['namesFound']);
} }
@ -444,7 +445,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
* @param callable $isPackageAcceptableCallable * @param callable $isPackageAcceptableCallable
* @return array|mixed * @return array|mixed
*/ */
private function whatProvides($name, $isPackageAcceptableCallable = null) private function whatProvides($name, array $acceptableStabilities = null, array $stabilityFlags = null)
{ {
if (!$this->hasPartialPackages() || !isset($this->partialPackagesByName[$name])) { if (!$this->hasPartialPackages() || !isset($this->partialPackagesByName[$name])) {
// skip platform packages, root package and composer-plugin-api // skip platform packages, root package and composer-plugin-api
@ -533,7 +534,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$version['version_normalized'] = $this->versionParser->normalize($version['version']); $version['version_normalized'] = $this->versionParser->normalize($version['version']);
} }
if ($this->isVersionAcceptable($isPackageAcceptableCallable, null, $normalizedName, $version)) { if ($this->isVersionAcceptable($acceptableStabilities, $stabilityFlags, null, $normalizedName, $version)) {
$versionsToLoad[$version['uid']] = $version; $versionsToLoad[$version['uid']] = $version;
} }
} }
@ -590,7 +591,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
/** /**
* @param array $packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded * @param array $packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded
*/ */
private function loadAsyncPackages(array $packageNames, $isPackageAcceptableCallable = null) private function loadAsyncPackages(array $packageNames, array $acceptableStabilities = null, array $stabilityFlags = null)
{ {
$this->loadRootServerFile(); $this->loadRootServerFile();
@ -603,11 +604,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
throw new \LogicException('loadAsyncPackages only supports v2 protocol composer repos with a metadata-url'); throw new \LogicException('loadAsyncPackages only supports v2 protocol composer repos with a metadata-url');
} }
// load ~dev variants as well if present // load ~dev versions of the packages as well if needed
// TODO ideally there should be a flag set from the repositoryset/poolbuilder to know which packages should have the dev packages loaded
// so we can optimize away some requests entirely
foreach ($packageNames as $name => $constraint) { foreach ($packageNames as $name => $constraint) {
$packageNames[$name.'~dev'] = $constraint; if ($acceptableStabilities && $stabilityFlags && StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), 'dev')) {
$packageNames[$name.'~dev'] = $constraint;
}
} }
foreach ($packageNames as $name => $constraint) { foreach ($packageNames as $name => $constraint) {
@ -629,7 +630,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
} }
$promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified) $promises[] = $this->asyncFetchFile($url, $cacheKey, $lastModified)
->then(function ($response) use (&$packages, &$namesFound, $contents, $realName, $constraint, $repo, $isPackageAcceptableCallable) { ->then(function ($response) use (&$packages, &$namesFound, $contents, $realName, $constraint, $repo, $acceptableStabilities, $stabilityFlags) {
if (true === $response) { if (true === $response) {
$response = $contents; $response = $contents;
} }
@ -651,7 +652,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
$version['version_normalized'] = $repo->versionParser->normalize($version['version']); $version['version_normalized'] = $repo->versionParser->normalize($version['version']);
} }
if ($repo->isVersionAcceptable($isPackageAcceptableCallable, $constraint, $realName, $version)) { if ($repo->isVersionAcceptable($acceptableStabilities, $stabilityFlags, $constraint, $realName, $version)) {
$versionsToLoad[] = $version; $versionsToLoad[] = $version;
} }
} }
@ -681,7 +682,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
* @param string $name package name (must be lowercased already) * @param string $name package name (must be lowercased already)
* @private * @private
*/ */
public function isVersionAcceptable($isPackageAcceptableCallable, $constraint, $name, $versionData) public function isVersionAcceptable(array $acceptableStabilities = null, array $stabilityFlags = null, $constraint = null, $name, $versionData)
{ {
$versions = array($versionData['version_normalized']); $versions = array($versionData['version_normalized']);
@ -690,7 +691,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
} }
foreach ($versions as $version) { foreach ($versions as $version) {
if ($isPackageAcceptableCallable && !call_user_func($isPackageAcceptableCallable, $name, VersionParser::parseStability($version))) { if ($acceptableStabilities && $stabilityFlags && !StabilityFilter::isPackageAcceptable($acceptableStabilities, $stabilityFlags, array($name), VersionParser::parseStability($version))) {
continue; continue;
} }

View File

@ -97,13 +97,13 @@ class CompositeRepository extends BaseRepository
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function loadPackages(array $packageMap, $isPackageAcceptableCallable) public function loadPackages(array $packageMap, array $acceptableStabilities, array $stabilityFlags)
{ {
$packages = array(); $packages = array();
$namesFound = array(); $namesFound = array();
foreach ($this->repositories as $repository) { foreach ($this->repositories as $repository) {
/* @var $repository RepositoryInterface */ /* @var $repository RepositoryInterface */
$result = $repository->findPackages($name, $constraint); $result = $repository->loadPackages($packageMap, $acceptableStabilities, $stabilityFlags);
$packages[] = $result['packages']; $packages[] = $result['packages'];
$namesFound[] = $result['namesFound']; $namesFound[] = $result['namesFound'];
} }

View File

@ -56,7 +56,6 @@ interface RepositoryInterface extends \Countable
*/ */
public function findPackages($name, $constraint = null); public function findPackages($name, $constraint = null);
// TODO this should really not be in this generic interface anymore
/** /**
* Returns list of registered packages. * Returns list of registered packages.
* *
@ -68,10 +67,11 @@ interface RepositoryInterface extends \Countable
* Returns list of registered packages with the supplied name * Returns list of registered packages with the supplied name
* *
* @param ConstraintInterface[] $packageNameMap package names pointing to constraints * @param ConstraintInterface[] $packageNameMap package names pointing to constraints
* @param $isPackageAcceptableCallable * @param array $acceptableStabilities
* @param array $stabilityFlags
* @return array [namesFound => string[], packages => PackageInterface[]] * @return array [namesFound => string[], packages => PackageInterface[]]
*/ */
public function loadPackages(array $packageNameMap, $isPackageAcceptableCallable); public function loadPackages(array $packageNameMap, array $acceptableStabilities, array $stabilityFlags);
/** /**
* Searches the repository for packages containing the query * Searches the repository for packages containing the query

View File

@ -22,7 +22,7 @@ use Composer\Repository\PlatformRepository;
use Composer\Repository\LockArrayRepository; use Composer\Repository\LockArrayRepository;
use Composer\Repository\InstalledRepositoryInterface; use Composer\Repository\InstalledRepositoryInterface;
use Composer\Semver\Constraint\ConstraintInterface; use Composer\Semver\Constraint\ConstraintInterface;
use Composer\Test\DependencyResolver\PoolTest; use Composer\Package\Version\StabilityFilter;
/** /**
* @author Nils Adermann <naderman@naderman.de> * @author Nils Adermann <naderman@naderman.de>
@ -89,23 +89,6 @@ class RepositorySet
} }
} }
public function isPackageAcceptable($name, $stability)
{
foreach ((array) $name as $n) {
// allow if package matches the global stability requirement and has no exception
if (!isset($this->stabilityFlags[$n]) && isset($this->acceptableStabilities[$stability])) {
return true;
}
// allow if package matches the package-specific stability flag
if (isset($this->stabilityFlags[$n]) && BasePackage::$stabilities[$stability] <= $this->stabilityFlags[$n]) {
return true;
}
}
return false;
}
/** /**
* Find packages providing or matching a name and optionally meeting a constraint in all repositories * Find packages providing or matching a name and optionally meeting a constraint in all repositories
* *
@ -113,10 +96,11 @@ class RepositorySet
* *
* @param string $name * @param string $name
* @param ConstraintInterface|null $constraint * @param ConstraintInterface|null $constraint
* @param bool $exactMatch * @param bool $exactMatch if set to false, packages which replace/provide the given name might be returned as well even if they do not match the name exactly
* @param bool $ignoreStability if set to true, packages are returned even though their stability does not match the required stability
* @return array * @return array
*/ */
public function findPackages($name, ConstraintInterface $constraint = null, $exactMatch = true) public function findPackages($name, ConstraintInterface $constraint = null, $exactMatch = true, $ignoreStability = false)
{ {
$packages = array(); $packages = array();
foreach ($this->repositories as $repository) { foreach ($this->repositories as $repository) {
@ -131,7 +115,7 @@ class RepositorySet
continue; continue;
} }
if ($this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) { if (!$ignoreStability && $this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) {
$result[] = $candidate; $result[] = $candidate;
} }
} }
@ -139,6 +123,11 @@ class RepositorySet
return $candidates; return $candidates;
} }
public function isPackageAcceptable($names, $stability)
{
return StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $names, $stability);
}
/** /**
* Create a pool for dependency resolution from the packages in this repository set. * Create a pool for dependency resolution from the packages in this repository set.
* *
@ -146,7 +135,7 @@ class RepositorySet
*/ */
public function createPool(Request $request) public function createPool(Request $request)
{ {
$poolBuilder = new PoolBuilder(array($this, 'isPackageAcceptable'), $this->rootRequires); $poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences, $this->rootRequires);
foreach ($this->repositories as $repo) { foreach ($this->repositories as $repo) {
if ($repo instanceof InstalledRepositoryInterface) { if ($repo instanceof InstalledRepositoryInterface) {
@ -154,7 +143,7 @@ class RepositorySet
} }
} }
return $this->pool = $poolBuilder->buildPool($this->repositories, $this->rootAliases, $this->rootReferences, $request); return $this->pool = $poolBuilder->buildPool($this->repositories, $request);
} }
// TODO unify this with above in some simpler version without "request"? // TODO unify this with above in some simpler version without "request"?