PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

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

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