PageRenderTime 43ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Composer/Package/Loader/RootPackageLoader.php

https://github.com/pborreli/composer
PHP | 277 lines | 208 code | 41 blank | 28 comment | 41 complexity | 58d9b252a98408932706865ad0d1fa7a MD5 | raw file
  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\Package\Loader;
  12. use Composer\Package\BasePackage;
  13. use Composer\Config;
  14. use Composer\Factory;
  15. use Composer\Package\Version\VersionParser;
  16. use Composer\Repository\RepositoryManager;
  17. use Composer\Repository\Vcs\HgDriver;
  18. use Composer\IO\NullIO;
  19. use Composer\Util\ProcessExecutor;
  20. /**
  21. * ArrayLoader built for the sole purpose of loading the root package
  22. *
  23. * Sets additional defaults and loads repositories
  24. *
  25. * @author Jordi Boggiano <j.boggiano@seld.be>
  26. */
  27. class RootPackageLoader extends ArrayLoader
  28. {
  29. private $manager;
  30. private $config;
  31. private $process;
  32. public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, ProcessExecutor $process = null)
  33. {
  34. $this->manager = $manager;
  35. $this->config = $config;
  36. $this->process = $process ?: new ProcessExecutor();
  37. parent::__construct($parser);
  38. }
  39. public function load(array $config, $class = 'Composer\Package\RootPackage')
  40. {
  41. if (!isset($config['name'])) {
  42. $config['name'] = '__root__';
  43. }
  44. if (!isset($config['version'])) {
  45. // override with env var if available
  46. if (getenv('COMPOSER_ROOT_VERSION')) {
  47. $version = getenv('COMPOSER_ROOT_VERSION');
  48. } else {
  49. $version = $this->guessVersion($config);
  50. }
  51. if (!$version) {
  52. $version = '1.0.0';
  53. }
  54. $config['version'] = $version;
  55. } else {
  56. $version = $config['version'];
  57. }
  58. $package = parent::load($config, $class);
  59. $aliases = array();
  60. $stabilityFlags = array();
  61. $references = array();
  62. foreach (array('require', 'require-dev') as $linkType) {
  63. if (isset($config[$linkType])) {
  64. $linkInfo = BasePackage::$supportedLinkTypes[$linkType];
  65. $method = 'get'.ucfirst($linkInfo['method']);
  66. $links = array();
  67. foreach ($package->$method() as $link) {
  68. $links[$link->getTarget()] = $link->getConstraint()->getPrettyString();
  69. }
  70. $aliases = $this->extractAliases($links, $aliases);
  71. $stabilityFlags = $this->extractStabilityFlags($links, $stabilityFlags);
  72. $references = $this->extractReferences($links, $references);
  73. }
  74. }
  75. $package->setAliases($aliases);
  76. $package->setStabilityFlags($stabilityFlags);
  77. $package->setReferences($references);
  78. if (isset($config['minimum-stability'])) {
  79. $package->setMinimumStability(VersionParser::normalizeStability($config['minimum-stability']));
  80. }
  81. $repos = Factory::createDefaultRepositories(null, $this->config, $this->manager);
  82. foreach ($repos as $repo) {
  83. $this->manager->addRepository($repo);
  84. }
  85. $package->setRepositories($this->config->getRepositories());
  86. return $package;
  87. }
  88. private function extractAliases(array $requires, array $aliases)
  89. {
  90. foreach ($requires as $reqName => $reqVersion) {
  91. if (preg_match('{^([^,\s]+) +as +([^,\s]+)$}', $reqVersion, $match)) {
  92. $aliases[] = array(
  93. 'package' => strtolower($reqName),
  94. 'version' => $this->versionParser->normalize($match[1], $reqVersion),
  95. 'alias' => $match[2],
  96. 'alias_normalized' => $this->versionParser->normalize($match[2], $reqVersion),
  97. );
  98. }
  99. }
  100. return $aliases;
  101. }
  102. private function extractStabilityFlags(array $requires, array $stabilityFlags)
  103. {
  104. $stabilities = BasePackage::$stabilities;
  105. foreach ($requires as $reqName => $reqVersion) {
  106. // parse explicit stability flags
  107. if (preg_match('{^[^,\s]*?@('.implode('|', array_keys($stabilities)).')$}i', $reqVersion, $match)) {
  108. $name = strtolower($reqName);
  109. $stability = $stabilities[VersionParser::normalizeStability($match[1])];
  110. if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) {
  111. continue;
  112. }
  113. $stabilityFlags[$name] = $stability;
  114. continue;
  115. }
  116. // infer flags for requirements that have an explicit -dev or -beta version specified for example
  117. $reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
  118. if (preg_match('{^[^,\s@]+$}', $reqVersion) && 'stable' !== ($stabilityName = VersionParser::parseStability($reqVersion))) {
  119. $name = strtolower($reqName);
  120. $stability = $stabilities[$stabilityName];
  121. if (isset($stabilityFlags[$name]) && $stabilityFlags[$name] > $stability) {
  122. continue;
  123. }
  124. $stabilityFlags[$name] = $stability;
  125. }
  126. }
  127. return $stabilityFlags;
  128. }
  129. private function extractReferences(array $requires, array $references)
  130. {
  131. foreach ($requires as $reqName => $reqVersion) {
  132. $reqVersion = preg_replace('{^([^,\s@]+) as .+$}', '$1', $reqVersion);
  133. if (preg_match('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) && 'dev' === ($stabilityName = VersionParser::parseStability($reqVersion))) {
  134. $name = strtolower($reqName);
  135. $references[$name] = $match[1];
  136. }
  137. }
  138. return $references;
  139. }
  140. private function guessVersion(array $config)
  141. {
  142. if (function_exists('proc_open')) {
  143. $version = $this->guessGitVersion($config);
  144. if (null !== $version) {
  145. return $version;
  146. }
  147. return $this->guessHgVersion($config);
  148. }
  149. }
  150. private function guessGitVersion(array $config)
  151. {
  152. // try to fetch current version from git branch
  153. if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output)) {
  154. $branches = array();
  155. $isFeatureBranch = false;
  156. $version = null;
  157. // find current branch and collect all branch names
  158. foreach ($this->process->splitLines($output) as $branch) {
  159. if ($branch && preg_match('{^(?:\* ) *(?:[^/ ]+?/)?(\S+|\(no branch\)) *([a-f0-9]+) .*$}', $branch, $match)) {
  160. if ($match[1] === '(no branch)') {
  161. $version = 'dev-'.$match[2];
  162. $isFeatureBranch = true;
  163. } else {
  164. $version = $this->versionParser->normalizeBranch($match[1]);
  165. $isFeatureBranch = 0 === strpos($version, 'dev-');
  166. if ('9999999-dev' === $version) {
  167. $version = 'dev-'.$match[1];
  168. }
  169. }
  170. }
  171. if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) {
  172. if (preg_match('{^(?:\* )? *(?:[^/ ]+?/)?(\S+) *([a-f0-9]+) .*$}', $branch, $match)) {
  173. $branches[] = $match[1];
  174. }
  175. }
  176. }
  177. if (!$isFeatureBranch) {
  178. return $version;
  179. }
  180. // try to find the best (nearest) version branch to assume this feature's version
  181. $version = $this->guessFeatureVersion($config, $version, $branches, 'git rev-list %candidate%..%branch%');
  182. return $version;
  183. }
  184. }
  185. private function guessHgVersion(array $config)
  186. {
  187. // try to fetch current version from hg branch
  188. if (0 === $this->process->execute('hg branch', $output)) {
  189. $branch = trim($output);
  190. $version = $this->versionParser->normalizeBranch($branch);
  191. $isFeatureBranch = 0 === strpos($version, 'dev-');
  192. if ('9999999-dev' === $version) {
  193. $version = 'dev-'.$branch;
  194. }
  195. if (!$isFeatureBranch) {
  196. return $version;
  197. }
  198. // re-use the HgDriver to fetch branches (this properly includes bookmarks)
  199. $config = array('url' => getcwd());
  200. $driver = new HgDriver($config, new NullIO(), $this->config, $this->process);
  201. $branches = array_keys($driver->getBranches());
  202. // try to find the best (nearest) version branch to assume this feature's version
  203. $version = $this->guessFeatureVersion($config, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"');
  204. return $version;
  205. }
  206. }
  207. private function guessFeatureVersion(array $config, $version, array $branches, $scmCmdline)
  208. {
  209. // ignore feature branches if they have no branch-alias or self.version is used
  210. // and find the branch they came from to use as a version instead
  211. if ((isset($config['extra']['branch-alias']) && !isset($config['extra']['branch-alias'][$version]))
  212. || strpos(json_encode($config), '"self.version"')
  213. ) {
  214. $branch = preg_replace('{^dev-}', '', $version);
  215. $length = PHP_INT_MAX;
  216. foreach ($branches as $candidate) {
  217. // do not compare against other feature branches
  218. if ($candidate === $branch || !preg_match('{^(master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) {
  219. continue;
  220. }
  221. $cmdLine = str_replace(array('%candidate%', '%branch%'), array($candidate, $branch), $scmCmdline);
  222. if (0 !== $this->process->execute($cmdLine, $output)) {
  223. continue;
  224. }
  225. if (strlen($output) < $length) {
  226. $length = strlen($output);
  227. $version = $this->versionParser->normalizeBranch($candidate);
  228. if ('9999999-dev' === $version) {
  229. $version = 'dev-'.$match[1];
  230. }
  231. }
  232. }
  233. }
  234. return $version;
  235. }
  236. }