/library/Zend/Module/Manager.php

https://github.com/noose/zf2 · PHP · 307 lines · 192 code · 24 blank · 91 comment · 35 complexity · 112d1087afd8920905cb49c502e25696 MD5 · raw file

  1. <?php
  2. namespace Zend\Module;
  3. use Traversable,
  4. Zend\Config\Config,
  5. Zend\EventManager\EventCollection,
  6. Zend\EventManager\EventManager;
  7. class Manager
  8. {
  9. /**
  10. * @var array An array of Module classes of loaded modules
  11. */
  12. protected $loadedModules = array();
  13. /**
  14. * @var EventCollection
  15. */
  16. protected $events;
  17. /**
  18. * @var ManagerOptions
  19. */
  20. protected $options;
  21. /**
  22. * @var Zend\Config\Config
  23. */
  24. protected $mergedConfig;
  25. /**
  26. * @var bool
  27. */
  28. protected $skipConfig = false;
  29. /**
  30. * __construct
  31. *
  32. * @param array|Traversable $modules
  33. * @param ManagerOptions $options
  34. * @return void
  35. */
  36. public function __construct($modules, ManagerOptions $options = null)
  37. {
  38. if ($options === null) {
  39. $this->setOptions(new ManagerOptions);
  40. } else {
  41. $this->setOptions($options);
  42. }
  43. if ($this->hasCachedConfig()) {
  44. $this->skipConfig = true;
  45. $this->setMergedConfig($this->getCachedConfig());
  46. }
  47. $this->loadModules($modules);
  48. $this->updateCache();
  49. $this->events()->trigger('init.post', $this);
  50. }
  51. /**
  52. * loadModules
  53. *
  54. * @param array|Traversable $modules
  55. * @return Manager
  56. */
  57. public function loadModules($modules)
  58. {
  59. if (is_array($modules) || $modules instanceof Traversable) {
  60. foreach ($modules as $moduleName) {
  61. $this->loadModule($moduleName);
  62. }
  63. } else {
  64. throw new \InvalidArgumentException(
  65. 'Parameter to \\ZendModule\\Manager\'s '
  66. . 'loadModules method must be an array or '
  67. . 'implement the \\Traversable interface'
  68. );
  69. }
  70. return $this;
  71. }
  72. /**
  73. * loadModule
  74. *
  75. * @param string $moduleName
  76. * @return mixed Module's Module class
  77. */
  78. public function loadModule($moduleName)
  79. {
  80. if (!isset($this->loadedModules[$moduleName])) {
  81. $class = $moduleName . '\Module';
  82. $module = new $class;
  83. $this->runModuleInit($module);
  84. $this->mergeModuleConfig($module);
  85. $this->loadedModules[$moduleName] = $module;
  86. }
  87. return $this->loadedModules[$moduleName];
  88. }
  89. /**
  90. * Loop through loaded modules and verify that all dependencies are met
  91. *
  92. * @TODO:
  93. * - This could probably be much more efficient (do not check satisfied
  94. * deps again, etc)
  95. * - Do more isset() checking on the dep arrays
  96. *
  97. * @return array An array of unsatisfied, optional dependencies
  98. */
  99. public function resolveDependencies()
  100. {
  101. foreach ($this->getLoadedModules() as $moduleName => $module) {
  102. if (!is_callable(array($module, 'getDependencies'))) {
  103. continue;
  104. }
  105. $unsatisfiedDeps = array();
  106. foreach ($module->getDependencies() as $dep => $depInfo) {
  107. preg_match('/(<|lt|<=|le|>|gt|>=|ge|==|=|eq|!=|<>|ne)?(\d.*)/',$depInfo['version'], $matches, PREG_OFFSET_CAPTURE);
  108. if ($dep === 'php') {
  109. if (!version_compare(PHP_VERSION, $matches[2][0], $matches[1][0] ?: '>=')) {
  110. if ($depInfo['required'] == true) {
  111. throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
  112. } else {
  113. $unsatifiedDeps[$moduleName][$dep] = $depInfo;
  114. }
  115. }
  116. } elseif (substr($dep, 0, 4) === 'ext/') {
  117. $extName = substr($dep, 4);
  118. if (!version_compare(phpversion($extName), $matches[2][0], $matches[1][0] ?: '>=')) {
  119. if ($depInfo['required'] == true) {
  120. throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
  121. } else {
  122. $unsatifiedDeps[$moduleName][$dep] = $depInfo;
  123. }
  124. }
  125. } else {
  126. $satisfied = false;
  127. foreach ($this->getLoadedModules() as $depModuleName => $depModule) {
  128. if (!is_callable(array($depModule, 'getProvides'))) {
  129. continue;
  130. }
  131. $provides = $depModule->getProvides();
  132. if ($provides['name'] !== $dep) {
  133. continue;
  134. }
  135. if (version_compare($provides['version'], $matches[2][0], $matches[1][0] ?: '>=')) {
  136. $satisfied = true;
  137. break;
  138. }
  139. }
  140. if (!$satisfied) {
  141. if ($depInfo['required'] == true) {
  142. throw new \RuntimeException("Required dependency unsatisfied: {$dep} {$depInfo['version']}");
  143. } else {
  144. $unsatifiedDeps[$moduleName][$dep] = $depInfo;
  145. }
  146. }
  147. }
  148. }
  149. }
  150. return $unsatisfiedDeps;
  151. }
  152. /**
  153. * Set the event manager instance used by this context
  154. *
  155. * @param EventCollection $events
  156. * @return Manager
  157. */
  158. public function setEventManager(EventCollection $events)
  159. {
  160. $this->events = $events;
  161. return $this;
  162. }
  163. /**
  164. * Retrieve the event manager
  165. *
  166. * Lazy-loads an EventManager instance if none registered.
  167. *
  168. * @return EventCollection
  169. */
  170. public function events()
  171. {
  172. if (!$this->events instanceof EventCollection) {
  173. $this->setEventManager(new EventManager(array(__CLASS__, get_class($this))));
  174. }
  175. return $this->events;
  176. }
  177. /**
  178. * Get options.
  179. *
  180. * @return ManagerOptions
  181. */
  182. public function getOptions()
  183. {
  184. return $this->options;
  185. }
  186. /**
  187. * Set options
  188. *
  189. * @param ManagerOptions $options
  190. * @return Manager
  191. */
  192. public function setOptions(ManagerOptions $options)
  193. {
  194. $this->options = $options;
  195. return $this;
  196. }
  197. /**
  198. * Get loadedModules.
  199. *
  200. * @return array
  201. */
  202. public function getLoadedModules()
  203. {
  204. return $this->loadedModules;
  205. }
  206. /**
  207. * getMergedConfig
  208. * Build a merged config object for all loaded modules
  209. *
  210. * @return Zend\Config\Config
  211. */
  212. public function getMergedConfig($readOnly = true)
  213. {
  214. if (null === $this->mergedConfig) {
  215. $this->setMergedConfig(new Config(array(), true));
  216. }
  217. if (true === $readOnly) {
  218. $this->mergedConfig->setReadOnly();
  219. }
  220. return $this->mergedConfig;
  221. }
  222. /**
  223. * setMergedConfig
  224. *
  225. * @param Config $config
  226. * @return Manager
  227. */
  228. public function setMergedConfig(Config $config)
  229. {
  230. $this->mergedConfig = $config;
  231. return $this;
  232. }
  233. /**
  234. * mergeModuleConfig
  235. *
  236. * @param mixed $module
  237. * @return Manager
  238. */
  239. public function mergeModuleConfig($module)
  240. {
  241. if ((false === $this->skipConfig)
  242. && (is_callable(array($module, 'getConfig')))
  243. ) {
  244. $this->getMergedConfig(false)->merge($module->getConfig($this->getOptions()->getApplicationEnv()));
  245. }
  246. return $this;
  247. }
  248. protected function runModuleInit($module)
  249. {
  250. if (is_callable(array($module, 'init'))) {
  251. $module->init($this);
  252. }
  253. return $this;
  254. }
  255. protected function hasCachedConfig()
  256. {
  257. if (($this->getOptions()->getCacheConfig())
  258. && (file_exists($this->getOptions()->getCacheFilePath()))
  259. ) {
  260. return true;
  261. }
  262. return false;
  263. }
  264. protected function getCachedConfig()
  265. {
  266. return new Config(include $this->getOptions()->getCacheFilePath());
  267. }
  268. protected function updateCache()
  269. {
  270. if (($this->getOptions()->getCacheConfig())
  271. && (false === $this->skipConfig)
  272. ) {
  273. $this->saveConfigCache($this->getMergedConfig());
  274. }
  275. return $this;
  276. }
  277. protected function saveConfigCache($config)
  278. {
  279. $content = "<?php\nreturn " . var_export($config->toArray(), 1) . ';';
  280. file_put_contents($this->getOptions()->getCacheFilePath(), $content);
  281. return $this;
  282. }
  283. }