123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- <?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\DependencyResolver;
- use Composer\Package\PackageInterface;
- use Composer\Package\AliasPackage;
- use Composer\Repository\PlatformRepository;
- /**
- * @author Nils Adermann <naderman@naderman.de>
- */
- class RuleSetGenerator
- {
- protected $policy;
- protected $pool;
- protected $rules;
- protected $addedMap;
- protected $conflictAddedMap;
- protected $addedPackages;
- protected $addedPackagesByNames;
- public function __construct(PolicyInterface $policy, Pool $pool)
- {
- $this->policy = $policy;
- $this->pool = $pool;
- }
- /**
- * Creates a new rule for the requirements of a package
- *
- * This rule is of the form (-A|B|C), where B and C are the providers of
- * one requirement of the package A.
- *
- * @param PackageInterface $package The package with a requirement
- * @param array $providers The providers of the requirement
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the requirement name,
- * that goes with the reason
- * @return Rule|null The generated rule or null if tautological
- */
- protected function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
- {
- $literals = array(-$package->id);
- foreach ($providers as $provider) {
- // self fulfilling rule?
- if ($provider === $package) {
- return null;
- }
- $literals[] = $provider->id;
- }
- return new GenericRule($literals, $reason, $reasonData);
- }
- /**
- * Creates a rule to install at least one of a set of packages
- *
- * The rule is (A|B|C) with A, B and C different packages. If the given
- * set of packages is empty an impossible rule is generated.
- *
- * @param array $packages The set of packages to choose from
- * @param int $reason A RULE_* constant describing the reason for
- * generating this rule
- * @param array $job The job this rule was created from
- * @return Rule The generated rule
- */
- protected function createInstallOneOfRule(array $packages, $reason, $job)
- {
- $literals = array();
- foreach ($packages as $package) {
- $literals[] = $package->id;
- }
- return new GenericRule($literals, $reason, $job['packageName'], $job);
- }
- /**
- * Creates a rule to remove a package
- *
- * The rule for a package A is (-A).
- *
- * @param PackageInterface $package The package to be removed
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param array $job The job this rule was created from
- * @return Rule The generated rule
- */
- protected function createRemoveRule(PackageInterface $package, $reason, $job)
- {
- return new GenericRule(array(-$package->id), $reason, $job['packageName'], $job);
- }
- /**
- * Creates a rule for two conflicting packages
- *
- * The rule for conflicting packages A and B is (-A|-B). A is called the issuer
- * and B the provider.
- *
- * @param PackageInterface $issuer The package declaring the conflict
- * @param PackageInterface $provider The package causing the conflict
- * @param int $reason A RULE_* constant describing the
- * reason for generating this rule
- * @param mixed $reasonData Any data, e.g. the package name, that
- * goes with the reason
- * @return Rule|null The generated rule
- */
- protected function createRule2Literals(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
- {
- // ignore self conflict
- if ($issuer === $provider) {
- return null;
- }
- return new Rule2Literals(-$issuer->id, -$provider->id, $reason, $reasonData);
- }
- /**
- * Adds a rule unless it duplicates an existing one of any type
- *
- * To be able to directly pass in the result of one of the rule creation
- * methods null is allowed which will not insert a rule.
- *
- * @param int $type A TYPE_* constant defining the rule type
- * @param Rule $newRule The rule about to be added
- */
- private function addRule($type, Rule $newRule = null)
- {
- if (!$newRule) {
- return;
- }
- $this->rules->add($newRule, $type);
- }
- protected function addRulesForPackage(PackageInterface $package, $ignorePlatformReqs)
- {
- $workQueue = new \SplQueue;
- $workQueue->enqueue($package);
- while (!$workQueue->isEmpty()) {
- /** @var PackageInterface $package */
- $package = $workQueue->dequeue();
- if (isset($this->addedMap[$package->id])) {
- continue;
- }
- $this->addedMap[$package->id] = true;
- $this->addedPackages[] = $package;
- foreach ($package->getNames() as $name) {
- $this->addedPackagesByNames[$name][] = $package;
- }
- foreach ($package->getRequires() as $link) {
- if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
- continue;
- }
- $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
- $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, Rule::RULE_PACKAGE_REQUIRES, $link));
- foreach ($possibleRequires as $require) {
- $workQueue->enqueue($require);
- }
- }
- $packageName = $package->getName();
- $obsoleteProviders = $this->pool->whatProvides($packageName, null);
- foreach ($obsoleteProviders as $provider) {
- if ($provider === $package) {
- continue;
- }
- if (($package instanceof AliasPackage) && $package->getAliasOf() === $provider) {
- $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, array($provider), Rule::RULE_PACKAGE_ALIAS, $package));
- } elseif (!$this->obsoleteImpossibleForAlias($package, $provider)) {
- $reason = ($packageName == $provider->getName()) ? Rule::RULE_PACKAGE_SAME_NAME : Rule::RULE_PACKAGE_IMPLICIT_OBSOLETES;
- $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $package));
- }
- }
- }
- }
- protected function addConflictRules()
- {
- /** @var PackageInterface $package */
- foreach ($this->addedPackages as $package) {
- foreach ($package->getConflicts() as $link) {
- if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
- continue;
- }
- /** @var PackageInterface $possibleConflict */
- foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
- $conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true);
- if ($conflictMatch === Pool::MATCH || $conflictMatch === Pool::MATCH_REPLACE) {
- $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $possibleConflict, Rule::RULE_PACKAGE_CONFLICT, $link));
- }
- }
- }
- // check obsoletes and implicit obsoletes of a package
- foreach ($package->getReplaces() as $link) {
- if (!isset($this->addedPackagesByNames[$link->getTarget()])) {
- continue;
- }
- /** @var PackageInterface $possibleConflict */
- foreach ($this->addedPackagesByNames[$link->getTarget()] as $provider) {
- if ($provider === $package) {
- continue;
- }
- if (!$this->obsoleteImpossibleForAlias($package, $provider)) {
- $reason = Rule::RULE_PACKAGE_OBSOLETES;
- $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRule2Literals($package, $provider, $reason, $link));
- }
- }
- }
- }
- }
- protected function obsoleteImpossibleForAlias($package, $provider)
- {
- $packageIsAlias = $package instanceof AliasPackage;
- $providerIsAlias = $provider instanceof AliasPackage;
- $impossible = (
- ($packageIsAlias && $package->getAliasOf() === $provider) ||
- ($providerIsAlias && $provider->getAliasOf() === $package) ||
- ($packageIsAlias && $providerIsAlias && $provider->getAliasOf() === $package->getAliasOf())
- );
- return $impossible;
- }
- protected function addRulesForRequest($request, $ignorePlatformReqs)
- {
- foreach ($request->getFixedPackages() as $package) {
- $this->addRulesForPackage($package, $ignorePlatformReqs);
- $rule = $this->createInstallOneOfRule(array($package), Rule::RULE_JOB_INSTALL, array(
- 'cmd' => 'fix',
- 'packageName' => $package->getName(),
- 'constraint' => null,
- 'fixed' => true
- ));
- $this->addRule(RuleSet::TYPE_JOB, $rule);
- }
- foreach ($request->getJobs() as $job) {
- switch ($job['cmd']) {
- case 'install':
- if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $job['packageName'])) {
- break;
- }
- $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
- if ($packages) {
- foreach ($packages as $package) {
- $this->addRulesForPackage($package, $ignorePlatformReqs);
- }
- $rule = $this->createInstallOneOfRule($packages, Rule::RULE_JOB_INSTALL, $job);
- $this->addRule(RuleSet::TYPE_JOB, $rule);
- }
- break;
- case 'remove':
- // remove all packages with this name including uninstalled
- // ones to make sure none of them are picked as replacements
- $packages = $this->pool->whatProvides($job['packageName'], $job['constraint']);
- foreach ($packages as $package) {
- $rule = $this->createRemoveRule($package, Rule::RULE_JOB_REMOVE, $job);
- $this->addRule(RuleSet::TYPE_JOB, $rule);
- }
- break;
- }
- }
- }
- public function getRulesFor(Request $request, $ignorePlatformReqs = false)
- {
- $this->rules = new RuleSet;
- $this->addedMap = array();
- $this->conflictAddedMap = array();
- $this->addedPackages = array();
- $this->addedPackagesByNames = array();
- $this->addRulesForRequest($request, $ignorePlatformReqs);
- $this->addConflictRules();
- // Remove references to packages
- $this->addedPackages = $this->addedPackagesByNames = null;
- return $this->rules;
- }
- }
|