ConfigurableComposer.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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\Repository;
  13. use Composer\Package;
  14. use Composer\Installer\LibraryInstaller;
  15. /**
  16. * @author Konstantin Kudryashiv <ever.zet@gmail.com>
  17. */
  18. class ConfigurableComposer extends Composer
  19. {
  20. private $configFile;
  21. private $lockFile;
  22. private $isLocked = false;
  23. private $lockedPackages = array();
  24. public function __construct($configFile = 'composer.json', $lockFile = 'composer.lock')
  25. {
  26. $this->configFile = $configFile;
  27. $this->lockFile = $lockFile;
  28. $this->setRepository('Platform', new Repository\PlatformRepository());
  29. if (!file_exists($configFile)) {
  30. throw new \UnexpectedValueException('Can not find composer config file');
  31. }
  32. $config = $this->loadJsonConfig($configFile);
  33. if (isset($config['path'])) {
  34. $this->setInstaller('library', new LibraryInstaller($config['path']));
  35. } else {
  36. $this->setInstaller('library', new LibraryInstaller());
  37. }
  38. if (isset($config['repositories'])) {
  39. $repositories = $this->loadRepositoriesFromConfig($config['repositories'])
  40. foreach ($repositories as $name => $repository) {
  41. $this->setRepository($name, $repository);
  42. }
  43. }
  44. if (isset($config['require'])) {
  45. $requirements = $this->loadRequirementsFromConfig($config['require']);
  46. foreach ($requirements as $name => $constraint) {
  47. $this->setRequirement($name, $constraint);
  48. }
  49. }
  50. if (file_exists($lockFile)) {
  51. $lock = $this->loadJsonConfig($lockFile);
  52. $platform = $this->getRepository('Platform');
  53. $packages = $this->loadPackagesFromLock($lock);
  54. foreach ($packages as $package) {
  55. if ($this->hasRequirement($package->getName())) {
  56. $platform->addPackage($package);
  57. $this->lockedPackages[] = $package;
  58. }
  59. }
  60. $this->isLocked = true;
  61. }
  62. }
  63. public function isLocked()
  64. {
  65. return $this->isLocked;
  66. }
  67. public function getLockedPackages()
  68. {
  69. return $this->lockedPackages;
  70. }
  71. public function lock(array $packages)
  72. {
  73. // TODO: write installed packages info into $this->lockFile
  74. }
  75. private function loadPackagesFromLock(array $lockList)
  76. {
  77. $packages = array();
  78. foreach ($lockList as $info) {
  79. $packages[] = new Package\MemoryPackage($info['package'], $info['version']);
  80. }
  81. return $packages;
  82. }
  83. private function loadRepositoriesFromConfig(array $repositoriesList)
  84. {
  85. $repositories = array();
  86. foreach ($repositoriesList as $name => $spec) {
  87. if (is_array($spec) && count($spec) === 1) {
  88. $repositories[$name] = $this->createRepository($name, key($spec), current($spec));
  89. } elseif (null === $spec) {
  90. $repositories[$name] = null;
  91. } else {
  92. throw new \UnexpectedValueException(
  93. 'Invalid repositories specification '.
  94. json_encode($spec).', should be: {"type": "url"}'
  95. );
  96. }
  97. }
  98. return $repositories;
  99. }
  100. private function loadRequirementsFromConfig(array $requirementsList)
  101. {
  102. $requirements = array();
  103. foreach ($requirementsList as $name => $version) {
  104. $name = $this->lowercase($name);
  105. if ('latest' === $version) {
  106. $requirements[$name] = null;
  107. } else {
  108. preg_match('#^([>=<~]*)([\d.]+.*)$#', $version, $match);
  109. if (!$match[1]) {
  110. $match[1] = '=';
  111. }
  112. $constraint = new Package\LinkConstraint\VersionConstraint($match[1], $match[2]);
  113. $requirements[$name] = $constraint;
  114. }
  115. }
  116. return $requirements;
  117. }
  118. private function loadJsonConfig($configFile)
  119. {
  120. $config = json_decode(file_get_contents($configFile), true);
  121. if (!$config) {
  122. switch (json_last_error()) {
  123. case JSON_ERROR_NONE:
  124. $msg = 'No error has occurred, is your composer.json file empty?';
  125. break;
  126. case JSON_ERROR_DEPTH:
  127. $msg = 'The maximum stack depth has been exceeded';
  128. break;
  129. case JSON_ERROR_STATE_MISMATCH:
  130. $msg = 'Invalid or malformed JSON';
  131. break;
  132. case JSON_ERROR_CTRL_CHAR:
  133. $msg = 'Control character error, possibly incorrectly encoded';
  134. break;
  135. case JSON_ERROR_SYNTAX:
  136. $msg = 'Syntax error';
  137. break;
  138. case JSON_ERROR_UTF8:
  139. $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded';
  140. break;
  141. }
  142. throw new \UnexpectedValueException('Incorrect composer.json file: '.$msg);
  143. }
  144. return $config;
  145. }
  146. private function lowercase($str)
  147. {
  148. if (function_exists('mb_strtolower')) {
  149. return mb_strtolower($str, 'UTF-8');
  150. }
  151. return strtolower($str, 'UTF-8');
  152. }
  153. private function createRepository($name, $type, $spec)
  154. {
  155. if (is_string($spec)) {
  156. $spec = array('url' => $spec);
  157. }
  158. $spec['url'] = rtrim($spec['url'], '/');
  159. switch ($type) {
  160. case 'git-bare':
  161. case 'git-multi':
  162. throw new \Exception($type.' repositories not supported yet');
  163. case 'git':
  164. return new Repository\GitRepository($spec['url']);
  165. case 'composer':
  166. return new Repository\ComposerRepository($spec['url']);
  167. case 'pear':
  168. return new Repository\PearRepository($spec['url'], $name);
  169. default:
  170. throw new \UnexpectedValueException(
  171. 'Unknown repository type: '.$type.', could not create repository '.$name
  172. );
  173. }
  174. }
  175. }