PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Zend/ModuleManager/Listener/ConfigListener.php

https://bitbucket.org/aboozar/zf2
PHP | 403 lines | 207 code | 48 blank | 148 comment | 19 complexity | 145b840098f04ef987a85913238a0203 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_ModuleManager
  9. */
  10. namespace Zend\ModuleManager\Listener;
  11. use ArrayAccess;
  12. use Traversable;
  13. use Zend\Config\Config;
  14. use Zend\Config\Factory as ConfigFactory;
  15. use Zend\EventManager\EventManagerInterface;
  16. use Zend\EventManager\ListenerAggregateInterface;
  17. use Zend\ModuleManager\Feature\ConfigProviderInterface;
  18. use Zend\ModuleManager\ModuleEvent;
  19. use Zend\Stdlib\ArrayUtils;
  20. use Zend\Stdlib\Glob;
  21. /**
  22. * Config listener
  23. *
  24. * @category Zend
  25. * @package Zend_ModuleManager
  26. * @subpackage Listener
  27. */
  28. class ConfigListener extends AbstractListener implements
  29. ConfigMergerInterface,
  30. ListenerAggregateInterface
  31. {
  32. const STATIC_PATH = 'static_path';
  33. const GLOB_PATH = 'glob_path';
  34. /**
  35. * @var array
  36. */
  37. protected $listeners = array();
  38. /**
  39. * @var array
  40. */
  41. protected $configs = array();
  42. /**
  43. * @var array
  44. */
  45. protected $mergedConfig = array();
  46. /**
  47. * @var Config
  48. */
  49. protected $mergedConfigObject;
  50. /**
  51. * @var bool
  52. */
  53. protected $skipConfig = false;
  54. /**
  55. * @var array
  56. */
  57. protected $paths = array();
  58. /**
  59. * __construct
  60. *
  61. * @param ListenerOptions $options
  62. */
  63. public function __construct(ListenerOptions $options = null)
  64. {
  65. parent::__construct($options);
  66. if ($this->hasCachedConfig()) {
  67. $this->skipConfig = true;
  68. $this->setMergedConfig($this->getCachedConfig());
  69. } else {
  70. $this->addConfigGlobPaths($this->getOptions()->getConfigGlobPaths());
  71. $this->addConfigStaticPaths($this->getOptions()->getConfigStaticPaths());
  72. }
  73. }
  74. /**
  75. * __invoke proxy to loadModule for easier attaching
  76. *
  77. * @param ModuleEvent $e
  78. * @return ConfigListener
  79. */
  80. public function __invoke(ModuleEvent $e)
  81. {
  82. return $this->loadModule($e);
  83. }
  84. /**
  85. * Attach one or more listeners
  86. *
  87. * @param EventManagerInterface $events
  88. * @return ConfigListener
  89. */
  90. public function attach(EventManagerInterface $events)
  91. {
  92. $this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULES, array($this, 'onloadModulesPre'), 1000);
  93. if ($this->skipConfig) {
  94. // We already have the config from cache, no need to collect or merge.
  95. return $this;
  96. }
  97. $this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, array($this, 'onLoadModule'));
  98. $this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULES, array($this, 'onLoadModulesPost'), -1000);
  99. return $this;
  100. }
  101. /**
  102. * Pass self to the ModuleEvent object early so everyone has access.
  103. *
  104. * @param ModuleEvent $e
  105. * @return ConfigListener
  106. */
  107. public function onloadModulesPre(ModuleEvent $e)
  108. {
  109. $e->setConfigListener($this);
  110. return $this;
  111. }
  112. /**
  113. * Merge the config for each module
  114. *
  115. * @param ModuleEvent $e
  116. * @return ConfigListener
  117. */
  118. public function onLoadModule(ModuleEvent $e)
  119. {
  120. $module = $e->getParam('module');
  121. if (!$module instanceof ConfigProviderInterface
  122. && !is_callable(array($module, 'getConfig'))
  123. ) {
  124. return $this;
  125. }
  126. $config = $module->getConfig();
  127. $this->addConfig($e->getModuleName(), $config);
  128. return $this;
  129. }
  130. /**
  131. * Merge all config files matched by the given glob()s
  132. *
  133. * This is only attached if config is not cached.
  134. *
  135. * @param ModuleEvent $e
  136. * @return ConfigListener
  137. */
  138. public function onLoadModulesPost(ModuleEvent $e)
  139. {
  140. // Load the config files
  141. foreach ($this->paths as $path) {
  142. $this->addConfigByPath($path['path'], $path['type']);
  143. }
  144. // Merge all of the collected configs
  145. $this->mergedConfig = $this->getOptions()->getExtraConfig() ?: array();
  146. foreach ($this->configs as $key => $config) {
  147. $this->mergedConfig = ArrayUtils::merge($this->mergedConfig, $config);
  148. }
  149. // If enabled, update the config cache
  150. if ($this->getOptions()->getConfigCacheEnabled()) {
  151. $this->updateCache();
  152. }
  153. return $this;
  154. }
  155. /**
  156. * Detach all previously attached listeners
  157. *
  158. * @param EventManagerInterface $events
  159. * @return ConfigListener
  160. */
  161. public function detach(EventManagerInterface $events)
  162. {
  163. foreach ($this->listeners as $key => $listener) {
  164. $events->detach($listener);
  165. unset($this->listeners[$key]);
  166. }
  167. $this->listeners = array();
  168. return $this;
  169. }
  170. /**
  171. * getMergedConfig
  172. *
  173. * @param bool $returnConfigAsObject
  174. * @return mixed
  175. */
  176. public function getMergedConfig($returnConfigAsObject = true)
  177. {
  178. if ($returnConfigAsObject === true) {
  179. if ($this->mergedConfigObject === null) {
  180. $this->mergedConfigObject = new Config($this->mergedConfig);
  181. }
  182. return $this->mergedConfigObject;
  183. } else {
  184. return $this->mergedConfig;
  185. }
  186. }
  187. /**
  188. * setMergedConfig
  189. *
  190. * @param array $config
  191. * @return ConfigListener
  192. */
  193. public function setMergedConfig(array $config)
  194. {
  195. $this->mergedConfig = $config;
  196. $this->mergedConfigObject = null;
  197. return $this;
  198. }
  199. /**
  200. * Add an array of glob paths of config files to merge after loading modules
  201. *
  202. * @param array|Traversable $globPaths
  203. * @return ConfigListener
  204. */
  205. public function addConfigGlobPaths($globPaths)
  206. {
  207. $this->addConfigPaths($globPaths, self::GLOB_PATH);
  208. return $this;
  209. }
  210. /**
  211. * Add a glob path of config files to merge after loading modules
  212. *
  213. * @param string $globPath
  214. * @return ConfigListener
  215. */
  216. public function addConfigGlobPath($globPath)
  217. {
  218. $this->addConfigPath($globPath, self::GLOB_PATH);
  219. return $this;
  220. }
  221. /**
  222. * Add an array of static paths of config files to merge after loading modules
  223. *
  224. * @param array|Traversable $staticPaths
  225. * @return ConfigListener
  226. */
  227. public function addConfigStaticPaths($staticPaths)
  228. {
  229. $this->addConfigPaths($staticPaths, self::STATIC_PATH);
  230. return $this;
  231. }
  232. /**
  233. * Add a static path of config files to merge after loading modules
  234. *
  235. * @param string $globPath
  236. * @return ConfigListener
  237. */
  238. public function addConfigStaticPath($staticPath)
  239. {
  240. $this->addConfigPath($staticPath, self::STATIC_PATH);
  241. return $this;
  242. }
  243. /**
  244. * Add an array of paths of config files to merge after loading modules
  245. *
  246. * @param mixed $paths
  247. * @return ConfigListener
  248. */
  249. protected function addConfigPaths($paths, $type)
  250. {
  251. if ($paths instanceof Traversable) {
  252. $paths = ArrayUtils::iteratorToArray($paths);
  253. }
  254. if (!is_array($paths)) {
  255. throw new Exception\InvalidArgumentException(
  256. sprintf('Argument passed to %::%s() must be an array, '
  257. . 'implement the \Traversable interface, or be an '
  258. . 'instance of Zend\Config\Config. %s given.',
  259. __CLASS__, __METHOD__, gettype($paths))
  260. );
  261. }
  262. foreach ($paths as $path) {
  263. $this->addConfigPath($path, $type);
  264. }
  265. }
  266. /**
  267. * Add a path of config files to load and merge after loading modules
  268. *
  269. * @param string $path
  270. * @param string $type
  271. * @return ConfigListener
  272. */
  273. protected function addConfigPath($path, $type)
  274. {
  275. if (!is_string($path)) {
  276. throw new Exception\InvalidArgumentException(
  277. sprintf('Parameter to %s::%s() must be a string; %s given.',
  278. __CLASS__, __METHOD__, gettype($path))
  279. );
  280. }
  281. $this->paths[] = array('type' => $type, 'path' => $path);
  282. return $this;
  283. }
  284. protected function addConfig($key, $config)
  285. {
  286. if ($config instanceof Traversable) {
  287. $config = ArrayUtils::iteratorToArray($config);
  288. }
  289. if (!is_array($config)) {
  290. throw new Exception\InvalidArgumentException(
  291. sprintf('Config being merged must be an array, '
  292. . 'implement the \Traversable interface, or be an '
  293. . 'instance of Zend\Config\Config. %s given.', gettype($config))
  294. );
  295. }
  296. $this->configs[$key] = $config;
  297. return $this;
  298. }
  299. /**
  300. * Given a path (glob or static), fetch the config and add it to the array
  301. * of configs to merge.
  302. *
  303. * @param string $path
  304. * @param string $type
  305. * @return ConfigListener
  306. */
  307. protected function addConfigByPath($path, $type)
  308. {
  309. switch ($type) {
  310. case self::STATIC_PATH:
  311. $this->addConfig($path, ConfigFactory::fromFile($path));
  312. break;
  313. case self::GLOB_PATH:
  314. // We want to keep track of where each value came from so we don't
  315. // use ConfigFactory::fromFiles() since it does merging internally.
  316. foreach(Glob::glob($path, Glob::GLOB_BRACE) as $file) {
  317. $this->addConfig($file, ConfigFactory::fromFile($file));
  318. }
  319. break;
  320. }
  321. return $this;
  322. }
  323. /**
  324. * @return bool
  325. */
  326. protected function hasCachedConfig()
  327. {
  328. if (($this->getOptions()->getConfigCacheEnabled())
  329. && (file_exists($this->getOptions()->getConfigCacheFile()))
  330. ) {
  331. return true;
  332. }
  333. return false;
  334. }
  335. /**
  336. * @return mixed
  337. */
  338. protected function getCachedConfig()
  339. {
  340. return include $this->getOptions()->getConfigCacheFile();
  341. }
  342. /**
  343. * @return ConfigListener
  344. */
  345. protected function updateCache()
  346. {
  347. if (($this->getOptions()->getConfigCacheEnabled())
  348. && (false === $this->skipConfig)
  349. ) {
  350. $configFile = $this->getOptions()->getConfigCacheFile();
  351. $this->writeArrayToFile($configFile, $this->getMergedConfig(false));
  352. }
  353. return $this;
  354. }
  355. }