PageRenderTime 72ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Classes/TYPO3/FLOW3/Configuration/ConfigurationManager.php

https://github.com/christianjul/FLOW3-Composer
PHP | 455 lines | 260 code | 47 blank | 148 comment | 33 complexity | 7705db0951e4d9ea078d2575e31a5269 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-3.0
  1. <?php
  2. namespace TYPO3\FLOW3\Configuration;
  3. /* *
  4. * This script belongs to the FLOW3 framework. *
  5. * *
  6. * It is free software; you can redistribute it and/or modify it under *
  7. * the terms of the GNU Lesser General Public License, either version 3 *
  8. * of the License, or (at your option) any later version. *
  9. * *
  10. * The TYPO3 project - inspiring people to share! *
  11. * */
  12. use TYPO3\FLOW3\Annotations as FLOW3;
  13. use TYPO3\FLOW3\Utility\Arrays;
  14. /**
  15. * A general purpose configuration manager
  16. *
  17. * @FLOW3\Scope("singleton")
  18. * @api
  19. */
  20. class ConfigurationManager {
  21. const CONFIGURATION_TYPE_CACHES = 'Caches';
  22. const CONFIGURATION_TYPE_OBJECTS = 'Objects';
  23. const CONFIGURATION_TYPE_ROUTES = 'Routes';
  24. const CONFIGURATION_TYPE_POLICY = 'Policy';
  25. const CONFIGURATION_TYPE_SETTINGS = 'Settings';
  26. /**
  27. * The application context of the configuration to manage
  28. *
  29. * @var \TYPO3\FLOW3\Core\ApplicationContext
  30. */
  31. protected $context;
  32. /**
  33. * An array of context name strings, from the most generic one to the most special one.
  34. * Example:
  35. * Development, Development/Foo, Development/Foo/Bar
  36. *
  37. * @var array
  38. */
  39. protected $orderedListOfContextNames = array();
  40. /**
  41. * @var \TYPO3\FLOW3\Configuration\Source\YamlSource
  42. */
  43. protected $configurationSource;
  44. /**
  45. * @var \TYPO3\FLOW3\Utility\Environment
  46. */
  47. protected $environment;
  48. /**
  49. * @var string
  50. */
  51. protected $includeCachedConfigurationsPathAndFilename;
  52. /**
  53. * Storage of the raw special configurations
  54. * @var array
  55. */
  56. protected $configurations = array(
  57. self::CONFIGURATION_TYPE_SETTINGS => array(),
  58. );
  59. /**
  60. * Active packages to load the configuration for
  61. * @var array<TYPO3\FLOW3\Package\PackageInterface>
  62. */
  63. protected $packages = array();
  64. /**
  65. * @var boolean
  66. */
  67. protected $cacheNeedsUpdate = FALSE;
  68. /**
  69. * Constructs the configuration manager
  70. *
  71. * @param \TYPO3\FLOW3\Core\ApplicationContext $context The application context to fetch configuration for
  72. */
  73. public function __construct(\TYPO3\FLOW3\Core\ApplicationContext $context) {
  74. $this->context = $context;
  75. $orderedListOfContextNames = array();
  76. $currentContext = $context;
  77. do {
  78. $orderedListOfContextNames[] = (string)$currentContext;
  79. } while ($currentContext = $currentContext->getParent());
  80. $this->orderedListOfContextNames = array_reverse($orderedListOfContextNames);
  81. $this->includeCachedConfigurationsPathAndFilename = FLOW3_PATH_CONFIGURATION . (string)$context . '/IncludeCachedConfigurations.php';
  82. }
  83. /**
  84. * Injects the configuration source
  85. *
  86. * @param \TYPO3\FLOW3\Configuration\Source\YamlSource $configurationSource
  87. * @return void
  88. */
  89. public function injectConfigurationSource(\TYPO3\FLOW3\Configuration\Source\YamlSource $configurationSource) {
  90. $this->configurationSource = $configurationSource;
  91. }
  92. /**
  93. * Injects the environment
  94. *
  95. * @param \TYPO3\FLOW3\Utility\Environment $environment
  96. * @return void
  97. */
  98. public function injectEnvironment(\TYPO3\FLOW3\Utility\Environment $environment) {
  99. $this->environment = $environment;
  100. }
  101. /**
  102. * Sets the active packages to load the configuration for
  103. *
  104. * @param array<TYPO3\FLOW3\Package\PackageInterface> $packages
  105. * @return void
  106. */
  107. public function setPackages(array $packages) {
  108. $this->packages = $packages;
  109. }
  110. /**
  111. * Get the available configuration-types
  112. *
  113. * @return array<string> array of configuration-type identifier strings
  114. */
  115. public function getAvailableConfigurationTypes() {
  116. return array(
  117. self::CONFIGURATION_TYPE_CACHES,
  118. self::CONFIGURATION_TYPE_OBJECTS,
  119. self::CONFIGURATION_TYPE_ROUTES,
  120. self::CONFIGURATION_TYPE_POLICY,
  121. self::CONFIGURATION_TYPE_SETTINGS
  122. );
  123. }
  124. /**
  125. * Returns the specified raw configuration.
  126. * The actual configuration will be merged from different sources in a defined order.
  127. *
  128. * Note that this is a low level method and only makes sense to be used by FLOW3 internally.
  129. *
  130. * @param string $configurationType The kind of configuration to fetch - must be one of the CONFIGURATION_TYPE_* constants
  131. * @param string $packageKey The package key to fetch configuration for.
  132. * @return array The configuration
  133. * @throws \TYPO3\FLOW3\Configuration\Exception\InvalidConfigurationTypeException on invalid configuration types
  134. */
  135. public function getConfiguration($configurationType, $packageKey = NULL) {
  136. $configuration = array();
  137. switch ($configurationType) {
  138. case self::CONFIGURATION_TYPE_ROUTES :
  139. case self::CONFIGURATION_TYPE_CACHES :
  140. case self::CONFIGURATION_TYPE_POLICY :
  141. if (!isset($this->configurations[$configurationType])) {
  142. $this->loadConfiguration($configurationType, $this->packages);
  143. }
  144. if (isset($this->configurations[$configurationType])) {
  145. $configuration = &$this->configurations[$configurationType];
  146. }
  147. break;
  148. case self::CONFIGURATION_TYPE_SETTINGS :
  149. if (!isset($this->configurations[$configurationType]) || $this->configurations[$configurationType] === array()) {
  150. $this->configurations[$configurationType] = array();
  151. $this->loadConfiguration($configurationType, $this->packages);
  152. }
  153. if (isset($this->configurations[$configurationType])) {
  154. $configuration = &$this->configurations[self::CONFIGURATION_TYPE_SETTINGS];
  155. }
  156. break;
  157. case self::CONFIGURATION_TYPE_OBJECTS :
  158. $this->loadConfiguration($configurationType, $this->packages);
  159. $configuration = &$this->configurations[$configurationType];
  160. break;
  161. default :
  162. throw new \TYPO3\FLOW3\Configuration\Exception\InvalidConfigurationTypeException('Invalid configuration type "' . $configurationType . '"', 1206031879);
  163. }
  164. if ($packageKey !== NULL && $configuration !== NULL) {
  165. return (Arrays::getValueByPath($configuration, $packageKey));
  166. } else {
  167. return $configuration;
  168. }
  169. }
  170. /**
  171. * Shuts down the configuration manager.
  172. * This method writes the current configuration into a cache file if FLOW3 was configured to do so.
  173. *
  174. * @return void
  175. */
  176. public function shutdown() {
  177. if ($this->configurations[self::CONFIGURATION_TYPE_SETTINGS]['TYPO3']['FLOW3']['configuration']['compileConfigurationFiles'] === TRUE && $this->cacheNeedsUpdate === TRUE) {
  178. $this->saveConfigurationCache();
  179. }
  180. }
  181. /**
  182. * Loads special configuration defined in the specified packages and merges them with
  183. * those potentially existing in the global configuration folders. The result is stored
  184. * in the configuration manager's configuration registry and can be retrieved with the
  185. * getConfiguration() method.
  186. *
  187. * @param string $configurationType The kind of configuration to load - must be one of the CONFIGURATION_TYPE_* constants
  188. * @param array $packages An array of Package objects (indexed by package key) to consider
  189. * @return void
  190. * @throws \TYPO3\FLOW3\Configuration\Exception\InvalidConfigurationTypeException
  191. */
  192. protected function loadConfiguration($configurationType, array $packages) {
  193. $this->cacheNeedsUpdate = TRUE;
  194. switch ($configurationType) {
  195. case self::CONFIGURATION_TYPE_SETTINGS :
  196. // Make sure that the FLOW3 package is the first item of the packages array:
  197. if (isset($packages['TYPO3.FLOW3'])) {
  198. $flow3Package = $packages['TYPO3.FLOW3'];
  199. unset($packages['TYPO3.FLOW3']);
  200. $packages = array_merge(array('TYPO3.FLOW3' => $flow3Package), $packages);
  201. unset($flow3Package);
  202. }
  203. $settings = array();
  204. foreach ($packages as $packageKey => $package) {
  205. if (Arrays::getValueByPath($settings, $packageKey) === NULL) {
  206. $settings = Arrays::setValueByPath($settings, $packageKey, array());
  207. }
  208. $settings = Arrays::arrayMergeRecursiveOverrule($settings, $this->configurationSource->load($package->getConfigurationPath() . self::CONFIGURATION_TYPE_SETTINGS));
  209. }
  210. $settings = Arrays::arrayMergeRecursiveOverrule($settings, $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . self::CONFIGURATION_TYPE_SETTINGS));
  211. foreach ($this->orderedListOfContextNames as $contextName) {
  212. foreach ($packages as $package) {
  213. $settings = Arrays::arrayMergeRecursiveOverrule($settings, $this->configurationSource->load($package->getConfigurationPath() . $contextName . '/' . self::CONFIGURATION_TYPE_SETTINGS));
  214. }
  215. $settings = Arrays::arrayMergeRecursiveOverrule($settings, $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $contextName . '/' . self::CONFIGURATION_TYPE_SETTINGS));
  216. }
  217. if ($this->configurations[self::CONFIGURATION_TYPE_SETTINGS] !== array()) {
  218. $this->configurations[self::CONFIGURATION_TYPE_SETTINGS] = Arrays::arrayMergeRecursiveOverrule($this->configurations[self::CONFIGURATION_TYPE_SETTINGS], $settings);
  219. } else {
  220. $this->configurations[self::CONFIGURATION_TYPE_SETTINGS] = $settings;
  221. }
  222. $this->configurations[self::CONFIGURATION_TYPE_SETTINGS]['TYPO3']['FLOW3']['core']['context'] = (string)$this->context;
  223. break;
  224. case self::CONFIGURATION_TYPE_OBJECTS :
  225. $this->configurations[$configurationType] = array();
  226. foreach ($packages as $packageKey => $package) {
  227. $configuration = $this->configurationSource->load($package->getConfigurationPath() . $configurationType);
  228. $configuration = Arrays::arrayMergeRecursiveOverrule($configuration, $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $configurationType));
  229. foreach ($this->orderedListOfContextNames as $contextName) {
  230. $configuration = Arrays::arrayMergeRecursiveOverrule($configuration, $this->configurationSource->load($package->getConfigurationPath() . $contextName . '/' . $configurationType));
  231. $configuration = Arrays::arrayMergeRecursiveOverrule($configuration, $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $contextName . '/' . $configurationType));
  232. }
  233. $this->configurations[$configurationType][$packageKey] = $configuration;
  234. }
  235. break;
  236. case self::CONFIGURATION_TYPE_CACHES :
  237. case self::CONFIGURATION_TYPE_POLICY :
  238. $this->configurations[$configurationType] = array();
  239. foreach ($packages as $package) {
  240. $this->configurations[$configurationType] = Arrays::arrayMergeRecursiveOverrule($this->configurations[$configurationType], $this->configurationSource->load($package->getConfigurationPath() . $configurationType));
  241. }
  242. $this->configurations[$configurationType] = Arrays::arrayMergeRecursiveOverrule($this->configurations[$configurationType], $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $configurationType));
  243. foreach ($this->orderedListOfContextNames as $contextName) {
  244. foreach ($packages as $package) {
  245. $this->configurations[$configurationType] = Arrays::arrayMergeRecursiveOverrule($this->configurations[$configurationType], $this->configurationSource->load($package->getConfigurationPath() . $contextName . '/' . $configurationType));
  246. }
  247. $this->configurations[$configurationType] = Arrays::arrayMergeRecursiveOverrule($this->configurations[$configurationType], $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $contextName . '/' . $configurationType));
  248. }
  249. break;
  250. case self::CONFIGURATION_TYPE_ROUTES :
  251. // load subroutes
  252. $subRoutesConfiguration = array();
  253. foreach ($packages as $packageKey => $package) {
  254. $subRoutesConfiguration[$packageKey] = array();
  255. foreach (array_reverse($this->orderedListOfContextNames) as $contextName) {
  256. $subRoutesConfiguration[$packageKey] = array_merge($subRoutesConfiguration[$packageKey], $this->configurationSource->load($package->getConfigurationPath() . $contextName . '/' . $configurationType));
  257. }
  258. $subRoutesConfiguration[$packageKey] = array_merge($subRoutesConfiguration[$packageKey], $this->configurationSource->load($package->getConfigurationPath() . $configurationType));
  259. }
  260. // load main routes
  261. $this->configurations[self::CONFIGURATION_TYPE_ROUTES] = array();
  262. foreach (array_reverse($this->orderedListOfContextNames) as $contextName) {
  263. $this->configurations[self::CONFIGURATION_TYPE_ROUTES] = array_merge($this->configurations[self::CONFIGURATION_TYPE_ROUTES], $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $contextName . '/' . $configurationType));
  264. }
  265. $this->configurations[self::CONFIGURATION_TYPE_ROUTES] = array_merge($this->configurations[self::CONFIGURATION_TYPE_ROUTES], $this->configurationSource->load(FLOW3_PATH_CONFIGURATION . $configurationType));
  266. // Merge routes with subroutes
  267. $this->mergeRoutesWithSubRoutes($this->configurations[$configurationType], $subRoutesConfiguration);
  268. break;
  269. default:
  270. throw new \TYPO3\FLOW3\Configuration\Exception\InvalidConfigurationTypeException('Configuration type "' . $configurationType . '" cannot be loaded with loadConfiguration().', 1251450613);
  271. }
  272. $this->postProcessConfiguration($this->configurations[$configurationType]);
  273. }
  274. /**
  275. * If a cache file with previously saved configuration exists, it is loaded.
  276. *
  277. * @return boolean If cached configuration was loaded or not
  278. */
  279. public function loadConfigurationCache() {
  280. if (file_exists($this->includeCachedConfigurationsPathAndFilename)) {
  281. $this->configurations = require($this->includeCachedConfigurationsPathAndFilename);
  282. return TRUE;
  283. }
  284. return FALSE;
  285. }
  286. /**
  287. * If a cache file with previously saved configuration exists, it is removed.
  288. *
  289. * @return void
  290. */
  291. public function flushConfigurationCache() {
  292. $configurationCachePath = $this->environment->getPathToTemporaryDirectory() . 'Configuration/';
  293. $cachePathAndFilename = $configurationCachePath . str_replace('/', '_', (string)$this->context) . 'Configurations.php';
  294. if (file_exists($cachePathAndFilename)) {
  295. if (unlink($cachePathAndFilename) === FALSE) {
  296. throw new \TYPO3\FLOW3\Configuration\Exception(sprintf('Could not delete configuration cache file "%s". Check file permissions for the parent directory.', $cachePathAndFilename), 1341999203);
  297. }
  298. }
  299. $this->configurations = array(self::CONFIGURATION_TYPE_SETTINGS => array());
  300. }
  301. /**
  302. * Saves the current configuration into a cache file and creates a cache inclusion script
  303. * in the context's Configuration directory.
  304. *
  305. * @return void
  306. * @throws \TYPO3\FLOW3\Configuration\Exception
  307. */
  308. protected function saveConfigurationCache() {
  309. $configurationCachePath = $this->environment->getPathToTemporaryDirectory() . 'Configuration/';
  310. if (!file_exists($configurationCachePath)) {
  311. \TYPO3\FLOW3\Utility\Files::createDirectoryRecursively($configurationCachePath);
  312. }
  313. $cachePathAndFilename = $configurationCachePath . str_replace('/', '_', (string)$this->context) . 'Configurations.php';
  314. $flow3RootPath = FLOW3_PATH_ROOT;
  315. $includeCachedConfigurationsCode = <<< "EOD"
  316. <?php
  317. if (FLOW3_PATH_ROOT !== '$flow3RootPath' || !file_exists('$cachePathAndFilename')) {
  318. unlink(__FILE__);
  319. return array();
  320. }
  321. return require '$cachePathAndFilename';
  322. ?>
  323. EOD;
  324. file_put_contents($cachePathAndFilename, '<?php return ' . var_export($this->configurations, TRUE) . '?>');
  325. if (!is_dir(dirname($this->includeCachedConfigurationsPathAndFilename)) && !is_link(dirname($this->includeCachedConfigurationsPathAndFilename))) {
  326. \TYPO3\FLOW3\Utility\Files::createDirectoryRecursively(dirname($this->includeCachedConfigurationsPathAndFilename));
  327. }
  328. file_put_contents($this->includeCachedConfigurationsPathAndFilename, $includeCachedConfigurationsCode);
  329. if (!file_exists($this->includeCachedConfigurationsPathAndFilename)) {
  330. throw new \TYPO3\FLOW3\Configuration\Exception(sprintf('Could not write configuration cache file "%s". Check file permissions for the parent directory.', $this->includeCachedConfigurationsPathAndFilename), 1323339284);
  331. }
  332. }
  333. /**
  334. * Post processes the given configuration array by replacing constants with their
  335. * actual value.
  336. *
  337. * @param array &$configurations The configuration to post process. The results are stored directly in the given array
  338. * @return void
  339. */
  340. protected function postProcessConfiguration(array &$configurations) {
  341. foreach ($configurations as $key => $configuration) {
  342. if (is_array($configuration)) {
  343. $this->postProcessConfiguration($configurations[$key]);
  344. } elseif (is_string($configuration)) {
  345. $matches = array();
  346. preg_match_all('/(?:%)([A-Z_0-9]+)(?:%)/', $configuration, $matches);
  347. if (count($matches[1]) > 0) {
  348. foreach ($matches[1] as $match) {
  349. if (defined($match)) $configurations[$key] = str_replace('%' . $match . '%', constant($match), $configurations[$key]);
  350. }
  351. }
  352. }
  353. }
  354. }
  355. /**
  356. * Loads specified sub routes and builds composite routes.
  357. *
  358. * @param array $routesConfiguration
  359. * @param array $subRoutesConfiguration
  360. * @return void
  361. * @throws \TYPO3\FLOW3\Configuration\Exception\ParseErrorException
  362. */
  363. protected function mergeRoutesWithSubRoutes(array &$routesConfiguration, array $subRoutesConfiguration) {
  364. $mergedRoutesConfiguration = array();
  365. foreach ($routesConfiguration as $routeConfiguration) {
  366. if (!isset($routeConfiguration['subRoutes'])) {
  367. $mergedRoutesConfiguration[] = $routeConfiguration;
  368. continue;
  369. }
  370. $mergedSubRoutesConfiguration = array($routeConfiguration);
  371. foreach ($routeConfiguration['subRoutes'] as $subRouteKey => $subRouteOptions) {
  372. if (!isset($subRouteOptions['package']) || !isset($subRoutesConfiguration[$subRouteOptions['package']])) {
  373. throw new \TYPO3\FLOW3\Configuration\Exception\ParseErrorException('Missing package configuration for SubRoute "' . (isset($routeConfiguration['name']) ? $routeConfiguration['name'] : 'unnamed Route') . '".', 1318414040);
  374. }
  375. $packageSubRoutesConfiguration = $subRoutesConfiguration[$subRouteOptions['package']];
  376. $mergedSubRoutesConfiguration = $this->buildSubrouteConfigurations($mergedSubRoutesConfiguration, $packageSubRoutesConfiguration, $subRouteKey);
  377. }
  378. $mergedRoutesConfiguration = array_merge($mergedRoutesConfiguration, $mergedSubRoutesConfiguration);
  379. }
  380. $routesConfiguration = $mergedRoutesConfiguration;
  381. }
  382. /**
  383. * Merges all routes in $routesConfiguration with the sub routes in $subRoutesConfiguration
  384. *
  385. * @param array $routesConfiguration
  386. * @param array $subRoutesConfiguration
  387. * @param string $subRouteKey the key of the sub route: <subRouteKey>
  388. * @return array the merged route configuration
  389. * @throws \TYPO3\FLOW3\Configuration\Exception\ParseErrorException
  390. */
  391. protected function buildSubrouteConfigurations(array $routesConfiguration, array $subRoutesConfiguration, $subRouteKey) {
  392. $mergedSubRoutesConfigurations = array();
  393. foreach ($subRoutesConfiguration as $subRouteConfiguration) {
  394. foreach ($routesConfiguration as $routeConfiguration) {
  395. $subRouteConfiguration['name'] = sprintf('%s :: %s', isset($routeConfiguration['name']) ? $routeConfiguration['name'] : 'Unnamed Route', isset($subRouteConfiguration['name']) ? $subRouteConfiguration['name'] : 'Unnamed Subroute');
  396. if (!isset($subRouteConfiguration['uriPattern'])) {
  397. throw new \TYPO3\FLOW3\Configuration\Exception\ParseErrorException('No uriPattern defined in route configuration "' . $subRouteConfiguration['name'] . '".', 1274197615);
  398. }
  399. $subRouteConfiguration['uriPattern'] = str_replace('<' . $subRouteKey . '>', $subRouteConfiguration['uriPattern'], $routeConfiguration['uriPattern']);
  400. $subRouteConfiguration = Arrays::arrayMergeRecursiveOverrule($routeConfiguration, $subRouteConfiguration);
  401. unset($subRouteConfiguration['subRoutes']);
  402. $mergedSubRoutesConfigurations[] = $subRouteConfiguration;
  403. }
  404. }
  405. return $mergedSubRoutesConfigurations;
  406. }
  407. }
  408. ?>