BaseRepository.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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\Repository;
  12. use Composer\Package\RootPackageInterface;
  13. use Composer\Semver\Constraint\ConstraintInterface;
  14. use Composer\Semver\Constraint\Constraint;
  15. use Composer\Package\Link;
  16. /**
  17. * Common ancestor class for generic repository functionality.
  18. *
  19. * @author Niels Keurentjes <niels.keurentjes@omines.com>
  20. */
  21. abstract class BaseRepository implements RepositoryInterface
  22. {
  23. /**
  24. * Returns a list of links causing the requested needle packages to be installed, as an associative array with the
  25. * dependent's name as key, and an array containing in order the PackageInterface and Link describing the relationship
  26. * as values. If recursive lookup was requested a third value is returned containing an identically formed array up
  27. * to the root package. That third value will be false in case a circular recursion was detected.
  28. *
  29. * @param string|string[] $needle The package name(s) to inspect.
  30. * @param ConstraintInterface|null $constraint Optional constraint to filter by.
  31. * @param bool $invert Whether to invert matches to discover reasons for the package *NOT* to be installed.
  32. * @param bool $recurse Whether to recursively expand the requirement tree up to the root package.
  33. * @param string[] $packagesFound Used internally when recurring
  34. * @return array An associative array of arrays as described above.
  35. */
  36. public function getDependents($needle, $constraint = null, $invert = false, $recurse = true, $packagesFound = null)
  37. {
  38. $needles = array_map('strtolower', (array) $needle);
  39. $results = array();
  40. // initialize the array with the needles before any recursion occurs
  41. if (null === $packagesFound) {
  42. $packagesFound = $needles;
  43. }
  44. // locate root package for use below
  45. $rootPackage = null;
  46. foreach ($this->getPackages() as $package) {
  47. if ($package instanceof RootPackageInterface) {
  48. $rootPackage = $package;
  49. break;
  50. }
  51. }
  52. // Loop over all currently installed packages.
  53. foreach ($this->getPackages() as $package) {
  54. $links = $package->getRequires();
  55. // each loop needs its own "tree" as we want to show the complete dependent set of every needle
  56. // without warning all the time about finding circular deps
  57. $packagesInTree = $packagesFound;
  58. // Replacements are considered valid reasons for a package to be installed during forward resolution
  59. if (!$invert) {
  60. $links += $package->getReplaces();
  61. }
  62. // Require-dev is only relevant for the root package
  63. if ($package instanceof RootPackageInterface) {
  64. $links += $package->getDevRequires();
  65. }
  66. // Cross-reference all discovered links to the needles
  67. foreach ($links as $link) {
  68. foreach ($needles as $needle) {
  69. if ($link->getTarget() === $needle) {
  70. if ($constraint === null || ($link->getConstraint()->matches($constraint) === !$invert)) {
  71. // already displayed this node's dependencies, cutting short
  72. if (in_array($link->getSource(), $packagesInTree)) {
  73. $results[$link->getSource()] = array($package, $link, false);
  74. continue;
  75. }
  76. $packagesInTree[] = $link->getSource();
  77. $dependents = $recurse ? $this->getDependents($link->getSource(), null, false, true, $packagesInTree) : array();
  78. $results[$link->getSource()] = array($package, $link, $dependents);
  79. }
  80. }
  81. }
  82. }
  83. // When inverting, we need to check for conflicts of the needles against installed packages
  84. if ($invert && in_array($package->getName(), $needles)) {
  85. foreach ($package->getConflicts() as $link) {
  86. foreach ($this->findPackages($link->getTarget()) as $pkg) {
  87. $version = new Constraint('=', $pkg->getVersion());
  88. if ($link->getConstraint()->matches($version) === $invert) {
  89. $results[] = array($package, $link, false);
  90. }
  91. }
  92. }
  93. }
  94. // When inverting, we need to check for conflicts of the needles' requirements against installed packages
  95. if ($invert && $constraint && in_array($package->getName(), $needles) && $constraint->matches(new Constraint('=', $package->getVersion()))) {
  96. foreach ($package->getRequires() as $link) {
  97. if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
  98. if ($this->findPackage($link->getTarget(), $link->getConstraint())) {
  99. continue;
  100. }
  101. $platformPkg = $this->findPackage($link->getTarget(), '*');
  102. $description = $platformPkg ? 'but '.$platformPkg->getPrettyVersion().' is installed' : 'but it is missing';
  103. $results[] = array($package, new Link($package->getName(), $link->getTarget(), null, 'requires', $link->getPrettyConstraint().' '.$description), false);
  104. continue;
  105. }
  106. foreach ($this->getPackages() as $pkg) {
  107. if (!in_array($link->getTarget(), $pkg->getNames())) {
  108. continue;
  109. }
  110. $version = new Constraint('=', $pkg->getVersion());
  111. if (!$link->getConstraint()->matches($version)) {
  112. // if we have a root package (we should but can not guarantee..) we show
  113. // the root requires as well to perhaps allow to find an issue there
  114. if ($rootPackage) {
  115. foreach (array_merge($rootPackage->getRequires(), $rootPackage->getDevRequires()) as $rootReq) {
  116. if (in_array($rootReq->getTarget(), $pkg->getNames()) && !$rootReq->getConstraint()->matches($link->getConstraint())) {
  117. $results[] = array($package, $link, false);
  118. $results[] = array($rootPackage, $rootReq, false);
  119. continue 3;
  120. }
  121. }
  122. $results[] = array($package, $link, false);
  123. $results[] = array($rootPackage, new Link($rootPackage->getName(), $link->getTarget(), null, 'does not require', 'but ' . $pkg->getPrettyVersion() . ' is installed'), false);
  124. } else {
  125. // no root so let's just print whatever we found
  126. $results[] = array($package, $link, false);
  127. }
  128. }
  129. continue 2;
  130. }
  131. }
  132. }
  133. }
  134. ksort($results);
  135. return $results;
  136. }
  137. }