[BUGFIX] Flush opcode caches while saving PHP files.
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Configuration / ConfigurationManager.php
1 <?php
2 namespace TYPO3\CMS\Core\Configuration;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2012-2013 Helge Funk <helge.funk@e-net.info>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 use TYPO3\CMS\Core\Utility;
28
29 /**
30 * Handle loading and writing of global and local (instance specific)
31 * configuration.
32 *
33 * This class handles the access to the files
34 * - EXT:core/Configuration/DefaultConfiguration.php (default TYPO3_CONF_VARS)
35 * - typo3conf/LocalConfiguration.php (overrides of TYPO3_CONF_VARS)
36 * - typo3conf/AdditionalConfiguration.php (optional additional local code blocks)
37 * - typo3conf/localconf.php (legacy configuration file)
38 *
39 * @author Helge Funk <helge.funk@e-net.info>
40 */
41 class ConfigurationManager {
42
43 /**
44 * @var string Path to default TYPO3_CONF_VARS file, relative to PATH_site
45 */
46 protected $defaultConfigurationFile = 'typo3/sysext/core/Configuration/DefaultConfiguration.php';
47
48 /**
49 * @var string Path to local overload TYPO3_CONF_VARS file, relative to PATH_site
50 */
51 protected $localConfigurationFile = 'typo3conf/LocalConfiguration.php';
52
53 /**
54 * @var string Path to additional local file, relative to PATH_site
55 */
56 protected $additionalConfigurationFile = 'typo3conf/AdditionalConfiguration.php';
57
58 /**
59 * @var string Path to factory configuration file used during installation as LocalConfiguration boilerplate
60 */
61 protected $factoryConfigurationFile = 'typo3/sysext/core/Configuration/FactoryConfiguration.php';
62
63 /**
64 * @var string Path to possible additional factory configuration file delivered by packages
65 */
66 protected $additionalFactoryConfigurationFile = 'typo3conf/AdditionalFactoryConfiguration.php';
67
68 /**
69 * @var string Absolute path to typo3conf directory
70 */
71 protected $pathTypo3Conf = PATH_typo3conf;
72
73 /**
74 * Writing to these configuration pathes is always allowed,
75 * even if the requested sub path does not exist yet.
76 *
77 * @var array
78 */
79 protected $whiteListedLocalConfigurationPaths = array(
80 'EXT/extConf',
81 'EXTCONF',
82 'INSTALL/wizardDone',
83 'DB',
84 'SYS/caching/cacheConfigurations',
85 );
86
87 /**
88 * Return default configuration array
89 *
90 * @return array
91 */
92 public function getDefaultConfiguration() {
93 return require $this->getDefaultConfigurationFileLocation();
94 }
95
96 /**
97 * Get the file location of the default configuration file,
98 * currently the path and filename.
99 *
100 * @return string
101 * @access private
102 */
103 public function getDefaultConfigurationFileLocation() {
104 return PATH_site . $this->defaultConfigurationFile;
105 }
106
107 /**
108 * Return local configuration array typo3conf/LocalConfiguration.php
109 *
110 * @return array Content array of local configuration file
111 */
112 public function getLocalConfiguration() {
113 return require $this->getLocalConfigurationFileLocation();
114 }
115
116 /**
117 * Get the file location of the local configuration file,
118 * currently the path and filename.
119 *
120 * @return string
121 * @access private
122 */
123 public function getLocalConfigurationFileLocation() {
124 return PATH_site . $this->localConfigurationFile;
125 }
126
127 /**
128 * Get the file location of the additional configuration file,
129 * currently the path and filename.
130 *
131 * @return string
132 * @access private
133 */
134 public function getAdditionalConfigurationFileLocation() {
135 return PATH_site . $this->additionalConfigurationFile;
136 }
137
138 /**
139 * Get absolute file location of factory configuration file
140 *
141 * @return string
142 */
143 protected function getFactoryConfigurationFileLocation() {
144 return PATH_site . $this->factoryConfigurationFile;
145 }
146
147 /**
148 * Get absolute file location of factory configuration file
149 *
150 * @return string
151 */
152 protected function getAdditionalFactoryConfigurationFileLocation() {
153 return PATH_site . $this->additionalFactoryConfigurationFile;
154 }
155
156 /**
157 * Override local configuration with new values.
158 *
159 * @param array $configurationToMerge Override configuration array
160 * @return void
161 */
162 public function updateLocalConfiguration(array $configurationToMerge) {
163 $newLocalConfiguration = $this->getLocalConfiguration();
164 Utility\ArrayUtility::mergeRecursiveWithOverrule($newLocalConfiguration, $configurationToMerge);
165 $this->writeLocalConfiguration($newLocalConfiguration);
166 }
167
168 /**
169 * Get a value at given path from default configuration
170 *
171 * @param string $path Path to search for
172 * @return mixed Value at path
173 */
174 public function getDefaultConfigurationValueByPath($path) {
175 return Utility\ArrayUtility::getValueByPath($this->getDefaultConfiguration(), $path);
176 }
177
178 /**
179 * Get a value at given path from local configuration
180 *
181 * @param string $path Path to search for
182 * @return mixed Value at path
183 */
184 public function getLocalConfigurationValueByPath($path) {
185 return Utility\ArrayUtility::getValueByPath($this->getLocalConfiguration(), $path);
186 }
187
188 /**
189 * Get a value from configuration, this is default configuration
190 * merged with local configuration
191 *
192 * @param string $path Path to search for
193 * @return mixed
194 */
195 public function getConfigurationValueByPath($path) {
196 $defaultConfiguration = $this->getDefaultConfiguration();
197 Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $this->getLocalConfiguration());
198 return Utility\ArrayUtility::getValueByPath($defaultConfiguration, $path);
199 }
200
201 /**
202 * Update a given path in local configuration to a new value.
203 *
204 * @param string $path Path to update
205 * @param mixed $value Value to set
206 * @return boolean TRUE on success
207 */
208 public function setLocalConfigurationValueByPath($path, $value) {
209 $result = FALSE;
210 if ($this->isValidLocalConfigurationPath($path)) {
211 $localConfiguration = $this->getLocalConfiguration();
212 $localConfiguration = Utility\ArrayUtility::setValueByPath($localConfiguration, $path, $value);
213 $result = $this->writeLocalConfiguration($localConfiguration);
214 }
215 return $result;
216 }
217
218 /**
219 * Update / set a list of path and value pairs in local configuration file
220 *
221 * @param array $pairs Key is path, value is value to set
222 * @return boolean TRUE on success
223 */
224 public function setLocalConfigurationValuesByPathValuePairs(array $pairs) {
225 $localConfiguration = $this->getLocalConfiguration();
226 foreach ($pairs as $path => $value) {
227 if ($this->isValidLocalConfigurationPath($path)) {
228 $localConfiguration = Utility\ArrayUtility::setValueByPath($localConfiguration, $path, $value);
229 }
230 }
231 return $this->writeLocalConfiguration($localConfiguration);
232 }
233
234 /**
235 * Remove keys from LocalConfiguration
236 *
237 * @param array $keys Array with key paths to remove from LocalConfiguration
238 * @return boolean TRUE if something was removed
239 */
240 public function removeLocalConfigurationKeysByPath(array $keys) {
241 $result = FALSE;
242 $localConfiguration = $this->getLocalConfiguration();
243 foreach ($keys as $path) {
244 // Remove key if path is within LocalConfiguration
245 if (Utility\ArrayUtility::isValidPath($localConfiguration, $path)) {
246 $result = TRUE;
247 $localConfiguration = Utility\ArrayUtility::removeByPath($localConfiguration, $path);
248 }
249 }
250 if ($result) {
251 $this->writeLocalConfiguration($localConfiguration);
252 }
253 return $result;
254 }
255
256 /**
257 * Checks if the configuration can be written.
258 *
259 * @return boolean
260 * @access private
261 */
262 public function canWriteConfiguration() {
263 $fileLocation = $this->getLocalConfigurationFileLocation();
264 return @is_writable($this->pathTypo3Conf) && (!file_exists($fileLocation) || @is_writable($fileLocation));
265 }
266
267 /**
268 * Reads the configuration array and exports it to the global variable
269 *
270 * @access private
271 * @throws \UnexpectedValueException
272 * @return void
273 */
274 public function exportConfiguration() {
275 if (@is_file($this->getLocalConfigurationFileLocation())) {
276 $localConfiguration = $this->getLocalConfiguration();
277 if (is_array($localConfiguration)) {
278 $defaultConfiguration = $this->getDefaultConfiguration();
279 Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $localConfiguration);
280 $GLOBALS['TYPO3_CONF_VARS'] = $defaultConfiguration;
281 } else {
282 throw new \UnexpectedValueException('LocalConfiguration invalid.', 1349272276);
283 }
284 if (@is_file($this->getAdditionalConfigurationFileLocation())) {
285 require $this->getAdditionalConfigurationFileLocation();
286 }
287 } else {
288 // No LocalConfiguration (yet), load DefaultConfiguration only
289 $GLOBALS['TYPO3_CONF_VARS'] = $this->getDefaultConfiguration();
290 }
291 }
292
293 /**
294 * Write local configuration array to typo3conf/LocalConfiguration.php
295 *
296 * @param array $configuration The local configuration to be written
297 * @throws \RuntimeException
298 * @return boolean TRUE on success
299 * @access private
300 */
301 public function writeLocalConfiguration(array $configuration) {
302 $localConfigurationFile = $this->getLocalConfigurationFileLocation();
303 if (!$this->canWriteConfiguration()) {
304 throw new \RuntimeException(
305 $localConfigurationFile . ' is not writable.', 1346323822
306 );
307 }
308 $configuration = Utility\ArrayUtility::sortByKeyRecursive($configuration);
309 $result = Utility\GeneralUtility::writeFile(
310 $localConfigurationFile,
311 '<?php' . LF .
312 'return ' .
313 Utility\ArrayUtility::arrayExport(
314 Utility\ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($configuration)
315 ) .
316 ';' . LF .
317 '?>',
318 TRUE
319 );
320
321 Utility\OpcodeCacheUtility::clearAllActive($localConfigurationFile);
322
323 return $result;
324 }
325
326 /**
327 * Write additional configuration array to typo3conf/AdditionalConfiguration.php
328 *
329 * @param array $additionalConfigurationLines The configuration lines to be written
330 * @throws \RuntimeException
331 * @return boolean TRUE on success
332 * @access private
333 */
334 public function writeAdditionalConfiguration(array $additionalConfigurationLines) {
335 return Utility\GeneralUtility::writeFile(
336 PATH_site . $this->additionalConfigurationFile,
337 '<?php' . LF .
338 implode(LF, $additionalConfigurationLines) . LF .
339 '?>'
340 );
341 }
342
343 /**
344 * Uses FactoryConfiguration file and a possible AdditionalFactoryConfiguration
345 * file in typo3conf to create a basic LocalConfiguration.php. This is used
346 * by the install tool in an early step.
347 *
348 * @throws \RuntimeException
349 * @return void
350 * @access private
351 */
352 public function createLocalConfigurationFromFactoryConfiguration() {
353 if (file_exists($this->getLocalConfigurationFileLocation())) {
354 throw new \RuntimeException(
355 'LocalConfiguration.php exists already',
356 1364836026
357 );
358 }
359 $localConfigurationArray = require $this->getFactoryConfigurationFileLocation();
360 $additionalFactoryConfigurationFileLocation = $this->getAdditionalFactoryConfigurationFileLocation();
361 if (file_exists($additionalFactoryConfigurationFileLocation)) {
362 $additionalFactoryConfigurationArray = require $additionalFactoryConfigurationFileLocation;
363 Utility\ArrayUtility::mergeRecursiveWithOverrule(
364 $localConfigurationArray,
365 $additionalFactoryConfigurationArray
366 );
367 }
368 $this->writeLocalConfiguration($localConfigurationArray);
369 }
370
371 /**
372 * Check if access / write to given path in local configuration is allowed.
373 *
374 * @param string $path Path to search for
375 * @return boolean TRUE if access is allowed
376 */
377 protected function isValidLocalConfigurationPath($path) {
378 // Early return for white listed paths
379 foreach ($this->whiteListedLocalConfigurationPaths as $whiteListedPath) {
380 if (Utility\GeneralUtility::isFirstPartOfStr($path, $whiteListedPath)) {
381 return TRUE;
382 }
383 }
384 return Utility\ArrayUtility::isValidPath($this->getDefaultConfiguration(), $path);
385 }
386
387 }