Transaction.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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(
  83. function ($operation) {
  84. return $operation['package'];
  85. },
  86. $this->findRootPackages($installMap, $updateMap)
  87. );
  88. $visited = array();
  89. while (!empty($queue)) {
  90. $package = array_pop($queue);
  91. $packageId = $package->id;
  92. if (!isset($visited[$packageId])) {
  93. $queue[] = $package;
  94. if ($package instanceof AliasPackage) {
  95. $queue[] = $package->getAliasOf();
  96. } else {
  97. foreach ($package->getRequires() as $link) {
  98. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  99. foreach ($possibleRequires as $require) {
  100. $queue[] = $require;
  101. }
  102. }
  103. }
  104. $visited[$package->id] = true;
  105. } else {
  106. if (isset($installMap[$packageId])) {
  107. $this->install(
  108. $installMap[$packageId]['package'],
  109. $installMap[$packageId]['reason']
  110. );
  111. unset($installMap[$packageId]);
  112. }
  113. if (isset($updateMap[$packageId])) {
  114. $this->update(
  115. $updateMap[$packageId]['source'],
  116. $updateMap[$packageId]['package'],
  117. $updateMap[$packageId]['reason']
  118. );
  119. unset($updateMap[$packageId]);
  120. }
  121. }
  122. }
  123. foreach ($uninstallMap as $uninstall) {
  124. $this->uninstall($uninstall['package'], $uninstall['reason']);
  125. }
  126. }
  127. protected function findRootPackages($installMap, $updateMap)
  128. {
  129. $packages = $installMap + $updateMap;
  130. $roots = $packages;
  131. foreach ($packages as $packageId => $operation) {
  132. $package = $operation['package'];
  133. if (!isset($roots[$packageId])) {
  134. continue;
  135. }
  136. foreach ($package->getRequires() as $link) {
  137. $possibleRequires = $this->pool->whatProvides($link->getTarget(), $link->getConstraint());
  138. foreach ($possibleRequires as $require) {
  139. if ($require !== $package) {
  140. unset($roots[$require->id]);
  141. }
  142. }
  143. }
  144. }
  145. return $roots;
  146. }
  147. protected function findUpdates()
  148. {
  149. $installMeansUpdateMap = array();
  150. foreach ($this->decisions as $i => $decision) {
  151. $literal = $decision[Decisions::DECISION_LITERAL];
  152. $package = $this->pool->literalToPackage($literal);
  153. if ($package instanceof AliasPackage) {
  154. continue;
  155. }
  156. // !wanted & installed
  157. if ($literal <= 0 && isset($this->installedMap[$package->id])) {
  158. $updates = $this->policy->findUpdatePackages($this->pool, $this->installedMap, $package);
  159. $literals = array($package->id);
  160. foreach ($updates as $update) {
  161. $literals[] = $update->id;
  162. }
  163. foreach ($literals as $updateLiteral) {
  164. if ($updateLiteral !== $literal) {
  165. $installMeansUpdateMap[abs($updateLiteral)] = $package;
  166. }
  167. }
  168. }
  169. }
  170. return $installMeansUpdateMap;
  171. }
  172. protected function install($package, $reason)
  173. {
  174. if ($package instanceof AliasPackage) {
  175. return $this->markAliasInstalled($package, $reason);
  176. }
  177. $this->transaction[] = new Operation\InstallOperation($package, $reason);
  178. }
  179. protected function update($from, $to, $reason)
  180. {
  181. $this->transaction[] = new Operation\UpdateOperation($from, $to, $reason);
  182. }
  183. protected function uninstall($package, $reason)
  184. {
  185. if ($package instanceof AliasPackage) {
  186. return $this->markAliasUninstalled($package, $reason);
  187. }
  188. $this->transaction[] = new Operation\UninstallOperation($package, $reason);
  189. }
  190. protected function markAliasInstalled($package, $reason)
  191. {
  192. $this->transaction[] = new Operation\MarkAliasInstalledOperation($package, $reason);
  193. }
  194. protected function markAliasUninstalled($package, $reason)
  195. {
  196. $this->transaction[] = new Operation\MarkAliasUninstalledOperation($package, $reason);
  197. }
  198. }