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

/src/Pyrus/Package/Dependency/Set.php

https://github.com/CloCkWeRX/Pyrus
PHP | 284 lines | 201 code | 34 blank | 49 comment | 37 complexity | 94a5c14b67ddc131a11afa067e4c6185 MD5 | raw file
  1. <?php
  2. /**
  3. * \Pyrus\Dependency\Set
  4. *
  5. * PHP version 5
  6. *
  7. * @category Pyrus
  8. * @package Pyrus
  9. * @author Greg Beaver <cellog@php.net>
  10. * @copyright 2010 The PEAR Group
  11. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  12. * @link https://github.com/pyrus/Pyrus
  13. */
  14. /**
  15. * Implements a set of dependency trees, and manipulates the trees to combine
  16. * them into a unique set of package releases to download
  17. *
  18. * This structure allows us to properly determine the best version, if any,
  19. * of a package that satisfies all dependencies of both packages to download and
  20. * installed packages.
  21. *
  22. * @category Pyrus
  23. * @package Pyrus
  24. * @author Greg Beaver <cellog@php.net>
  25. * @copyright 2010 The PEAR Group
  26. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  27. * @link https://github.com/pyrus/Pyrus
  28. */
  29. namespace Pyrus\Package\Dependency;
  30. class Set
  31. {
  32. protected $packageTrees = array();
  33. protected $duplicates = array();
  34. protected $optionalDeps = array();
  35. function __construct(array $packages)
  36. {
  37. Set\PackageTree::setLocalPackages($packages);
  38. foreach ($packages as $package) {
  39. $tree = new Set\PackageTree($this, $package);
  40. $this->packageTrees[] = $tree;
  41. while (!$tree->populate()) {
  42. $this->refine();
  43. }
  44. //echo $tree; // uncomment to get a map of each separate dep tree
  45. }
  46. }
  47. function retrieveAllPackages()
  48. {
  49. $ret = array();
  50. $this->optionalDeps = Set\PackageTree::getUnusedOptionalDeps();
  51. foreach ($this->packageTrees as $tree) {
  52. foreach ($tree->getPackageSet() as $package) {
  53. $name = $package->name();
  54. $ret[$name] = $package->node;
  55. if (isset($this->optionalDeps[$name])) {
  56. unset($this->optionalDeps[$name]);
  57. }
  58. }
  59. }
  60. return $ret;
  61. }
  62. function synchronizeDeps()
  63. {
  64. foreach ($this->packageTrees as $tree) {
  65. $tree->synchronize();
  66. //echo $tree; // uncomment to get a map of each separate dep tree
  67. }
  68. }
  69. function refine()
  70. {
  71. $dirty = Set\PackageTree::dirtyNodes();
  72. while (count($dirty)) {
  73. foreach ($dirty as $i => $node) {
  74. foreach ($this->packageTrees as $package) {
  75. if (!$package->has($node)) {
  76. continue;
  77. }
  78. $package->rebuildIfNecessary($node);
  79. }
  80. $dirty[$i] = null;
  81. }
  82. $dirty = array_filter($dirty);
  83. }
  84. }
  85. function getIgnoredOptionalDeps()
  86. {
  87. return $this->optionalDeps;
  88. }
  89. function getDependencies(\Pyrus\Package $info)
  90. {
  91. $deps = array();
  92. foreach ($this->packageTrees as $tree) {
  93. $deps = $tree->getDependencies($deps, $info->channel . '/' . $info->name);
  94. }
  95. return array_merge($deps, $this->getDependenciesOn($info));
  96. }
  97. function getDependenciesOn($info)
  98. {
  99. $name = $info->name;
  100. $channel = $info->channel;
  101. $packages = \Pyrus\Config::current()->registry
  102. ->getDependentPackages($info->getPackageFileObject());
  103. $ret = array();
  104. foreach ($packages as $package) {
  105. $deps = $package->dependencies;
  106. foreach (array('package', 'subpackage') as $type) {
  107. foreach (array('required', 'optional') as $required) {
  108. foreach ($deps[$required]->$type as $dep) {
  109. if ($dep->channel != $channel || $dep->name != $name) {
  110. continue;
  111. }
  112. $ret[] = $dep;
  113. }
  114. }
  115. }
  116. }
  117. return $ret;
  118. }
  119. /**
  120. * Return a composite dependency on the package, as defined by combining
  121. * all dependencies on this package into one.
  122. *
  123. * As an example, for these dependencies:
  124. *
  125. * <pre>
  126. * P1 version >= 1.2.0
  127. * P1 version <= 3.0.0, != 2.3.2
  128. * P1 version >= 1.1.0, != 1.2.0
  129. * </pre>
  130. *
  131. * The composite dependency is
  132. *
  133. * <pre>
  134. * P1 version >= 1.2.0, <= 3.0.0, != 2.3.2, 1.2.0
  135. * </pre>
  136. * @param Pyrus\Package $info
  137. * @param bool $conflicting if true, return a composite <conflicts> dependency, if any
  138. */
  139. function getCompositeDependency(\Pyrus\Package $info, $conflicting = false)
  140. {
  141. $deps = $this->getDependencies($info);
  142. if (!count($deps)) {
  143. $dep = new \Pyrus\PackageFile\v2\Dependencies\Package(
  144. 'required', 'package', null, array('name' => $info->name, 'channel' => $info->channel, 'uri' => null,
  145. 'min' => null, 'max' => null,
  146. 'recommended' => null, 'exclude' => null,
  147. 'providesextension' => null, 'conflicts' => null), 0);
  148. $dep->setCompositeSources(array());
  149. return $dep;
  150. }
  151. $compdep = array('name' => $info->name, 'channel' => $info->channel, 'uri' => null,
  152. 'min' => null, 'max' => null,
  153. 'recommended' => null, 'exclude' => null,
  154. 'providesextension' => null, 'conflicts' => null);
  155. $initial = true;
  156. $max = $min = $recommended = null;
  157. $useddeps = array();
  158. foreach ($deps as $actualdep) {
  159. if ($conflicting) {
  160. if (!$actualdep->conflicts) {
  161. continue;
  162. }
  163. $compdep['conflicts'] = '';
  164. } elseif ($actualdep->conflicts) {
  165. continue;
  166. }
  167. $useddeps[] = $actualdep;
  168. $deppackage = $actualdep->getPackageFile()->channel . '/' .
  169. $actualdep->getPackageFile()->name;
  170. if ($initial) {
  171. if ($actualdep->min) {
  172. $compdep['min'] = $actualdep->min;
  173. $min = $deppackage;
  174. }
  175. if ($actualdep->max) {
  176. $compdep['max'] = $actualdep->max;
  177. $max = $deppackage;
  178. }
  179. if ($actualdep->recommended) {
  180. $compdep['recommended'] = $actualdep->recommended;
  181. $recommended = $deppackage;
  182. }
  183. $compdep['exclude'] = $actualdep->exclude;
  184. $initial = false;
  185. continue;
  186. }
  187. if (isset($compdep['recommended']) && isset($actualdep->recommended)
  188. && $actualdep->recommended != $compdep['recommended']) {
  189. throw new \Pyrus\Package\Exception('Cannot install ' . $info->channel . '/' .
  190. $info->name . ', two dependencies conflict (different recommended values for ' .
  191. $deppackage . ' and ' . $recommended . ')');
  192. }
  193. if ($compdep['max'] && $actualdep->min && version_compare($actualdep->min, $compdep['max'], '>')) {
  194. throw new \Pyrus\Package\Exception('Cannot install ' . $info->channel . '/' .
  195. $info->name . ', two dependencies conflict (' .
  196. $deppackage . ' min is > ' . $max . ' max)');
  197. }
  198. if ($compdep['min'] && $actualdep->max && version_compare($actualdep->max, $compdep['min'], '<')) {
  199. throw new \Pyrus\Package\Exception('Cannot install ' . $info->channel . '/' .
  200. $info->name . ', two dependencies conflict (' .
  201. $deppackage . ' max is < ' . $min . ' min)');
  202. }
  203. if ($actualdep->min) {
  204. if ($compdep['min']) {
  205. if (version_compare($actualdep->min, $compdep['min'], '>')) {
  206. $compdep['min'] = $actualdep->min;
  207. $min = $deppackage;
  208. }
  209. } else {
  210. $compdep['min'] = $actualdep->min;
  211. $min = $deppackage;
  212. }
  213. }
  214. if ($actualdep->max) {
  215. if ($compdep['max']) {
  216. if (version_compare($actualdep->max, $compdep['max'], '<')) {
  217. $compdep['max'] = $actualdep->max;
  218. $max = $deppackage;
  219. }
  220. } else {
  221. $compdep['max'] = $actualdep->max;
  222. $max = $deppackage;
  223. }
  224. }
  225. if ($actualdep->recommended) {
  226. $compdep['recommended'] = $actualdep->recommended;
  227. $recommended = $deppackage;
  228. }
  229. if ($actualdep->exclude) {
  230. if (!$compdep['exclude']) {
  231. $compdep['exclude'] = array();
  232. foreach ($actualdep->exclude as $exclude) {
  233. $compdep['exclude'][] = $exclude;
  234. }
  235. continue;
  236. }
  237. foreach ($actualdep->exclude as $exclude) {
  238. if (in_array($exclude, $compdep['exclude'])) {
  239. continue;
  240. }
  241. $compdep['exclude'][] = $exclude;
  242. }
  243. }
  244. }
  245. $dep = new \Pyrus\PackageFile\v2\Dependencies\Package(
  246. 'required', 'package', null, $compdep, 0);
  247. $dep->setCompositeSources($useddeps);
  248. return $dep;
  249. }
  250. }