Install.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  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;
  12. use Composer\Autoload\AutoloadGenerator;
  13. use Composer\DependencyResolver\DefaultPolicy;
  14. use Composer\DependencyResolver\Operation\UpdateOperation;
  15. use Composer\DependencyResolver\Pool;
  16. use Composer\DependencyResolver\Request;
  17. use Composer\DependencyResolver\Solver;
  18. use Composer\IO\IOInterface;
  19. use Composer\Package\AliasPackage;
  20. use Composer\Package\Link;
  21. use Composer\Package\LinkConstraint\VersionConstraint;
  22. use Composer\Package\PackageInterface;
  23. use Composer\Repository\CompositeRepository;
  24. use Composer\Repository\PlatformRepository;
  25. use Composer\Repository\RepositoryInterface;
  26. use Composer\Script\EventDispatcher;
  27. use Composer\Script\ScriptEvents;
  28. class Install
  29. {
  30. /**
  31. * Run installation (or update)
  32. *
  33. * @param IOInterface $io
  34. * @param Composer $composer
  35. * @param EventDispatcher $eventDispatcher
  36. * @param bool $preferSource
  37. * @param bool $dryRun
  38. * @param bool $verbose
  39. * @param bool $noInstallRecommends
  40. * @param bool $installSuggests
  41. * @param bool $update
  42. * @param RepositoryInterface $additionalInstalledRepository
  43. */
  44. public function run(IOInterface $io, Composer $composer, EventDispatcher $eventDispatcher, $preferSource = false, $dryRun = false, $verbose = false, $noInstallRecommends = false, $installSuggests = false, $update = false, RepositoryInterface $additionalInstalledRepository = null)
  45. {
  46. if ($dryRun) {
  47. $verbose = true;
  48. }
  49. if ($preferSource) {
  50. $composer->getDownloadManager()->setPreferSource(true);
  51. }
  52. $repoManager = $composer->getRepositoryManager();
  53. // create local repo, this contains all packages that are installed in the local project
  54. $localRepo = $repoManager->getLocalRepository();
  55. // create installed repo, this contains all local packages + platform packages (php & extensions)
  56. $installedRepo = new CompositeRepository(array($localRepo, new PlatformRepository()));
  57. if ($additionalInstalledRepository) {
  58. $installedRepo->addRepository($additionalInstalledRepository);
  59. }
  60. // prepare aliased packages
  61. if (!$update && $composer->getLocker()->isLocked()) {
  62. $aliases = $composer->getLocker()->getAliases();
  63. } else {
  64. $aliases = $composer->getPackage()->getAliases();
  65. }
  66. foreach ($aliases as $alias) {
  67. foreach ($repoManager->findPackages($alias['package'], $alias['version']) as $package) {
  68. $package->getRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias']));
  69. }
  70. foreach ($repoManager->getLocalRepository()->findPackages($alias['package'], $alias['version']) as $package) {
  71. $repoManager->getLocalRepository()->addPackage(new AliasPackage($package, $alias['alias_normalized'], $alias['alias']));
  72. $repoManager->getLocalRepository()->removePackage($package);
  73. }
  74. }
  75. // creating repository pool
  76. $pool = new Pool;
  77. $pool->addRepository($installedRepo);
  78. foreach ($repoManager->getRepositories() as $repository) {
  79. $pool->addRepository($repository);
  80. }
  81. // dispatch pre event
  82. if (!$dryRun) {
  83. $eventName = $update ? ScriptEvents::PRE_UPDATE_CMD : ScriptEvents::PRE_INSTALL_CMD;
  84. $eventDispatcher->dispatchCommandEvent($eventName);
  85. }
  86. // creating requirements request
  87. $installFromLock = false;
  88. $request = new Request($pool);
  89. if ($update) {
  90. $io->write('<info>Updating dependencies</info>');
  91. $request->updateAll();
  92. $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests);
  93. foreach ($links as $link) {
  94. $request->install($link->getTarget(), $link->getConstraint());
  95. }
  96. } elseif ($composer->getLocker()->isLocked()) {
  97. $installFromLock = true;
  98. $io->write('<info>Installing from lock file</info>');
  99. if (!$composer->getLocker()->isFresh()) {
  100. $io->write('<warning>Your lock file is out of sync with your composer.json, run "composer.phar update" to update dependencies</warning>');
  101. }
  102. foreach ($composer->getLocker()->getLockedPackages() as $package) {
  103. $version = $package->getVersion();
  104. foreach ($aliases as $alias) {
  105. if ($alias['package'] === $package->getName() && $alias['version'] === $package->getVersion()) {
  106. $version = $alias['alias'];
  107. break;
  108. }
  109. }
  110. $constraint = new VersionConstraint('=', $version);
  111. $request->install($package->getName(), $constraint);
  112. }
  113. } else {
  114. $io->write('<info>Installing dependencies</info>');
  115. $links = $this->collectLinks($composer->getPackage(), $noInstallRecommends, $installSuggests);
  116. foreach ($links as $link) {
  117. $request->install($link->getTarget(), $link->getConstraint());
  118. }
  119. }
  120. // prepare solver
  121. $installationManager = $composer->getInstallationManager();
  122. $policy = new DependencyResolver\DefaultPolicy();
  123. $solver = new DependencyResolver\Solver($policy, $pool, $installedRepo);
  124. // solve dependencies
  125. $operations = $solver->solve($request);
  126. // force dev packages to be updated to latest reference on update
  127. if ($update) {
  128. foreach ($localRepo->getPackages() as $package) {
  129. if ($package instanceof AliasPackage) {
  130. $package = $package->getAliasOf();
  131. }
  132. // skip non-dev packages
  133. if (!$package->isDev()) {
  134. continue;
  135. }
  136. // skip packages that will be updated/uninstalled
  137. foreach ($operations as $operation) {
  138. if (('update' === $operation->getJobType() && $package === $operation->getInitialPackage())
  139. || ('uninstall' === $operation->getJobType() && $package === $operation->getPackage())
  140. ) {
  141. continue 2;
  142. }
  143. }
  144. // force update
  145. $newPackage = $repoManager->findPackage($package->getName(), $package->getVersion());
  146. if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) {
  147. $operations[] = new UpdateOperation($package, $newPackage);
  148. }
  149. }
  150. }
  151. // anti-alias local repository to allow updates to work fine
  152. foreach ($repoManager->getLocalRepository()->getPackages() as $package) {
  153. if ($package instanceof AliasPackage) {
  154. $repoManager->getLocalRepository()->addPackage(clone $package->getAliasOf());
  155. $repoManager->getLocalRepository()->removePackage($package);
  156. }
  157. }
  158. // execute operations
  159. if (!$operations) {
  160. $io->write('<info>Nothing to install/update</info>');
  161. }
  162. foreach ($operations as $operation) {
  163. if ($verbose) {
  164. $io->write((string) $operation);
  165. }
  166. if (!$dryRun) {
  167. $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::PRE_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
  168. // if installing from lock, restore dev packages' references to their locked state
  169. if ($installFromLock) {
  170. $package = null;
  171. if ('update' === $operation->getJobType()) {
  172. $package = $operation->getTargetPackage();
  173. } elseif ('install' === $operation->getJobType()) {
  174. $package = $operation->getPackage();
  175. }
  176. if ($package && $package->isDev()) {
  177. $lockData = $composer->getLocker()->getLockData();
  178. foreach ($lockData['packages'] as $lockedPackage) {
  179. if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) {
  180. $package->setSourceReference($lockedPackage['source-reference']);
  181. break;
  182. }
  183. }
  184. }
  185. }
  186. $installationManager->execute($operation);
  187. $eventDispatcher->dispatchPackageEvent(constant('Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType())), $operation);
  188. }
  189. }
  190. if (!$dryRun) {
  191. if ($update || !$composer->getLocker()->isLocked()) {
  192. $composer->getLocker()->setLockData($localRepo->getPackages(), $aliases);
  193. $io->write('<info>Writing lock file</info>');
  194. }
  195. $localRepo->write();
  196. $io->write('<info>Generating autoload files</info>');
  197. $generator = new AutoloadGenerator;
  198. $generator->dump($localRepo, $composer->getPackage(), $installationManager, $installationManager->getVendorPath().'/.composer');
  199. // dispatch post event
  200. $eventName = $update ? ScriptEvents::POST_UPDATE_CMD : ScriptEvents::POST_INSTALL_CMD;
  201. $eventDispatcher->dispatchCommandEvent($eventName);
  202. }
  203. }
  204. private function collectLinks(PackageInterface $package, $noInstallRecommends, $installSuggests)
  205. {
  206. $links = $package->getRequires();
  207. if (!$noInstallRecommends) {
  208. $links = array_merge($links, $package->getRecommends());
  209. }
  210. if ($installSuggests) {
  211. $links = array_merge($links, $package->getSuggests());
  212. }
  213. return $links;
  214. }
  215. }