PageRenderTime 77ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Composer/DependencyResolver/Solver.php

https://github.com/cucentric/composer
PHP | 2072 lines | 1181 code | 326 blank | 565 comment | 260 complexity | 41d8f17a318cebe63d143daed426bdc5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\DependencyResolver;
  12. use Composer\Repository\RepositoryInterface;
  13. use Composer\Package\PackageInterface;
  14. use Composer\DependencyResolver\Operation;
  15. /**
  16. * @author Nils Adermann <naderman@naderman.de>
  17. */
  18. class Solver
  19. {
  20. const RULE_INTERNAL_ALLOW_UPDATE = 1;
  21. const RULE_JOB_INSTALL = 2;
  22. const RULE_JOB_REMOVE = 3;
  23. const RULE_JOB_LOCK = 4;
  24. const RULE_NOT_INSTALLABLE = 5;
  25. const RULE_NOTHING_PROVIDES_DEP = 6;
  26. const RULE_PACKAGE_CONFLICT = 7;
  27. const RULE_PACKAGE_NOT_EXIST = 8;
  28. const RULE_PACKAGE_REQUIRES = 9;
  29. const RULE_PACKAGE_OBSOLETES = 10;
  30. const RULE_INSTALLED_PACKAGE_OBSOLETES = 11;
  31. const RULE_PACKAGE_SAME_NAME = 12;
  32. const RULE_PACKAGE_IMPLICIT_OBSOLETES = 13;
  33. const RULE_LEARNED = 14;
  34. protected $policy;
  35. protected $pool;
  36. protected $installed;
  37. protected $rules;
  38. protected $updateAll;
  39. protected $ruleToJob = array();
  40. protected $addedMap = array();
  41. protected $fixMap = array();
  42. protected $updateMap = array();
  43. protected $noObsoletes = array();
  44. protected $watches = array();
  45. protected $removeWatches = array();
  46. protected $decisionMap;
  47. protected $installedPackageMap;
  48. protected $packageToUpdateRule = array();
  49. protected $packageToFeatureRule = array();
  50. public function __construct(PolicyInterface $policy, Pool $pool, RepositoryInterface $installed)
  51. {
  52. $this->policy = $policy;
  53. $this->pool = $pool;
  54. $this->installed = $installed;
  55. $this->rules = new RuleSet;
  56. }
  57. /**
  58. * Creates a new rule for the requirements of a package
  59. *
  60. * This rule is of the form (-A|B|C), where B and C are the providers of
  61. * one requirement of the package A.
  62. *
  63. * @param PackageInterface $package The package with a requirement
  64. * @param array $providers The providers of the requirement
  65. * @param int $reason A RULE_* constant describing the
  66. * reason for generating this rule
  67. * @param mixed $reasonData Any data, e.g. the requirement name,
  68. * that goes with the reason
  69. * @return Rule The generated rule or null if tautological
  70. */
  71. public function createRequireRule(PackageInterface $package, array $providers, $reason, $reasonData = null)
  72. {
  73. $literals = array(new Literal($package, false));
  74. foreach ($providers as $provider) {
  75. // self fulfilling rule?
  76. if ($provider === $package) {
  77. return null;
  78. }
  79. $literals[] = new Literal($provider, true);
  80. }
  81. return new Rule($literals, $reason, $reasonData);
  82. }
  83. /**
  84. * Create a new rule for updating a package
  85. *
  86. * If package A1 can be updated to A2 or A3 the rule is (A1|A2|A3).
  87. *
  88. * @param PackageInterface $package The package to be updated
  89. * @param array $updates An array of update candidate packages
  90. * @param int $reason A RULE_* constant describing the
  91. * reason for generating this rule
  92. * @param mixed $reasonData Any data, e.g. the package name, that
  93. * goes with the reason
  94. * @return Rule The generated rule or null if tautology
  95. */
  96. protected function createUpdateRule(PackageInterface $package, array $updates, $reason, $reasonData = null)
  97. {
  98. $literals = array(new Literal($package, true));
  99. foreach ($updates as $update) {
  100. $literals[] = new Literal($update, true);
  101. }
  102. return new Rule($literals, $reason, $reasonData);
  103. }
  104. /**
  105. * Creates a new rule for installing a package
  106. *
  107. * The rule is simply (A) for a package A to be installed.
  108. *
  109. * @param PackageInterface $package The package to be installed
  110. * @param int $reason A RULE_* constant describing the
  111. * reason for generating this rule
  112. * @param mixed $reasonData Any data, e.g. the package name, that
  113. * goes with the reason
  114. * @return Rule The generated rule
  115. */
  116. public function createInstallRule(PackageInterface $package, $reason, $reasonData = null)
  117. {
  118. return new Rule(new Literal($package, true));
  119. }
  120. /**
  121. * Creates a rule to install at least one of a set of packages
  122. *
  123. * The rule is (A|B|C) with A, B and C different packages. If the given
  124. * set of packages is empty an impossible rule is generated.
  125. *
  126. * @param array $packages The set of packages to choose from
  127. * @param int $reason A RULE_* constant describing the reason for
  128. * generating this rule
  129. * @param mixed $reasonData Any data, e.g. the package name, that goes with
  130. * the reason
  131. * @return Rule The generated rule
  132. */
  133. public function createInstallOneOfRule(array $packages, $reason, $reasonData = null)
  134. {
  135. if (empty($packages)) {
  136. return $this->createImpossibleRule($reason, $reasonData);
  137. }
  138. $literals = array();
  139. foreach ($packages as $package) {
  140. $literals[] = new Literal($package, true);
  141. }
  142. return new Rule($literals, $reason, $reasonData);
  143. }
  144. /**
  145. * Creates a rule to remove a package
  146. *
  147. * The rule for a package A is (-A).
  148. *
  149. * @param PackageInterface $package The package to be removed
  150. * @param int $reason A RULE_* constant describing the
  151. * reason for generating this rule
  152. * @param mixed $reasonData Any data, e.g. the package name, that
  153. * goes with the reason
  154. * @return Rule The generated rule
  155. */
  156. public function createRemoveRule(PackageInterface $package, $reason, $reasonData = null)
  157. {
  158. return new Rule(array(new Literal($package, false)), $reason, $reasonData);
  159. }
  160. /**
  161. * Creates a rule for two conflicting packages
  162. *
  163. * The rule for conflicting packages A and B is (-A|-B). A is called the issuer
  164. * and B the provider.
  165. *
  166. * @param PackageInterface $issuer The package declaring the conflict
  167. * @param Package $provider The package causing the conflict
  168. * @param int $reason A RULE_* constant describing the
  169. * reason for generating this rule
  170. * @param mixed $reasonData Any data, e.g. the package name, that
  171. * goes with the reason
  172. * @return Rule The generated rule
  173. */
  174. public function createConflictRule(PackageInterface $issuer, PackageInterface $provider, $reason, $reasonData = null)
  175. {
  176. // ignore self conflict
  177. if ($issuer === $provider) {
  178. return null;
  179. }
  180. return new Rule(array(new Literal($issuer, false), new Literal($provider, false)), $reason, $reasonData);
  181. }
  182. /**
  183. * Intentionally creates a rule impossible to solve
  184. *
  185. * The rule is an empty one so it can never be satisfied.
  186. *
  187. * @param int $reason A RULE_* constant describing the reason for
  188. * generating this rule
  189. * @param mixed $reasonData Any data, e.g. the package name, that goes with
  190. * the reason
  191. * @return Rule An empty rule
  192. */
  193. public function createImpossibleRule($reason, $reasonData = null)
  194. {
  195. return new Rule(array(), $reason, $reasonData);
  196. }
  197. /**
  198. * Adds a rule unless it duplicates an existing one of any type
  199. *
  200. * To be able to directly pass in the result of one of the rule creation
  201. * methods the rule may also be null to indicate that no rule should be
  202. * added.
  203. *
  204. * @param int $type A TYPE_* constant defining the rule type
  205. * @param Rule $newRule The rule about to be added
  206. */
  207. private function addRule($type, Rule $newRule = null) {
  208. if ($newRule) {
  209. foreach ($this->rules->getIterator() as $rule) {
  210. if ($rule->equals($newRule)) {
  211. return;
  212. }
  213. }
  214. $this->rules->add($newRule, $type);
  215. }
  216. }
  217. public function addRulesForPackage(PackageInterface $package)
  218. {
  219. $workQueue = new \SPLQueue;
  220. $workQueue->enqueue($package);
  221. while (!$workQueue->isEmpty()) {
  222. $package = $workQueue->dequeue();
  223. if (isset($this->addedMap[$package->getId()])) {
  224. continue;
  225. }
  226. $this->addedMap[$package->getId()] = true;
  227. $dontFix = 0;
  228. if (isset($this->installedPackageMap[$package->getId()]) && !isset($this->fixMap[$package->getId()])) {
  229. $dontFix = 1;
  230. }
  231. if (!$dontFix && !$this->policy->installable($this, $this->pool, $this->installed, $package)) {
  232. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRemoveRule($package, self::RULE_NOT_INSTALLABLE, (string) $package));
  233. continue;
  234. }
  235. foreach ($package->getRequires() as $link) {
  236. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  237. // the strategy here is to not insist on dependencies
  238. // that are already broken. so if we find one provider
  239. // that was already installed, we know that the
  240. // dependency was not broken before so we enforce it
  241. if ($dontFix) {
  242. $foundInstalled = false;
  243. foreach ($possibleRequires as $require) {
  244. if (isset($this->installedPackageMap[$require->getId()])) {
  245. $foundInstalled = true;
  246. break;
  247. }
  248. }
  249. // no installed provider found: previously broken dependency => don't add rule
  250. if (!$foundInstalled) {
  251. continue;
  252. }
  253. }
  254. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createRequireRule($package, $possibleRequires, self::RULE_PACKAGE_REQUIRES, (string) $link));
  255. foreach ($possibleRequires as $require) {
  256. $workQueue->enqueue($require);
  257. }
  258. }
  259. foreach ($package->getConflicts() as $link) {
  260. $possibleConflicts = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  261. foreach ($possibleConflicts as $conflict) {
  262. if ($dontFix && isset($this->installedPackageMap[$conflict->getId()])) {
  263. continue;
  264. }
  265. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $conflict, self::RULE_PACKAGE_CONFLICT, (string) $link));
  266. }
  267. }
  268. // check obsoletes and implicit obsoletes of a package
  269. // if ignoreinstalledsobsoletes is not set, we're also checking
  270. // obsoletes of installed packages (like newer rpm versions)
  271. //
  272. /** @TODO: if ($this->noInstalledObsoletes) */
  273. if (true) {
  274. $noObsoletes = isset($this->noObsoletes[$package->getId()]);
  275. $isInstalled = (isset($this->installedPackageMap[$package->getId()]));
  276. foreach ($package->getReplaces() as $link) {
  277. $obsoleteProviders = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  278. foreach ($obsoleteProviders as $provider) {
  279. if ($provider === $package) {
  280. continue;
  281. }
  282. if ($isInstalled && $dontFix && $this->installed === $provider->getRepository()) {
  283. continue; // don't repair installed/installed problems
  284. }
  285. $reason = ($isInstalled) ? self::RULE_INSTALLED_PACKAGE_OBSOLETES : self::RULE_PACKAGE_OBSOLETES;
  286. $this->addRule(RuleSet::TYPE_PACKAGE, $this->createConflictRule($package, $provider, $reason, (string) $link));
  287. }
  288. }
  289. // check implicit obsoletes
  290. // for installed packages we only need to check installed/installed problems (and
  291. // only when dontFix is not set), as the others are picked up when looking at the
  292. // uninstalled package.
  293. if (!$isInstalled || !$dontFix) {
  294. $obsoleteProviders = $this->pool->whatProvides($package->getName(), null);
  295. foreach ($obsoleteProviders as $provider) {
  296. if ($provider === $package) {
  297. continue;
  298. }
  299. if ($isInstalled && $this->installed !== $provider->getRepository()) {
  300. continue;
  301. }
  302. // obsolete same packages even when noObsoletes
  303. if ($noObsoletes && (!$package->equals($provider))) {
  304. continue;
  305. }
  306. $reason = ($package->getName() == $provider->getName()) ? self::RULE_PACKAGE_SAME_NAME : self::RULE_PACKAGE_IMPLICIT_OBSOLETES;
  307. $this->addRule(RuleSet::TYPE_PACKAGE, $rule = $this->createConflictRule($package, $provider, $reason, (string) $package));
  308. }
  309. }
  310. }
  311. foreach ($package->getRecommends() as $link) {
  312. foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $recommend) {
  313. $workQueue->enqueue($recommend);
  314. }
  315. }
  316. foreach ($package->getSuggests() as $link) {
  317. foreach ($this->pool->whatProvides($link->getTarget(), $link->getConstraint()) as $suggest) {
  318. $workQueue->enqueue($suggest);
  319. }
  320. }
  321. }
  322. }
  323. /**
  324. * Adds all rules for all update packages of a given package
  325. *
  326. * @param PackageInterface $package Rules for this package's updates are to
  327. * be added
  328. * @param bool $allowAll Whether downgrades are allowed
  329. */
  330. private function addRulesForUpdatePackages(PackageInterface $package, $allowAll)
  331. {
  332. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, $allowAll);
  333. $this->addRulesForPackage($package);
  334. foreach ($updates as $update) {
  335. $this->addRulesForPackage($update);
  336. }
  337. }
  338. /**
  339. * Alters watch chains for a rule.
  340. *
  341. * Next1/2 always points to the next rule that is watching the same package.
  342. * The watches array contains rules to start from for each package
  343. *
  344. */
  345. private function addWatchesToRule(Rule $rule)
  346. {
  347. // skip simple assertions of the form (A) or (-A)
  348. if ($rule->isAssertion()) {
  349. return;
  350. }
  351. if (!isset($this->watches[$rule->watch1])) {
  352. $this->watches[$rule->watch1] = null;
  353. }
  354. $rule->next1 = $this->watches[$rule->watch1];
  355. $this->watches[$rule->watch1] = $rule;
  356. if (!isset($this->watches[$rule->watch2])) {
  357. $this->watches[$rule->watch2] = null;
  358. }
  359. $rule->next2 = $this->watches[$rule->watch2];
  360. $this->watches[$rule->watch2] = $rule;
  361. }
  362. /**
  363. * Put watch2 on rule's literal with highest level
  364. */
  365. private function watch2OnHighest(Rule $rule)
  366. {
  367. $literals = $rule->getLiterals();
  368. // if there are only 2 elements, both are being watched anyway
  369. if ($literals < 3) {
  370. return;
  371. }
  372. $watchLevel = 0;
  373. foreach ($literals as $literal) {
  374. $level = abs($this->decisionMap[$literal->getPackageId()]);
  375. if ($level > $watchLevel) {
  376. $rule->watch2 = $literal->getId();
  377. $watchLevel = $level;
  378. }
  379. }
  380. }
  381. private function findDecisionRule(PackageInterface $package)
  382. {
  383. foreach ($this->decisionQueue as $i => $literal) {
  384. if ($package === $literal->getPackage()) {
  385. return $this->decisionQueueWhy[$i];
  386. }
  387. }
  388. return null;
  389. }
  390. // aka solver_makeruledecisions
  391. private function makeAssertionRuleDecisions()
  392. {
  393. // do we need to decide a SYSTEMSOLVABLE at level 1?
  394. $decisionStart = count($this->decisionQueue);
  395. for ($ruleIndex = 0; $ruleIndex < count($this->rules); $ruleIndex++) {
  396. $rule = $this->rules->ruleById($ruleIndex);
  397. if ($rule->isWeak() || !$rule->isAssertion() || $rule->isDisabled()) {
  398. continue;
  399. }
  400. $literals = $rule->getLiterals();
  401. $literal = $literals[0];
  402. if (!$this->decided($literal->getPackage())) {
  403. $this->decisionQueue[] = $literal;
  404. $this->decisionQueueWhy[] = $rule;
  405. $this->addDecision($literal, 1);
  406. continue;
  407. }
  408. if ($this->decisionsSatisfy($literal)) {
  409. continue;
  410. }
  411. // found a conflict
  412. if (RuleSet::TYPE_LEARNED === $rule->getType()) {
  413. $rule->disable();
  414. continue;
  415. }
  416. $conflict = $this->findDecisionRule($literal->getPackage());
  417. /** TODO: handle conflict with systemsolvable? */
  418. $this->learnedPool[] = array($rule, $conflict);
  419. if ($conflict && RuleSet::TYPE_PACKAGE === $conflict->getType()) {
  420. if ($rule->getType() == RuleSet::TYPE_JOB) {
  421. $why = $this->ruleToJob[$rule->getId()];
  422. } else {
  423. $why = $rule;
  424. }
  425. $this->problems[] = array($why);
  426. $this->disableProblem($why);
  427. continue;
  428. }
  429. // conflict with another job or update/feature rule
  430. $this->problems[] = array();
  431. // push all of our rules (can only be feature or job rules)
  432. // asserting this literal on the problem stack
  433. foreach ($this->rules->getIteratorFor(array(RuleSet::TYPE_JOB, RuleSet::TYPE_UPDATE, RuleSet::TYPE_FEATURE)) as $assertRule) {
  434. if ($assertRule->isDisabled() || !$assertRule->isAssertion() || $assertRule->isWeak()) {
  435. continue;
  436. }
  437. $assertRuleLiterals = $assertRule->getLiterals();
  438. $assertRuleLiteral = $assertRuleLiterals[0];
  439. if ($literal->getPackageId() !== $assertRuleLiteral->getPackageId()) {
  440. continue;
  441. }
  442. if ($assertRule->getType() === RuleSet::TYPE_JOB) {
  443. $why = $this->ruleToJob[$assertRule->getId()];
  444. } else {
  445. $why = $assertRule;
  446. }
  447. $this->problems[count($this->problems) - 1][] = $why;
  448. $this->disableProblem($why);
  449. }
  450. // start over
  451. while (count($this->decisionQueue) > $decisionStart) {
  452. $decisionLiteral = array_pop($this->decisionQueue);
  453. array_pop($this->decisionQueueWhy);
  454. unset($this->decisionQueueFree[count($this->decisionQueue)]);
  455. $this->decisionMap[$decisionLiteral->getPackageId()] = 0;
  456. }
  457. $ruleIndex = -1;
  458. }
  459. foreach ($this->rules as $rule) {
  460. if (!$rule->isWeak() || !$rule->isAssertion() || $rule->isDisabled()) {
  461. continue;
  462. }
  463. $literals = $rule->getLiterals();
  464. $literal = $literals[0];
  465. if ($this->decisionMap[$literal->getPackageId()] == 0) {
  466. $this->decisionQueue[] = $literal;
  467. $this->decisionQueueWhy[] = $rule;
  468. $this->addDecision($literal, 1);
  469. continue;
  470. }
  471. if ($this->decisionsSatisfy($literals[0])) {
  472. continue;
  473. }
  474. // conflict, but this is a weak rule => disable
  475. if ($rule->getType() == RuleSet::TYPE_JOB) {
  476. $why = $this->ruleToJob[$rule->getId()];
  477. } else {
  478. $why = $rule;
  479. }
  480. $this->disableProblem($why);
  481. /** TODO solver_reenablepolicyrules(solv, -(v + 1)); */
  482. }
  483. }
  484. public function addChoiceRules()
  485. {
  486. // void
  487. // solver_addchoicerules(Solver *solv)
  488. // {
  489. // Pool *pool = solv->pool;
  490. // Map m, mneg;
  491. // Rule *r;
  492. // Queue q, qi;
  493. // int i, j, rid, havechoice;
  494. // Id p, d, *pp;
  495. // Id p2, pp2;
  496. // Solvable *s, *s2;
  497. //
  498. // solv->choicerules = solv->nrules;
  499. // if (!pool->installed)
  500. // {
  501. // solv->choicerules_end = solv->nrules;
  502. // return;
  503. // }
  504. // solv->choicerules_ref = sat_calloc(solv->rpmrules_end, sizeof(Id));
  505. // queue_init(&q);
  506. // queue_init(&qi);
  507. // map_init(&m, pool->nsolvables);
  508. // map_init(&mneg, pool->nsolvables);
  509. // /* set up negative assertion map from infarch and dup rules */
  510. // for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++)
  511. // if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
  512. // MAPSET(&mneg, -r->p);
  513. // for (rid = solv->duprules, r = solv->rules + rid; rid < solv->duprules_end; rid++, r++)
  514. // if (r->p < 0 && !r->w2 && (r->d == 0 || r->d == -1))
  515. // MAPSET(&mneg, -r->p);
  516. // for (rid = 1; rid < solv->rpmrules_end ; rid++)
  517. // {
  518. // r = solv->rules + rid;
  519. // if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
  520. // continue; /* only look at requires rules */
  521. // // solver_printrule(solv, SAT_DEBUG_RESULT, r);
  522. // queue_empty(&q);
  523. // queue_empty(&qi);
  524. // havechoice = 0;
  525. // FOR_RULELITERALS(p, pp, r)
  526. // {
  527. // if (p < 0)
  528. // continue;
  529. // s = pool->solvables + p;
  530. // if (!s->repo)
  531. // continue;
  532. // if (s->repo == pool->installed)
  533. // {
  534. // queue_push(&q, p);
  535. // continue;
  536. // }
  537. // /* check if this package is "blocked" by a installed package */
  538. // s2 = 0;
  539. // FOR_PROVIDES(p2, pp2, s->name)
  540. // {
  541. // s2 = pool->solvables + p2;
  542. // if (s2->repo != pool->installed)
  543. // continue;
  544. // if (!pool->implicitobsoleteusesprovides && s->name != s2->name)
  545. // continue;
  546. // if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
  547. // continue;
  548. // break;
  549. // }
  550. // if (p2)
  551. // {
  552. // /* found installed package p2 that we can update to p */
  553. // if (MAPTST(&mneg, p))
  554. // continue;
  555. // if (policy_is_illegal(solv, s2, s, 0))
  556. // continue;
  557. // queue_push(&qi, p2);
  558. // queue_push(&q, p);
  559. // continue;
  560. // }
  561. // if (s->obsoletes)
  562. // {
  563. // Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
  564. // s2 = 0;
  565. // while ((obs = *obsp++) != 0)
  566. // {
  567. // FOR_PROVIDES(p2, pp2, obs)
  568. // {
  569. // s2 = pool->solvables + p2;
  570. // if (s2->repo != pool->installed)
  571. // continue;
  572. // if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
  573. // continue;
  574. // if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
  575. // continue;
  576. // break;
  577. // }
  578. // if (p2)
  579. // break;
  580. // }
  581. // if (obs)
  582. // {
  583. // /* found installed package p2 that we can update to p */
  584. // if (MAPTST(&mneg, p))
  585. // continue;
  586. // if (policy_is_illegal(solv, s2, s, 0))
  587. // continue;
  588. // queue_push(&qi, p2);
  589. // queue_push(&q, p);
  590. // continue;
  591. // }
  592. // }
  593. // /* package p is independent of the installed ones */
  594. // havechoice = 1;
  595. // }
  596. // if (!havechoice || !q.count)
  597. // continue; /* no choice */
  598. //
  599. // /* now check the update rules of the installed package.
  600. // * if all packages of the update rules are contained in
  601. // * the dependency rules, there's no need to set up the choice rule */
  602. // map_empty(&m);
  603. // FOR_RULELITERALS(p, pp, r)
  604. // if (p > 0)
  605. // MAPSET(&m, p);
  606. // for (i = 0; i < qi.count; i++)
  607. // {
  608. // if (!qi.elements[i])
  609. // continue;
  610. // Rule *ur = solv->rules + solv->updaterules + (qi.elements[i] - pool->installed->start);
  611. // if (!ur->p)
  612. // ur = solv->rules + solv->featurerules + (qi.elements[i] - pool->installed->start);
  613. // if (!ur->p)
  614. // continue;
  615. // FOR_RULELITERALS(p, pp, ur)
  616. // if (!MAPTST(&m, p))
  617. // break;
  618. // if (p)
  619. // break;
  620. // for (j = i + 1; j < qi.count; j++)
  621. // if (qi.elements[i] == qi.elements[j])
  622. // qi.elements[j] = 0;
  623. // }
  624. // if (i == qi.count)
  625. // {
  626. // #if 0
  627. // printf("skipping choice ");
  628. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
  629. // #endif
  630. // continue;
  631. // }
  632. // d = q.count ? pool_queuetowhatprovides(pool, &q) : 0;
  633. // solver_addrule(solv, r->p, d);
  634. // queue_push(&solv->weakruleq, solv->nrules - 1);
  635. // solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
  636. // #if 0
  637. // printf("OLD ");
  638. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + rid);
  639. // printf("WEAK CHOICE ");
  640. // solver_printrule(solv, SAT_DEBUG_RESULT, solv->rules + solv->nrules - 1);
  641. // #endif
  642. // }
  643. // queue_free(&q);
  644. // queue_free(&qi);
  645. // map_free(&m);
  646. // map_free(&mneg);
  647. // solv->choicerules_end = solv->nrules;
  648. // }
  649. }
  650. /***********************************************************************
  651. ***
  652. *** Policy rule disabling/reenabling
  653. ***
  654. *** Disable all policy rules that conflict with our jobs. If a job
  655. *** gets disabled later on, reenable the involved policy rules again.
  656. ***
  657. *** /
  658. #define DISABLE_UPDATE 1
  659. #define DISABLE_INFARCH 2
  660. #define DISABLE_DUP 3
  661. */
  662. protected function jobToDisableQueue(array $job, array $disableQueue)
  663. {
  664. switch ($job['cmd']) {
  665. case 'install':
  666. foreach ($job['packages'] as $package) {
  667. if (isset($this->installedPackageMap[$package->getId()])) {
  668. $disableQueue[] = array('type' => 'update', 'package' => $package);
  669. }
  670. /* all job packages obsolete * /
  671. qstart = q->count;
  672. pass = 0;
  673. memset(&omap, 0, sizeof(omap));
  674. FOR_JOB_SELECT(p, pp, select, what)
  675. {
  676. Id p2, pp2;
  677. if (pass == 1)
  678. map_grow(&omap, installed->end - installed->start);
  679. s = pool->solvables + p;
  680. if (s->obsoletes)
  681. {
  682. Id obs, *obsp;
  683. obsp = s->repo->idarraydata + s->obsoletes;
  684. while ((obs = *obsp++) != 0)
  685. FOR_PROVIDES(p2, pp2, obs)
  686. {
  687. Solvable *ps = pool->solvables + p2;
  688. if (ps->repo != installed)
  689. continue;
  690. if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
  691. continue;
  692. if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
  693. continue;
  694. if (pass)
  695. MAPSET(&omap, p2 - installed->start);
  696. else
  697. queue_push2(q, DISABLE_UPDATE, p2);
  698. }
  699. }
  700. FOR_PROVIDES(p2, pp2, s->name)
  701. {
  702. Solvable *ps = pool->solvables + p2;
  703. if (ps->repo != installed)
  704. continue;
  705. if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
  706. continue;
  707. if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
  708. continue;
  709. if (pass)
  710. MAPSET(&omap, p2 - installed->start);
  711. else
  712. queue_push2(q, DISABLE_UPDATE, p2);
  713. }
  714. if (pass)
  715. {
  716. for (i = j = qstart; i < q->count; i += 2)
  717. {
  718. if (MAPTST(&omap, q->elements[i + 1] - installed->start))
  719. {
  720. MAPCLR(&omap, q->elements[i + 1] - installed->start);
  721. q->elements[j + 1] = q->elements[i + 1];
  722. j += 2;
  723. }
  724. }
  725. queue_truncate(q, j);
  726. }
  727. if (q->count == qstart)
  728. break;
  729. pass++;
  730. }
  731. if (omap.size)
  732. map_free(&omap);
  733. if (qstart == q->count)
  734. return; /* nothing to prune * /
  735. if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
  736. return; /* all is set */
  737. /* now that we know which installed packages are obsoleted check each of them * /
  738. for (i = j = qstart; i < q->count; i += 2)
  739. {
  740. Solvable *is = pool->solvables + q->elements[i + 1];
  741. FOR_JOB_SELECT(p, pp, select, what)
  742. {
  743. int illegal = 0;
  744. s = pool->solvables + p;
  745. if ((set & SOLVER_SETEVR) != 0)
  746. illegal |= POLICY_ILLEGAL_DOWNGRADE; /* ignore * /
  747. if ((set & SOLVER_SETARCH) != 0)
  748. illegal |= POLICY_ILLEGAL_ARCHCHANGE; /* ignore * /
  749. if ((set & SOLVER_SETVENDOR) != 0)
  750. illegal |= POLICY_ILLEGAL_VENDORCHANGE; /* ignore * /
  751. illegal = policy_is_illegal(solv, is, s, illegal);
  752. if (illegal && illegal == POLICY_ILLEGAL_DOWNGRADE && (set & SOLVER_SETEV) != 0)
  753. {
  754. /* it's ok if the EV is different * /
  755. if (evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE_EVONLY) != 0)
  756. illegal = 0;
  757. }
  758. if (illegal)
  759. break;
  760. }
  761. if (!p)
  762. {
  763. /* no package conflicts with the update rule * /
  764. /* thus keep the DISABLE_UPDATE * /
  765. q->elements[j + 1] = q->elements[i + 1];
  766. j += 2;
  767. }
  768. }
  769. queue_truncate(q, j);
  770. return;*/
  771. }
  772. break;
  773. case 'remove':
  774. foreach ($job['packages'] as $package) {
  775. if (isset($this->installedPackageMap[$package->getId()])) {
  776. $disableQueue[] = array('type' => 'update', 'package' => $package);
  777. }
  778. }
  779. break;
  780. }
  781. return $disableQueue;
  782. }
  783. protected function disableUpdateRule($package)
  784. {
  785. // find update & feature rule and disable
  786. if (isset($this->packageToUpdateRule[$package->getId()])) {
  787. $this->packageToUpdateRule[$package->getId()]->disable();
  788. }
  789. if (isset($this->packageToFeatureRule[$package->getId()])) {
  790. $this->packageToFeatureRule[$package->getId()]->disable();
  791. }
  792. }
  793. /**
  794. * Disables all policy rules that conflict with jobs
  795. */
  796. protected function disablePolicyRules()
  797. {
  798. $lastJob = null;
  799. $allQueue = array();
  800. $iterator = $this->rules->getIteratorFor(RuleSet::TYPE_JOB);
  801. foreach ($iterator as $rule) {
  802. if ($rule->isDisabled()) {
  803. continue;
  804. }
  805. $job = $this->ruleToJob[$rule->getId()];
  806. if ($job === $lastJob) {
  807. continue;
  808. }
  809. $lastJob = $job;
  810. $allQueue = $this->jobToDisableQueue($job, $allQueue);
  811. }
  812. foreach ($allQueue as $disable) {
  813. switch ($disable['type']) {
  814. case 'update':
  815. $this->disableUpdateRule($disable['package']);
  816. break;
  817. default:
  818. throw new \RuntimeException("Unsupported disable type: " . $disable['type']);
  819. }
  820. }
  821. }
  822. public function solve(Request $request)
  823. {
  824. $this->jobs = $request->getJobs();
  825. $installedPackages = $this->installed->getPackages();
  826. $this->installedPackageMap = array();
  827. foreach ($installedPackages as $package) {
  828. $this->installedPackageMap[$package->getId()] = $package;
  829. }
  830. $this->decisionMap = new \SplFixedArray($this->pool->getMaxId() + 1);
  831. foreach ($this->jobs as $job) {
  832. switch ($job['cmd']) {
  833. case 'update-all':
  834. foreach ($installedPackages as $package) {
  835. $this->updateMap[$package->getId()] = true;
  836. }
  837. break;
  838. case 'fix-all':
  839. foreach ($installedPackages as $package) {
  840. $this->fixMap[$package->getId()] = true;
  841. }
  842. break;
  843. }
  844. foreach ($job['packages'] as $package) {
  845. switch ($job['cmd']) {
  846. case 'fix':
  847. if (isset($this->installedPackageMap[$package->getId()])) {
  848. $this->fixMap[$package->getId()] = true;
  849. }
  850. break;
  851. case 'update':
  852. if (isset($this->installedPackageMap[$package->getId()])) {
  853. $this->updateMap[$package->getId()] = true;
  854. }
  855. break;
  856. }
  857. }
  858. }
  859. foreach ($installedPackages as $package) {
  860. $this->addRulesForPackage($package);
  861. }
  862. foreach ($installedPackages as $package) {
  863. $this->addRulesForUpdatePackages($package, true);
  864. }
  865. foreach ($this->jobs as $job) {
  866. foreach ($job['packages'] as $package) {
  867. switch ($job['cmd']) {
  868. case 'install':
  869. $this->installCandidateMap[$package->getId()] = true;
  870. $this->addRulesForPackage($package);
  871. break;
  872. }
  873. }
  874. }
  875. // solver_addrpmrulesforweak(solv, &addedmap);
  876. foreach ($installedPackages as $package) {
  877. // create a feature rule which allows downgrades
  878. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, true);
  879. $featureRule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
  880. // create an update rule which does not allow downgrades
  881. $updates = $this->policy->findUpdatePackages($this, $this->pool, $this->installed, $package, false);
  882. $rule = $this->createUpdateRule($package, $updates, self::RULE_INTERNAL_ALLOW_UPDATE, (string) $package);
  883. if ($rule->equals($featureRule)) {
  884. if ($this->policy->allowUninstall()) {
  885. $featureRule->setWeak(true);
  886. $this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
  887. $this->packageToFeatureRule[$package->getId()] = $rule;
  888. } else {
  889. $this->addRule(RuleSet::TYPE_UPDATE, $rule);
  890. $this->packageToUpdateRule[$package->getId()] = $rule;
  891. }
  892. } else if ($this->policy->allowUninstall()) {
  893. $featureRule->setWeak(true);
  894. $rule->setWeak(true);
  895. $this->addRule(RuleSet::TYPE_FEATURE, $featureRule);
  896. $this->addRule(RuleSet::TYPE_UPDATE, $rule);
  897. $this->packageToFeatureRule[$package->getId()] = $rule;
  898. $this->packageToUpdateRule[$package->getId()] = $rule;
  899. }
  900. }
  901. foreach ($this->jobs as $job) {
  902. switch ($job['cmd']) {
  903. case 'install':
  904. $rule = $this->createInstallOneOfRule($job['packages'], self::RULE_JOB_INSTALL, $job['packageName']);
  905. $this->addRule(RuleSet::TYPE_JOB, $rule);
  906. $this->ruleToJob[$rule->getId()] = $job;
  907. break;
  908. case 'remove':
  909. // remove all packages with this name including uninstalled
  910. // ones to make sure none of them are picked as replacements
  911. // todo: cleandeps
  912. foreach ($job['packages'] as $package) {
  913. $rule = $this->createRemoveRule($package, self::RULE_JOB_REMOVE);
  914. $this->addRule(RuleSet::TYPE_JOB, $rule);
  915. $this->ruleToJob[$rule->getId()] = $job;
  916. }
  917. break;
  918. case 'lock':
  919. foreach ($job['packages'] as $package) {
  920. if (isset($this->installedPackageMap[$package->getId()])) {
  921. $rule = $this->createInstallRule($package, self::RULE_JOB_LOCK);
  922. } else {
  923. $rule = $this->createRemoveRule($package, self::RULE_JOB_LOCK);
  924. }
  925. $this->addRule(RuleSet::TYPE_JOB, $rule);
  926. $this->ruleToJob[$rule->getId()] = $job;
  927. }
  928. break;
  929. }
  930. }
  931. $this->addChoiceRules();
  932. foreach ($this->rules as $rule) {
  933. $this->addWatchesToRule($rule);
  934. }
  935. /* disable update rules that conflict with our job */
  936. $this->disablePolicyRules();
  937. /* make decisions based on job/update assertions */
  938. $this->makeAssertionRuleDecisions();
  939. $installRecommended = 0;
  940. $this->runSat(true, $installRecommended);
  941. //$this->printDecisionMap();
  942. //findrecommendedsuggested(solv);
  943. //solver_prepare_solutions(solv);
  944. return $this->createTransaction();
  945. }
  946. protected function createTransaction()
  947. {
  948. $transaction = array();
  949. $installMeansUpdateMap = array();
  950. $ignoreRemoveMap = array();
  951. foreach ($this->decisionQueue as $i => $literal) {
  952. $package = $literal->getPackage();
  953. // !wanted & installed
  954. if (!$literal->isWanted() && isset($this->installedPackageMap[$package->getId()])) {
  955. $literals = array();
  956. if (isset($this->packageToUpdateRule[$package->getId()])) {
  957. $literals = array_merge($literals, $this->packageToUpdateRule[$package->getId()]->getLiterals());
  958. }
  959. if (isset($this->packageToFeatureRule[$package->getId()])) {
  960. $literals = array_merge($literals, $this->packageToFeatureRule[$package->getId()]->getLiterals());
  961. }
  962. foreach ($literals as $updateLiteral) {
  963. if (!$updateLiteral->equals($literal)) {
  964. $installMeansUpdateMap[$updateLiteral->getPackageId()] = $package;
  965. }
  966. }
  967. }
  968. }
  969. foreach ($this->decisionQueue as $i => $literal) {
  970. $package = $literal->getPackage();
  971. // wanted & installed || !wanted & !installed
  972. if ($literal->isWanted() == (isset($this->installedPackageMap[$package->getId()]))) {
  973. continue;
  974. }
  975. if ($literal->isWanted()) {
  976. if (isset($installMeansUpdateMap[$literal->getPackageId()])) {
  977. $source = $installMeansUpdateMap[$literal->getPackageId()];
  978. $transaction[] = new Operation\UpdateOperation(
  979. $source, $package, $this->decisionQueueWhy[$i]
  980. );
  981. // avoid updates to one package from multiple origins
  982. unset($installMeansUpdateMap[$literal->getPackageId()]);
  983. $ignoreRemove[$source->getId()] = true;
  984. } else {
  985. $transaction[] = new Operation\InstallOperation(
  986. $package, $this->decisionQueueWhy[$i]
  987. );
  988. }
  989. } else if (!isset($ignoreRemove[$package->getId()])) {
  990. $transaction[] = new Operation\UninstallOperation(
  991. $package, $this->decisionQueueWhy[$i]
  992. );
  993. }
  994. }
  995. return array_reverse($transaction);
  996. }
  997. protected $decisionQueue = array();
  998. protected $decisionQueueWhy = array();
  999. protected $decisionQueueFree = array();
  1000. protected $propagateIndex;
  1001. protected $branches = array();
  1002. protected $problems = array();
  1003. protected $learnedPool = array();
  1004. protected $recommendsIndex;
  1005. protected function literalFromId($id)
  1006. {
  1007. $package = $this->pool->packageById(abs($id));
  1008. return new Literal($package, $id > 0);
  1009. }
  1010. protected function addDecision(Literal $l, $level)
  1011. {
  1012. if ($l->isWanted()) {
  1013. $this->decisionMap[$l->getPackageId()] = $level;
  1014. } else {
  1015. $this->decisionMap[$l->getPackageId()] = -$level;
  1016. }
  1017. }
  1018. protected function addDecisionId($literalId, $level)
  1019. {
  1020. $packageId = abs($literalId);
  1021. if ($literalId > 0) {
  1022. $this->decisionMap[$packageId] = $level;
  1023. } else {
  1024. $this->decisionMap[$packageId] = -$level;
  1025. }
  1026. }
  1027. protected function decisionsContain(Literal $l)
  1028. {
  1029. return (
  1030. $this->decisionMap[$l->getPackageId()] > 0 && $l->isWanted() ||
  1031. $this->decisionMap[$l->getPackageId()] < 0 && !$l->isWanted()
  1032. );
  1033. }
  1034. protected function decisionsContainId($literalId)
  1035. {
  1036. $packageId = abs($literalId);
  1037. return (
  1038. $this->decisionMap[$packageId] > 0 && $literalId > 0 ||
  1039. $this->decisionMap[$packageId] < 0 && $literalId < 0
  1040. );
  1041. }
  1042. protected function decisionsSatisfy(Literal $l)
  1043. {
  1044. return ($l->isWanted() && $this->decisionMap[$l->getPackageId()] > 0) ||
  1045. (!$l->isWanted() && $this->decisionMap[$l->getPackageId()] <= 0);
  1046. }
  1047. protected function decisionsConflict(Literal $l)
  1048. {
  1049. return (
  1050. $this->decisionMap[$l->getPackageId()] > 0 && !$l->isWanted() ||
  1051. $this->decisionMap[$l->getPackageId()] < 0 && $l->isWanted()
  1052. );
  1053. }
  1054. protected function decisionsConflictId($literalId)
  1055. {
  1056. $packageId = abs($literalId);
  1057. return (
  1058. $this->decisionMap[$packageId] > 0 && !($literalId < 0) ||
  1059. $this->decisionMap[$packageId] < 0 && $literalId > 0
  1060. );
  1061. }
  1062. protected function decided(PackageInterface $p)
  1063. {
  1064. return $this->decisionMap[$p->getId()] != 0;
  1065. }
  1066. protected function undecided(PackageInterface $p)
  1067. {
  1068. return $this->decisionMap[$p->getId()] == 0;
  1069. }
  1070. protected function decidedInstall(PackageInterface $p) {
  1071. return $this->decisionMap[$p->getId()] > 0;
  1072. }
  1073. protected function decidedRemove(PackageInterface $p) {
  1074. return $this->decisionMap[$p->getId()] < 0;
  1075. }
  1076. /**
  1077. * Makes a decision and propagates it to all rules.
  1078. *
  1079. * Evaluates each term affected by the decision (linked through watches)
  1080. * If we find unit rules we make new decisions based on them
  1081. *
  1082. * @return Rule|null A rule on conflict, otherwise null.
  1083. */
  1084. protected function propagate($level)
  1085. {
  1086. while ($this->propagateIndex < count($this->decisionQueue)) {
  1087. // we invert the decided literal here, example:
  1088. // A was decided => (-A|B) now requires B to be true, so we look for
  1089. // rules which are fulfilled by -A, rather than A.
  1090. $literal = $this->decisionQueue[$this->propagateIndex]->inverted();
  1091. $this->propagateIndex++;
  1092. // /* foreach rule where 'pkg' is now FALSE */
  1093. //for (rp = watches + pkg; *rp; rp = next_rp)
  1094. if (!isset($this->watches[$literal->getId()])) {
  1095. continue;
  1096. }
  1097. for ($rule = $this->watches[$literal->getId()]; $rule !== null; $rule = $nextRule) {
  1098. $nextRule = $rule->getNext($literal);
  1099. if ($rule->isDisabled()) {
  1100. continue;
  1101. }
  1102. $otherWatch = $rule->getOtherWatch($literal);
  1103. if ($this->decisionsContainId($otherWatch)) {
  1104. continue;
  1105. }
  1106. $ruleLiterals = $rule->getLiterals();
  1107. if (sizeof($ruleLiterals) > 2) {
  1108. foreach ($ruleLiterals as $ruleLiteral) {
  1109. if ($otherWatch !== $ruleLiteral->getId() &&
  1110. !$this->decisionsConflict($ruleLiteral)) {
  1111. if ($literal->getId() === $rule->watch1) {
  1112. $rule->watch1 = $ruleLiteral->getId();
  1113. $rule->next1 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null ;
  1114. } else {
  1115. $rule->watch2 = $ruleLiteral->getId();
  1116. $rule->next2 = (isset($this->watches[$ruleLiteral->getId()])) ? $this->watches[$ruleLiteral->getId()] : null ;
  1117. }
  1118. $this->watches[$ruleLiteral->getId()] = $rule;
  1119. continue 2;
  1120. }
  1121. }
  1122. }
  1123. // yay, we found a unit clause! try setting it to true
  1124. if ($this->decisionsConflictId($otherWatch)) {
  1125. return $rule;
  1126. }
  1127. $this->addDecisionId($otherWatch, $level);
  1128. $this->decisionQueue[] = $this->literalFromId($otherWatch);
  1129. $this->decisionQueueWhy[] = $rule;
  1130. }
  1131. }
  1132. return null;
  1133. }
  1134. /**
  1135. * Reverts a decision at the given level.
  1136. */
  1137. private function revert($level)
  1138. {
  1139. while (!empty($this->decisionQueue)) {
  1140. $literal = $this->decisionQueue[count($this->decisionQueue) - 1];
  1141. if (!$this->decisionMap[$literal->getPackageId()]) {
  1142. break;
  1143. }
  1144. $decisionLevel = abs($this->decisionMap[$literal->getPackageId()]);
  1145. if ($decisionLevel <= $level) {
  1146. break;
  1147. }
  1148. /** TODO: implement recommendations
  1149. *if (v > 0 && solv->recommendations.count && v == solv->recommendations.elements[solv->recommendations.count - 1])
  1150. * solv->recommendations.count--;
  1151. */
  1152. $this->decisionMap[$literal->getPackageId()] = 0;
  1153. array_pop($this->decisionQueue);
  1154. array_pop($this->decisionQueueWhy);
  1155. $this->propagateIndex = count($this->decisionQueue);
  1156. }
  1157. while (!empty($this->branches)) {
  1158. list($literals, $branchLevel) = $this->branches[count($this->branches) - 1];
  1159. if ($branchLevel >= $level) {
  1160. break;
  1161. }
  1162. array_pop($this->branches);
  1163. }
  1164. $this->recommendsIndex = -1;
  1165. }
  1166. /**-------------------------------------------------------------------
  1167. *
  1168. * setpropagatelearn
  1169. *
  1170. * add free decision (solvable to install) to decisionq
  1171. * increase level and propagate decision
  1172. * return if no conflict.
  1173. *
  1174. * in conflict case, analyze conflict rule, add resulting
  1175. * rule to learnt rule set, make decision from learnt
  1176. * rule (always unit) and re-propagate.
  1177. *
  1178. * returns the new solver level or 0 if unsolvable
  1179. *
  1180. */
  1181. private function setPropagateLearn($level, Literal $literal, $disableRules, Rule $rule)
  1182. {
  1183. assert($rule != null);
  1184. assert($literal != null);
  1185. $level++;
  1186. $this->addDecision($literal, $level);
  1187. $this->decisionQueue[] = $literal;
  1188. $this->decisionQueueWhy[] = $rule;
  1189. $this->decisionQueueFree[count($this->decisionQueueWhy) - 1] = true;
  1190. while (true) {
  1191. $rule = $this->propagate($level);
  1192. if (!$rule) {
  1193. break;
  1194. }
  1195. if ($level == 1) {
  1196. return $this->analyzeUnsolvable($rule, $disableRules);
  1197. }
  1198. // conflict
  1199. list($newLevel, $newRule, $why) = $this->analyze($level, $rule);
  1200. assert($newLevel > 0);
  1201. assert($newLevel < $level);
  1202. $level = $newLevel;
  1203. $this->revert($level);
  1204. assert($newRule != null);
  1205. $this->addRule(RuleSet::TYPE_LEARNED, $newRule);
  1206. $this->learnedWhy[$newRule->getId()] = $why;
  1207. $this->watch2OnHighest($newRule);
  1208. $this->addWatchesToRule($newRule);
  1209. $literals = $newRule->getLiterals();
  1210. $this->addDecision($literals[0], $level);
  1211. $this->decisionQueue[] = $literals[0];
  1212. $this->decisionQueueWhy[] = $newRule;
  1213. }

Large files files are truncated, but you can click here to view the full file