RepositorySet.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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\DependencyResolver\Pool;
  13. use Composer\DependencyResolver\PoolBuilder;
  14. use Composer\DependencyResolver\Request;
  15. use Composer\Package\BasePackage;
  16. use Composer\Package\Version\VersionParser;
  17. use Composer\Repository\CompositeRepository;
  18. use Composer\Repository\PlatformRepository;
  19. use Composer\Repository\LockArrayRepository;
  20. use Composer\Repository\InstalledRepositoryInterface;
  21. use Composer\Semver\Constraint\ConstraintInterface;
  22. use Composer\Package\Version\StabilityFilter;
  23. /**
  24. * @author Nils Adermann <naderman@naderman.de>
  25. */
  26. class RepositorySet
  27. {
  28. /**
  29. * Packages which replace/provide the given name might be returned as well even if they do not match the name exactly
  30. */
  31. const ALLOW_PROVIDERS_REPLACERS = 1;
  32. /**
  33. * Packages are returned even though their stability does not match the required stability
  34. */
  35. const ALLOW_UNACCEPTABLE_STABILITIES = 2;
  36. /**
  37. * Packages will be looked up in all repositories, even after they have been found in a higher prio one
  38. */
  39. const ALLOW_SHADOWED_REPOSITORIES = 4;
  40. /** @var array */
  41. private $rootAliases;
  42. /** @var array */
  43. private $rootReferences;
  44. /** @var RepositoryInterface[] */
  45. private $repositories = array();
  46. private $acceptableStabilities;
  47. private $stabilityFlags;
  48. private $rootRequires;
  49. /** @var bool */
  50. private $locked = false;
  51. public function __construct(array $rootAliases = array(), array $rootReferences = array(), $minimumStability = 'stable', array $stabilityFlags = array(), array $rootRequires = array())
  52. {
  53. $this->rootAliases = $rootAliases;
  54. $this->rootReferences = $rootReferences;
  55. $this->acceptableStabilities = array();
  56. foreach (BasePackage::$stabilities as $stability => $value) {
  57. if ($value <= BasePackage::$stabilities[$minimumStability]) {
  58. $this->acceptableStabilities[$stability] = $value;
  59. }
  60. }
  61. $this->stabilityFlags = $stabilityFlags;
  62. $this->rootRequires = $rootRequires;
  63. foreach ($rootRequires as $name => $constraint) {
  64. if (preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $name)) {
  65. unset($this->rootRequires[$name]);
  66. }
  67. }
  68. }
  69. public function getRootRequires()
  70. {
  71. return $this->rootRequires;
  72. }
  73. /**
  74. * Adds a repository to this repository set
  75. *
  76. * The first repos added have a higher priority. As soon as a package is found in any
  77. * repository the search for that package ends, and following repos will not be consulted.
  78. *
  79. * @param RepositoryInterface $repo A package repository
  80. */
  81. public function addRepository(RepositoryInterface $repo)
  82. {
  83. if ($this->locked) {
  84. throw new \RuntimeException("Pool has already been created from this repository set, it cannot be modified anymore.");
  85. }
  86. if ($repo instanceof CompositeRepository) {
  87. $repos = $repo->getRepositories();
  88. } else {
  89. $repos = array($repo);
  90. }
  91. foreach ($repos as $repo) {
  92. $this->repositories[] = $repo;
  93. }
  94. }
  95. /**
  96. * Find packages providing or matching a name and optionally meeting a constraint in all repositories
  97. *
  98. * Returned in the order of repositories, matching priority
  99. *
  100. * @param string $name
  101. * @param ConstraintInterface|null $constraint
  102. * @param int $flags any of the ALLOW_* constants from this class to tweak what is returned
  103. * @return array
  104. */
  105. public function findPackages($name, ConstraintInterface $constraint = null, $flags = 0)
  106. {
  107. $exactMatch = ($flags & self::ALLOW_PROVIDERS_REPLACERS) === 0;
  108. $ignoreStability = ($flags & self::ALLOW_UNACCEPTABLE_STABILITIES) !== 0;
  109. $loadFromAllRepos = ($flags & self::ALLOW_SHADOWED_REPOSITORIES) !== 0;
  110. $packages = array();
  111. if ($loadFromAllRepos) {
  112. foreach ($this->repositories as $repository) {
  113. $packages[] = $repository->findPackages($name, $constraint) ?: array();
  114. }
  115. } else {
  116. foreach ($this->repositories as $repository) {
  117. $result = $repository->loadPackages(array($name => $constraint), $ignoreStability ? BasePackage::$stabilities : $this->acceptableStabilities, $ignoreStability ? array() : $this->stabilityFlags);
  118. $packages[] = $result['packages'];
  119. foreach ($result['namesFound'] as $nameFound) {
  120. // avoid loading the same package again from other repositories once it has been found
  121. if ($name === $nameFound) {
  122. break 2;
  123. }
  124. }
  125. }
  126. }
  127. $candidates = $packages ? call_user_func_array('array_merge', $packages) : array();
  128. $result = array();
  129. foreach ($candidates as $candidate) {
  130. if ($exactMatch && $candidate->getName() !== $name) {
  131. continue;
  132. }
  133. if (!$ignoreStability && $this->isPackageAcceptable($candidate->getNames(), $candidate->getStability())) {
  134. $result[] = $candidate;
  135. }
  136. }
  137. return $candidates;
  138. }
  139. public function getProviders($packageName)
  140. {
  141. foreach ($this->repositories as $repository) {
  142. if ($repository instanceof ComposerRepository) {
  143. if ($providers = $repository->getProviders($packageName)) {
  144. return $providers;
  145. }
  146. }
  147. }
  148. return array();
  149. }
  150. public function isPackageAcceptable($names, $stability)
  151. {
  152. return StabilityFilter::isPackageAcceptable($this->acceptableStabilities, $this->stabilityFlags, $names, $stability);
  153. }
  154. /**
  155. * Create a pool for dependency resolution from the packages in this repository set.
  156. *
  157. * @return Pool
  158. */
  159. public function createPool(Request $request)
  160. {
  161. $poolBuilder = new PoolBuilder($this->acceptableStabilities, $this->stabilityFlags, $this->rootAliases, $this->rootReferences);
  162. foreach ($this->repositories as $repo) {
  163. if ($repo instanceof InstalledRepositoryInterface) {
  164. throw new \LogicException('The pool can not accept packages from an installed repository');
  165. }
  166. }
  167. $this->locked = true;
  168. return $poolBuilder->buildPool($this->repositories, $request);
  169. }
  170. // TODO unify this with above in some simpler version without "request"?
  171. public function createPoolForPackage($packageName, LockArrayRepository $lockedRepo = null)
  172. {
  173. return $this->createPoolForPackages(array($packageName), $lockedRepo);
  174. }
  175. public function createPoolForPackages($packageNames, LockArrayRepository $lockedRepo = null)
  176. {
  177. $request = new Request($lockedRepo);
  178. foreach ($packageNames as $packageName) {
  179. $request->requireName($packageName);
  180. }
  181. return $this->createPool($request);
  182. }
  183. }