/bluebox/libraries/Package/Dependency.php

https://github.com/robertleeplummerjr/bluebox · PHP · 263 lines · 196 code · 56 blank · 11 comment · 26 complexity · e19513f09dbd7810d53d8d4eb80b6c21 MD5 · raw file

  1. <?php defined('SYSPATH') or die('No direct access allowed.');
  2. /**
  3. * @package Core/Libraries/Package
  4. * @author K Anderson <bitbashing@gmail.com>
  5. * @license Mozilla Public License (MPL)
  6. */
  7. class Package_Dependency
  8. {
  9. public static function validateIntegration($identifier)
  10. {
  11. $package = Package_Catalog::getPackageByIdentifier($identifier);
  12. $failures = array();
  13. foreach($package['required'] as $requirement => $conditions)
  14. {
  15. switch($requirement)
  16. {
  17. case 'not':
  18. foreach($conditions as $name => $condition)
  19. {
  20. if (!$dependency = Package_Catalog::getInstalledPackage($name))
  21. {
  22. try
  23. {
  24. // Hmmm, this is offly optimistic....
  25. $dependency = Package_Transaction::checkTransaction($name);
  26. }
  27. catch(Exception $e)
  28. {
  29. continue;
  30. }
  31. }
  32. if (self::compareVersion($dependency['version'], $condition))
  33. {
  34. Package_Message::log('debug', 'dependency restriction, ' .$package['packageName'] .' can not be installed with ' .$name .' version ' .$condition);
  35. Package_Message::set($package['displayName'] .' version ' .$package['version'] .' can not be installed with ' .$dependency['displayName'] .' version ' .$dependency['version'], 'error', $identifier);
  36. Package_Message::set($dependency['displayName'] .' conflicts with ' .$package['displayName'], 'alert', $dependency['identifier']);
  37. $failures['not'][$name] = $condition;
  38. }
  39. }
  40. break;
  41. case 'or':
  42. $failed = array();
  43. foreach($conditions as $name => $condition)
  44. {
  45. if (!$dependency = Package_Catalog::getInstalledPackage($name))
  46. {
  47. try
  48. {
  49. // Hmmm, this is offly optimistic....
  50. $dependency = Package_Transaction::checkTransaction($name);
  51. }
  52. catch(Exception $e)
  53. {
  54. continue;
  55. }
  56. }
  57. if (self::compareVersion($dependency['version'], $condition))
  58. {
  59. continue 2;
  60. }
  61. Package_Message::set($dependency['displayName'] .' is part of a series of packages, one of which must be installed for ' .$package['displayName'], 'info', $dependency['identifier']);
  62. $failed[$name] = $condition;
  63. }
  64. Package_Message::log('debug', 'dependency restriction ' .$package['packageName'] .' requires one to be installed -> ' .implode(', ', $failed));
  65. Package_Message::set($package['displayName'] .' version ' .$package['version'] .' requires one of the following to be install: ' .implode(', ', $failed), 'error', $identifier);
  66. $failures['or'] += $failed;
  67. break;
  68. default:
  69. if (!$dependency = Package_Catalog::getInstalledPackage($requirement))
  70. {
  71. try
  72. {
  73. // Hmmm, this is offly optimistic....
  74. $dependency = Package_Transaction::checkTransaction($requirement);
  75. }
  76. catch(Exception $e)
  77. {
  78. Package_Message::log('debug', 'dependency restriction ' .$package['packageName'] .' requires ' .$requirement .' version ' .$conditions .' but it is not installed');
  79. Package_Message::set($package['displayName'] .' version ' .$package['version'] .' requires ' .ucfirst($requirement) .' but it is not avaliable', 'error', $identifier);
  80. $failures['missing'][$requirement] = $conditions;
  81. continue;
  82. }
  83. }
  84. if (!self::compareVersion($dependency['version'], $conditions))
  85. {
  86. Package_Message::log('debug', 'dependency restriction ' .$package['packageName'] .' requires ' .$requirement .' version ' .$conditions);
  87. Package_Message::set($package['displayName'] .' version ' .$package['version'] .' requires ' .$dependency['displayName'] .' version ' . $conditions .' but it is not avaliable', 'error', $identifier);
  88. $failures['incompatible'][$requirement] = $conditions;
  89. }
  90. }
  91. }
  92. if (!empty($failures))
  93. {
  94. $dependencyException = new Package_Dependency_Exception('Package did not pass dependency validation');
  95. $dependencyException->loadFailures($failures);
  96. throw $dependencyException;
  97. }
  98. }
  99. public static function validateAbandon($identifier)
  100. {
  101. $package = Package_Catalog::getPackageByIdentifier($identifier);
  102. if (!empty($package['denyRemoval']))
  103. {
  104. Package_Message::log('debug', 'denyRemoval flag set on package ' .$package['packageName']);
  105. Package_Message::set($package['displayName'] .' is not eligible for removal', 'error', $identifier);
  106. throw new Package_Dependency_Exception('Package is not eligible for removal');
  107. }
  108. $dependents = self::listDependents($package['packageName']);
  109. $failures = array();
  110. foreach($dependents as $dependent)
  111. {
  112. if ($dependentPackage = Package_Catalog::getInstalledPackage($dependent))
  113. {
  114. Package_Message::log('debug', 'dependency restriction ' .$package['packageName'] .' is being used by ' .$dependent .' version ' . $dependentPackage['version']);
  115. Package_Message::set($package['displayName'] .' is being used by ' .$dependentPackage['displayName'] .' version ' . $dependentPackage['version'], 'error', $identifier);
  116. Package_Message::set($dependentPackage['displayName'] .' is using ' .$package['displayName'], 'alert', $dependentPackage['identifier']);
  117. $failures['indispensable'][$dependent] = $dependentPackage['version'];
  118. }
  119. }
  120. if (!empty($failures))
  121. {
  122. $dependencyException = new Package_Dependency_Exception('Package did not pass dependency validation');
  123. $dependencyException->loadFailures($failures);
  124. throw $dependencyException;
  125. }
  126. }
  127. public static function compareVersion($avaliableVersion, $requiredVersion, $operator = '>=')
  128. {
  129. $validOperators = array(
  130. '!=', '<>', 'ne',
  131. '<=', 'le',
  132. '<', 'lt',
  133. '>=', 'ge',
  134. '>', 'gt',
  135. '==', 'eq'
  136. );
  137. if (count($logic = explode(' and ', $requiredVersion)) == 2)
  138. {
  139. return self::compareVersion($avaliableVersion, $logic[0], $operator)
  140. && self::compareVersion($avaliableVersion, $logic[1], $operator);
  141. }
  142. if (count($logic = explode(' or ', $requiredVersion)) == 2)
  143. {
  144. return self::compareVersion($avaliableVersion, $logic[0], $operator)
  145. || self::compareVersion($avaliableVersion, $logic[1], $operator);
  146. }
  147. // This might seem odd but if the operator is in the version strings there has to be a space....
  148. if (strstr($requiredVersion, ' '))
  149. {
  150. // Check the strings for a valid operator
  151. foreach($validOperators as $validOperator)
  152. {
  153. $validOperator .= ' ';
  154. if (stristr($requiredVersion, $validOperator))
  155. {
  156. $requiredVersion = str_replace($validOperator, '', $requiredVersion);
  157. $operator = str_replace(' ', '', $validOperator);
  158. break;
  159. }
  160. }
  161. }
  162. // make the comparision
  163. return version_compare($avaliableVersion, $requiredVersion, $operator);
  164. }
  165. protected static function listDependents($parentPackage)
  166. {
  167. $catalog = Package_Catalog::getCatalog();
  168. $dependents = array();
  169. foreach ($catalog as $id => $package)
  170. {
  171. if ($package['status'] == Package_Manager::STATUS_UNINSTALLED)
  172. {
  173. continue;
  174. }
  175. $dependent = $package['packageName'];
  176. foreach($package['required'] as $requirement => $conditions)
  177. {
  178. switch($requirement)
  179. {
  180. case 'not':
  181. continue;
  182. case 'or':
  183. foreach($conditions as $name => $condition)
  184. {
  185. if ($name == $parentPackage)
  186. {
  187. if (!in_array($dependent, $dependents))
  188. {
  189. $dependents[] = $dependent;
  190. }
  191. }
  192. }
  193. break;
  194. default:
  195. if ($requirement == $parentPackage)
  196. {
  197. if (!in_array($dependent, $dependents))
  198. {
  199. $dependents[] = $dependent;
  200. }
  201. }
  202. }
  203. }
  204. }
  205. return $dependents;
  206. }
  207. }