Problem.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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. /**
  13. * Represents a problem detected while solving dependencies
  14. *
  15. * @author Nils Adermann <naderman@naderman.de>
  16. */
  17. class Problem
  18. {
  19. /**
  20. * A map containing the id of each rule part of this problem as a key
  21. * @var array
  22. */
  23. protected $reasonSeen;
  24. /**
  25. * A set of reasons for the problem, each is a rule or a job and a rule
  26. * @var array
  27. */
  28. protected $reasons = array();
  29. protected $section = 0;
  30. /**
  31. * Add a rule as a reason
  32. *
  33. * @param Rule $rule A rule which is a reason for this problem
  34. */
  35. public function addRule(Rule $rule)
  36. {
  37. $this->addReason($rule->getId(), array(
  38. 'rule' => $rule,
  39. 'job' => $rule->getJob(),
  40. ));
  41. }
  42. /**
  43. * Retrieve all reasons for this problem
  44. *
  45. * @return array The problem's reasons
  46. */
  47. public function getReasons()
  48. {
  49. return $this->reasons;
  50. }
  51. /**
  52. * A human readable textual representation of the problem's reasons
  53. *
  54. * @param array $installedMap A map of all installed packages
  55. */
  56. public function getPrettyString(array $installedMap = array())
  57. {
  58. $reasons = call_user_func_array('array_merge', array_reverse($this->reasons));
  59. if (count($reasons) === 1) {
  60. reset($reasons);
  61. $reason = current($reasons);
  62. $rule = $reason['rule'];
  63. $job = $reason['job'];
  64. if ($job && $job['cmd'] === 'install' && empty($job['packages'])) {
  65. // handle php extensions
  66. if (0 === stripos($job['packageName'], 'ext-')) {
  67. $ext = substr($job['packageName'], 4);
  68. $error = extension_loaded($ext) ? 'has the wrong version ('.phpversion($ext).') installed' : 'is missing from your system';
  69. return 'The requested PHP extension '.$job['packageName'].$this->constraintToText($job['constraint']).' '.$error.'.';
  70. }
  71. return 'The requested package '.$job['packageName'].$this->constraintToText($job['constraint']).' could not be found.';
  72. }
  73. }
  74. $messages = array();
  75. foreach ($reasons as $reason) {
  76. $rule = $reason['rule'];
  77. $job = $reason['job'];
  78. if ($job) {
  79. $messages[] = $this->jobToText($job);
  80. } elseif ($rule) {
  81. if ($rule instanceof Rule) {
  82. $messages[] = $rule->getPrettyString($installedMap);
  83. }
  84. }
  85. }
  86. return "\n - ".implode("\n - ", $messages);
  87. }
  88. /**
  89. * Store a reason descriptor but ignore duplicates
  90. *
  91. * @param string $id A canonical identifier for the reason
  92. * @param string $reason The reason descriptor
  93. */
  94. protected function addReason($id, $reason)
  95. {
  96. if (!isset($this->reasonSeen[$id])) {
  97. $this->reasonSeen[$id] = true;
  98. $this->reasons[$this->section][] = $reason;
  99. }
  100. }
  101. public function nextSection()
  102. {
  103. $this->section++;
  104. }
  105. /**
  106. * Turns a job into a human readable description
  107. *
  108. * @param array $job
  109. * @return string
  110. */
  111. protected function jobToText($job)
  112. {
  113. switch ($job['cmd']) {
  114. case 'install':
  115. if (!$job['packages']) {
  116. return 'No package found to satisfy install request for '.$job['packageName'].$this->constraintToText($job['constraint']);
  117. }
  118. return 'Installation request for '.$job['packageName'].$this->constraintToText($job['constraint']).' -> satisfiable by '.$this->getPackageList($job['packages']).'.';
  119. case 'update':
  120. return 'Update request for '.$job['packageName'].$this->constraintToText($job['constraint']).'.';
  121. case 'remove':
  122. return 'Removal request for '.$job['packageName'].$this->constraintToText($job['constraint']).'';
  123. }
  124. return 'Job(cmd='.$job['cmd'].', target='.$job['packageName'].', packages=['.$this->packageList($job['packages']).'])';
  125. }
  126. protected function getPackageList($packages)
  127. {
  128. return implode(', ', array_map(function ($package) {
  129. return $package->getPrettyString();
  130. },
  131. $packages
  132. ));
  133. }
  134. /**
  135. * Turns a constraint into text usable in a sentence describing a job
  136. *
  137. * @param LinkConstraint $constraint
  138. * @return string
  139. */
  140. protected function constraintToText($constraint)
  141. {
  142. return ($constraint) ? ' '.$constraint->getPrettyString() : '';
  143. }
  144. }