Transaction.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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\Package\AliasPackage;
  13. /**
  14. * @author Nils Adermann <naderman@naderman.de>
  15. */
  16. class Transaction
  17. {
  18. protected $policy;
  19. protected $pool;
  20. protected $installedMap;
  21. protected $decisions;
  22. protected $transaction;
  23. public function __construct($policy, $pool, $installedMap, $decisions)
  24. {
  25. $this->policy = $policy;
  26. $this->pool = $pool;
  27. $this->installedMap = $installedMap;
  28. $this->decisions = $decisions;
  29. $this->transaction = array();
  30. }
  31. public function getOperations()
  32. {
  33. $installMeansUpdateMap = $this->findUpdates();
  34. $updateMap = array();
  35. $installMap = array();
  36. $uninstallMap = array();
  37. foreach ($this->decisions as $i => $decision) {
  38. $literal = $decision[Decisions::DECISION_LITERAL];
  39. $reason = $decision[Decisions::DECISION_REASON];
  40. $package = $this->pool->literalToPackage($literal);
  41. // wanted & installed || !wanted & !installed
  42. if (($literal > 0) == (isset($this->installedMap[$package->id]))) {
  43. continue;
  44. }
  45. if ($literal > 0) {
  46. if (isset($installMeansUpdateMap[abs($literal)]) && !$package instanceof AliasPackage) {
  47. $source = $installMeansUpdateMap[abs($literal)];
  48. $updateMap[$package->id] = array(
  49. 'package' => $package,
  50. 'source' => $source,
  51. 'reason' => $reason,
  52. );
  53. // avoid updates to one package from multiple origins
  54. unset($installMeansUpdateMap[abs($literal)]);
  55. $ignoreRemove[$source->id] = true;
  56. } else {
  57. $installMap[$package->id] = array(
  58. 'package' => $package,
  59. 'reason' => $reason,
  60. );
  61. }
  62. }
  63. }
  64. foreach ($this->decisions as $i => $decision) {
  65. $literal = $decision[Decisions::DECISION_LITERAL];
  66. $reason = $decision[Decisions::DECISION_REASON];
  67. $package = $this->pool->literalToPackage($literal);
  68. if ($literal <= 0 &&
  69. isset($this->installedMap[$package->id]) &&
  70. !isset($ignoreRemove[$package->id])) {
  71. $uninstallMap[$package->id] = array(
  72. 'package' => $package,
  73. 'reason' => $reason,
  74. );
  75. }
  76. }
  77. $this->transactionFromMaps($installMap, $updateMap, $uninstallMap);
  78. return $this->transaction;
  79. }
  80. protected function transactionFromMaps($installMap, $updateMap, $uninstallMap)
  81. {
  82. $queue = array_map(function ($operation) {
  83. return $operation['package'];
  84. },
  85. $this->findRootPackages($installMap, $updateMap)
  86. );
  87. $visited = array();
  88. while (!empty($queue)) {
  89. $package = array_pop($queue);
  90. $packageId = $package->id;
  91. if (!isset($visited[$packageId])) {
  92. array_push($queue, $package);
  93. if ($package instanceof AliasPackage) {
  94. array_push($queue, $package->getAliasOf());
  95. } else {
  96. foreach ($package->getRequires() as $link) {
  97. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  98. foreach ($possibleRequires as $require) {
  99. array_push($queue, $require);
  100. }
  101. }
  102. }
  103. $visited[$package->id] = true;
  104. } else {
  105. if (isset($installMap[$packageId])) {
  106. $this->install(
  107. $installMap[$packageId]['package'],
  108. $installMap[$packageId]['reason']
  109. );
  110. unset($installMap[$packageId]);
  111. }
  112. if (isset($updateMap[$packageId])) {
  113. $this->update(
  114. $updateMap[$packageId]['source'],
  115. $updateMap[$packageId]['package'],
  116. $updateMap[$packageId]['reason']
  117. );
  118. unset($updateMap[$packageId]);
  119. }
  120. }
  121. }
  122. foreach ($uninstallMap as $uninstall) {
  123. $this->uninstall($uninstall['package'], $uninstall['reason']);
  124. }
  125. }
  126. protected function findRootPackages($installMap, $updateMap)
  127. {
  128. $packages = $installMap + $updateMap;
  129. $roots = $packages;
  130. foreach ($packages as $packageId => $operation) {
  131. $package = $operation['package'];
  132. if (!isset($roots[$packageId])) {
  133. continue;
  134. }
  135. foreach ($package->getRequires() as $link) {
  136. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  137. foreach ($possibleRequires as $require) {
  138. unset($roots[$require->id]);
  139. }
  140. }
  141. }
  142. return $roots;
  143. }
  144. protected function findUpdates()
  145. {
  146. $installMeansUpdateMap = array();
  147. foreach ($this->decisions as $i => $decision) {
  148. $literal = $decision[Decisions::DECISION_LITERAL];
  149. $package = $this->pool->literalToPackage($literal);
  150. if ($package instanceof AliasPackage) {
  151. continue;
  152. }
  153. // !wanted & installed
  154. if ($literal <= 0 && isset($this->installedMap[$package->id])) {
  155. $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package);
  156. $literals = array($package->id);
  157. foreach ($updates as $update) {
  158. $literals[] = $update->id;
  159. }
  160. foreach ($literals as $updateLiteral) {
  161. if ($updateLiteral !== $literal) {
  162. $installMeansUpdateMap[abs($updateLiteral)] = $package;
  163. }
  164. }
  165. }
  166. }
  167. return $installMeansUpdateMap;
  168. }
  169. protected function install($package, $reason)
  170. {
  171. if ($package instanceof AliasPackage) {
  172. return $this->markAliasInstalled($package, $reason);
  173. }
  174. $this->transaction[] = new Operation\InstallOperation($package, $reason);
  175. }
  176. protected function update($from, $to, $reason)
  177. {
  178. $this->transaction[] = new Operation\UpdateOperation($from, $to, $reason);
  179. }
  180. protected function uninstall($package, $reason)
  181. {
  182. if ($package instanceof AliasPackage) {
  183. return $this->markAliasUninstalled($package, $reason);
  184. }
  185. $this->transaction[] = new Operation\UninstallOperation($package, $reason);
  186. }
  187. protected function markAliasInstalled($package, $reason)
  188. {
  189. $this->transaction[] = new Operation\MarkAliasInstalledOperation($package, $reason);
  190. }
  191. protected function markAliasUninstalled($package, $reason)
  192. {
  193. $this->transaction[] = new Operation\MarkAliasUninstalledOperation($package, $reason);
  194. }
  195. }