/libraries/axiom/axModuleManager.class.php

https://github.com/bdelespierre/php-axiom · PHP · 271 lines · 136 code · 31 blank · 104 comment · 25 complexity · 0ba7b996944d67b366d6b2a6cb306cf5 MD5 · raw file

  1. <?php
  2. /**
  3. * @brief Module manager class file
  4. * @file axModuleManager.class.php
  5. */
  6. /**
  7. * @brief Module Manager
  8. *
  9. * @todo Module manager long description
  10. * @class axModuleManager
  11. * @author Delespierre
  12. * @ingroup Core
  13. * @copyright Copyright 2010-2011, Benjamin Delespierre (http://bdelespierre.fr)
  14. * @license http://www.gnu.org/licenses/lgpl.html Lesser General Public Licence version 3
  15. */
  16. class axModuleManager {
  17. /**
  18. * @brief Cache file
  19. * @var string
  20. */
  21. const CACHE_FILE = "module.cache.php";
  22. /**
  23. * @brief Modules path
  24. * @property string $_path
  25. */
  26. protected $_path;
  27. /**
  28. * @brief Axiom version
  29. * @property string $_axiomVersion
  30. */
  31. protected $_axiomVersion;
  32. /**
  33. * @brief Options
  34. * @property array $_options
  35. */
  36. protected $_options;
  37. /**
  38. * @brief Module descriptors
  39. * @property array $_modules
  40. */
  41. protected $_modules;
  42. /**
  43. * @brief Constructor
  44. *
  45. * Options:
  46. * @li check_dependencies [boolean] wherever to check dependencies or not
  47. * @li cache_dir [string | false] false will disable caching
  48. *
  49. * @param string $path The path to the modules directory
  50. * @param string $axiom_version Used during dependencies check
  51. * @param array $options @optional @default{array()} see above
  52. */
  53. public function __construct ($path, $axiom_version, array $options = array()) {
  54. $default = array(
  55. 'check_dependencies' => true,
  56. 'cache_dir' => false,
  57. );
  58. if (!$this->_path = realpath($path)) {
  59. throw new axMissingFileException($path);
  60. }
  61. $this->_axiomVersion = $axiom_version;
  62. $this->_options = $options + $default;
  63. $this->_modules = array();
  64. }
  65. /**
  66. * @brief Get available modules
  67. * @return array
  68. */
  69. public function getModules () {
  70. if (empty($this->_modules)) {
  71. if ($this->_options['cache_dir'] && is_readable($c = $this->_options['cache_dir'] . '/' . self::CACHE_FILE)) {
  72. require $c;
  73. $this->_modules = $modules;
  74. }
  75. else {
  76. $directories = new DirectoryIterator($this->_path);
  77. $iterator = new axDirectoryFilterIterator($directories, array('.', '..', '.svn', '.git'));
  78. foreach ($iterator as $item) {
  79. $this->getInformations((string)$item);
  80. }
  81. $this->_cache();
  82. }
  83. }
  84. return $this->_modules;
  85. }
  86. /**
  87. * @brief Check if the module exists
  88. * @param string $module
  89. * @return boolena
  90. */
  91. public function exists ($module) {
  92. return array_key_exists($module, $this->getModules()) && $this->_modules[$module];
  93. }
  94. /**
  95. * @brief Get module meta-inf.
  96. *
  97. * Wil return false in case of error.
  98. *
  99. * @param string $module
  100. * @return array
  101. */
  102. public function getInformations ($module) {
  103. if (!empty($this->_modules[$module]))
  104. return $this->_modules[$module];
  105. if (!is_file($p = $this->_path . "/$module/module.ini"))
  106. return false;
  107. if (($meta = parse_ini_file($p, false)) === false)
  108. return false;
  109. $meta['path'] = $this->_path . "/$module";
  110. return $this->_modules[$module] = $meta;
  111. }
  112. /**
  113. * @brief Load the given module
  114. * @param string $module
  115. * @return boolean
  116. */
  117. public function load ($module) {
  118. if (!$this->exists($module))
  119. return false;
  120. if (!$meta = $this->getInformations($module))
  121. throw new RuntimeException("Module {$module} informations are not avaialble, check that module.ini isn't missing", 2050);
  122. if ($this->_options['check_dependencies'] && !$this->_checkDependencies($module))
  123. throw new RuntimeException("Module {$module} dependencies check failed");
  124. if (!$this->_loadDependencies($module))
  125. throw new RuntimeException("Cannot load dependency module for {$module}");
  126. return (boolean)require_once $meta['path'] . "/config/bootstrap.php";
  127. }
  128. /**
  129. * @brief Check if updates are available for the given module
  130. * @param string $module
  131. * @return boolean
  132. */
  133. public function checkUpdates ($module) {
  134. // TODO axModuleManager::checkUpdates
  135. }
  136. /**
  137. * @brief Get the given module dependencies
  138. * @param string $module
  139. * @throws RuntimeException If the module doesn't exists
  140. * @return array
  141. */
  142. protected function _getDependencies ($module) {
  143. if (!isset($this->_modules[$module]) || !$this->_modules[$module])
  144. throw new RuntimeException("Unknown module {$module}");
  145. return isset($this->_modules[$module]['dependencies']) ?
  146. $this->_modules[$module]['dependencies'] : array();
  147. }
  148. /**
  149. * @brief Check dependencies according to version numbers
  150. * @param string $module
  151. * @throws RuntimeException If a module's meta-infs cannot be found
  152. * @return boolean
  153. */
  154. protected function _checkDependencies ($module) {
  155. foreach ($this->_getDependencies($module) as $dep) {
  156. list($dep_module_name, $dep_module_version) = explode('-', $dep);
  157. if ($dep_module_name == 'axiom') {
  158. if (!self::_compareVersions($this->_axiomVersion, $dep_module_version))
  159. return false;
  160. continue;
  161. }
  162. if (!$this->exists($dep_module_name))
  163. return false;
  164. if (!$dep_meta = $this->getInformations($dep_module_name))
  165. throw new RuntimeException("Cannot load dependencie module information");
  166. if (!self::_compareVersions($dep_meta['version'], $dep_module_version))
  167. return false;
  168. }
  169. return true;
  170. }
  171. /**
  172. * @brief Load dependencies for the given module
  173. * @param string $module
  174. * @return boolean
  175. */
  176. protected function _loadDependencies ($module) {
  177. foreach ($this->_getDependencies($module) as $module => $dep) {
  178. list($dep_module_name,$dep_module_version) = explode('-', $dep);
  179. if ($dep_module_name == 'axiom')
  180. continue;
  181. try {
  182. if (!$this->load($dep_module_name)) {
  183. return false;
  184. }
  185. }
  186. catch (Exception $e) {
  187. return false;
  188. }
  189. }
  190. return true;
  191. }
  192. /**
  193. * @brief Store modules informations for later use
  194. *
  195. * Will do nothing if cache is disabled
  196. *
  197. * @return boolean
  198. */
  199. protected function _cache () {
  200. if (!$this->_options['cache_dir'])
  201. return false;
  202. $buffer = '<?php $modules=' . var_export($this->_modules, true) . '; ?>';
  203. return (boolean)file_put_contents($this->_options['cache_dir'] . '/' . self::CACHE_FILE, $buffer);
  204. }
  205. /**
  206. * @brief Parses a version to an integer
  207. *
  208. * @param string $version
  209. * @return interger
  210. */
  211. protected static function _parseModuleVersion ($version) {
  212. list($maj,$min,$build) = explode('.', $version);
  213. return $maj * 10000 + $min * 100 + $build;
  214. }
  215. /**
  216. * @brief Check if the left version is higher than the right version
  217. *
  218. * Both parameters can be either strings or integer so these calls are equivalents:
  219. * @code
  220. * self::_compareVersion('1.2.3', '1.0.2');
  221. * self::_compareVersion(100203, 100002);
  222. * @endcode
  223. *
  224. * @param mixed $version_a
  225. * @param mixed $version_b
  226. * @return boolean
  227. */
  228. protected static function _compareVersions ($version_a, $version_b) {
  229. if (is_string($version_a))
  230. $version_a = self::_parseModuleVersion($version_a);
  231. if (is_string($version_b))
  232. $version_b = self::_parseModuleVersion($version_b);
  233. return $version_a >= $version_b;
  234. }
  235. }