InstallCommand.php 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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\Command;
  12. use Composer\DependencyResolver\Pool;
  13. use Composer\DependencyResolver\Request;
  14. use Composer\DependencyResolver\DefaultPolicy;
  15. use Composer\DependencyResolver\Solver;
  16. use Composer\Repository\PlatformRepository;
  17. use Composer\Package\MemoryPackage;
  18. use Composer\Package\LinkConstraint\VersionConstraint;
  19. /**
  20. * @author Jordi Boggiano <j.boggiano@seld.be>
  21. */
  22. class InstallCommand
  23. {
  24. protected $composer;
  25. public function install($composer)
  26. {
  27. $this->composer = $composer;
  28. // TODO this needs a parameter to enable installing from source (i.e. git clone, instead of downloading archives)
  29. $sourceInstall = false;
  30. $config = $this->loadConfig();
  31. echo 'Loading repositories'.PHP_EOL;
  32. if (isset($config['repositories'])) {
  33. foreach ($config['repositories'] as $name => $spec) {
  34. $composer->addRepository($name, $spec);
  35. }
  36. }
  37. $pool = new Pool;
  38. $repoInstalled = new PlatformRepository;
  39. $pool->addRepository($repoInstalled);
  40. // TODO check the lock file to see what's currently installed
  41. // $repoInstalled->addPackage(new MemoryPackage('$Package', '$Version'));
  42. echo 'Loading package list'.PHP_EOL;
  43. foreach ($composer->getRepositories() as $repository) {
  44. $pool->addRepository($repository);
  45. }
  46. $request = new Request($pool);
  47. echo 'Building up request'.PHP_EOL;
  48. // TODO there should be an update flag or dedicated update command
  49. // TODO check lock file to remove packages that disappeared from the requirements
  50. foreach ($config['require'] as $name => $version) {
  51. if ('latest' === $version) {
  52. $request->install($name);
  53. } else {
  54. preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match);
  55. if (!$match[1]) {
  56. $match[1] = '=';
  57. }
  58. $constraint = new VersionConstraint($match[1], $match[2]);
  59. $request->install($name, $constraint);
  60. }
  61. }
  62. echo 'Solving dependencies'.PHP_EOL;
  63. $policy = new DefaultPolicy;
  64. $solver = new Solver($policy, $pool, $repoInstalled);
  65. $transaction = $solver->solve($request);
  66. $lock = array();
  67. foreach ($transaction as $task) {
  68. switch ($task['job']) {
  69. case 'install':
  70. $package = $task['package'];
  71. echo '> Installing '.$package->getName().PHP_EOL;
  72. if ($sourceInstall) {
  73. // TODO
  74. } else {
  75. if ($package->getDistType()) {
  76. $downloaderType = $package->getDistType();
  77. $type = 'dist';
  78. } elseif ($package->getSourceType()) {
  79. echo 'Package '.$package->getName().' has no dist url, installing from source instead.';
  80. $downloaderType = $package->getSourceType();
  81. $type = 'source';
  82. } else {
  83. throw new \UnexpectedValueException('Package '.$package->getName().' has no source or dist URL.');
  84. }
  85. $downloader = $composer->getDownloader($downloaderType);
  86. $installer = $composer->getInstaller($package->getType());
  87. if (!$installer->install($package, $downloader, $type)) {
  88. throw new \LogicException($package->getName().' could not be installed.');
  89. }
  90. }
  91. $lock[$package->getName()] = array('version' => $package->getVersion());
  92. break;
  93. default:
  94. throw new \UnexpectedValueException('Unhandled job type : '.$task['job']);
  95. }
  96. }
  97. echo '> Done'.PHP_EOL;
  98. $this->storeLockFile($lock);
  99. }
  100. protected function loadConfig()
  101. {
  102. if (!file_exists('composer.json')) {
  103. throw new \UnexpectedValueException('composer.json config file not found in '.getcwd());
  104. }
  105. $config = json_decode(file_get_contents('composer.json'), true);
  106. if (!$config) {
  107. switch (json_last_error()) {
  108. case JSON_ERROR_NONE:
  109. $msg = 'No error has occurred, is your composer.json file empty?';
  110. break;
  111. case JSON_ERROR_DEPTH:
  112. $msg = 'The maximum stack depth has been exceeded';
  113. break;
  114. case JSON_ERROR_STATE_MISMATCH:
  115. $msg = 'Invalid or malformed JSON';
  116. break;
  117. case JSON_ERROR_CTRL_CHAR:
  118. $msg = 'Control character error, possibly incorrectly encoded';
  119. break;
  120. case JSON_ERROR_SYNTAX:
  121. $msg = 'Syntax error';
  122. break;
  123. case JSON_ERROR_UTF8:
  124. $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
  125. break;
  126. }
  127. throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg);
  128. }
  129. return $config;
  130. }
  131. protected function storeLockFile(array $content)
  132. {
  133. file_put_contents('composer.lock', json_encode($content)."\n");
  134. echo '> composer.lock dumped'.PHP_EOL;
  135. }
  136. }