[BUGFIX] Don't write LocalConfiguration on every Install Tool entry
[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 = Utility\GeneralUtility::array_merge_recursive_overrule(
164 $this->getLocalConfiguration(),
165 $configurationToMerge
166 );
167 $this->writeLocalConfiguration($newLocalConfiguration);
168 }
169
170 /**
171 * Get a value at given path from default configuration
172 *
173 * @param string $path Path to search for
174 * @return mixed Value at path
175 */
176 public function getDefaultConfigurationValueByPath($path) {
177 return Utility\ArrayUtility::getValueByPath($this->getDefaultConfiguration(), $path);
178 }
179
180 /**
181 * Get a value at given path from local configuration
182 *
183 * @param string $path Path to search for
184 * @return mixed Value at path
185 */
186 public function getLocalConfigurationValueByPath($path) {
187 return Utility\ArrayUtility::getValueByPath($this->getLocalConfiguration(), $path);
188 }
189
190 /**
191 * Get a value from configuration, this is default configuration
192 * merged with local configuration
193 *
194 * @param string $path Path to search for
195 * @return mixed
196 */
197 public function getConfigurationValueByPath($path) {
198 return Utility\ArrayUtility::getValueByPath(
199 Utility\GeneralUtility::array_merge_recursive_overrule(
200 $this->getDefaultConfiguration(), $this->getLocalConfiguration()
201 ),
202 $path
203 );
204 }
205
206 /**
207 * Update a given path in local configuration to a new value.
208 *
209 * @param string $path Path to update
210 * @param mixed $value Value to set
211 * @return boolean TRUE on success
212 */
213 public function setLocalConfigurationValueByPath($path, $value) {
214 $result = FALSE;
215 if ($this->isValidLocalConfigurationPath($path)) {
216 $localConfiguration = $this->getLocalConfiguration();
217 $localConfiguration = Utility\ArrayUtility::setValueByPath($localConfiguration, $path, $value);
218 $result = $this->writeLocalConfiguration($localConfiguration);
219 }
220 return $result;
221 }
222
223 /**
224 * Update / set a list of path and value pairs in local configuration file
225 *
226 * @param array $pairs Key is path, value is value to set
227 * @return boolean TRUE on success
228 */
229 public function setLocalConfigurationValuesByPathValuePairs(array $pairs) {
230 $localConfiguration = $this->getLocalConfiguration();
231 foreach ($pairs as $path => $value) {
232 if ($this->isValidLocalConfigurationPath($path)) {
233 $localConfiguration = Utility\ArrayUtility::setValueByPath($localConfiguration, $path, $value);
234 }
235 }
236 return $this->writeLocalConfiguration($localConfiguration);
237 }
238
239 /**
240 * Remove keys from LocalConfiguration
241 *
242 * @param array $keys Array with key paths to remove from LocalConfiguration
243 * @return boolean TRUE if something was removed
244 */
245 public function removeLocalConfigurationKeysByPath(array $keys) {
246 $result = FALSE;
247 $localConfiguration = $this->getLocalConfiguration();
248 foreach ($keys as $path) {
249 // Remove key if path is within LocalConfiguration
250 if (Utility\ArrayUtility::isValidPath($localConfiguration, $path)) {
251 $result = TRUE;
252 $localConfiguration = Utility\ArrayUtility::removeByPath($localConfiguration, $path);
253 }
254 }
255 if ($result) {
256 $this->writeLocalConfiguration($localConfiguration);
257 }
258 return $result;
259 }
260
261 /**
262 * Checks if the configuration can be written.
263 *
264 * @return boolean
265 * @access private
266 */
267 public function canWriteConfiguration() {
268 $result = TRUE;
269 if (!@is_writable($this->pathTypo3Conf)) {
270 $result = FALSE;
271 }
272 if (
273 file_exists($this->getLocalConfigurationFileLocation())
274 && !@is_writable($this->getLocalConfigurationFileLocation())
275 ) {
276 $result = FALSE;
277 }
278 return $result;
279 }
280
281 /**
282 * Reads the configuration array and exports it to the global variable
283 *
284 * @access private
285 * @throws \UnexpectedValueException
286 * @return void
287 */
288 public function exportConfiguration() {
289 if (@is_file($this->getLocalConfigurationFileLocation())) {
290 $localConfiguration = $this->getLocalConfiguration();
291 if (is_array($localConfiguration)) {
292 $GLOBALS['TYPO3_CONF_VARS'] = Utility\GeneralUtility::array_merge_recursive_overrule($this->getDefaultConfiguration(), $localConfiguration);
293 } else {
294 throw new \UnexpectedValueException('LocalConfiguration invalid.', 1349272276);
295 }
296 if (@is_file($this->getAdditionalConfigurationFileLocation())) {
297 require $this->getAdditionalConfigurationFileLocation();
298 }
299 } else {
300 // No LocalConfiguration (yet), load DefaultConfiguration only
301 $GLOBALS['TYPO3_CONF_VARS'] = $this->getDefaultConfiguration();
302 }
303 }
304
305 /**
306 * Write local configuration array to typo3conf/LocalConfiguration.php
307 *
308 * @param array $configuration The local configuration to be written
309 * @throws \RuntimeException
310 * @return boolean TRUE on success
311 * @access private
312 */
313 public function writeLocalConfiguration(array $configuration) {
314 $localConfigurationFile = $this->getLocalConfigurationFileLocation();
315 if (!$this->canWriteConfiguration()) {
316 throw new \RuntimeException(
317 $localConfigurationFile . ' is not writable.', 1346323822
318 );
319 }
320 $configuration = Utility\ArrayUtility::sortByKeyRecursive($configuration);
321 $result = Utility\GeneralUtility::writeFile(
322 $localConfigurationFile,
323 '<?php' . LF .
324 'return ' .
325 Utility\ArrayUtility::arrayExport(
326 Utility\ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($configuration)
327 ) .
328 ';' . LF .
329 '?>',
330 TRUE
331 );
332 return $result === FALSE ? FALSE : TRUE;
333 }
334
335 /**
336 * Write additional configuration array to typo3conf/AdditionalConfiguration.php
337 *
338 * @param array $additionalConfigurationLines The configuration lines to be written
339 * @throws \RuntimeException
340 * @return boolean TRUE on success
341 * @access private
342 */
343 public function writeAdditionalConfiguration(array $additionalConfigurationLines) {
344 $result = Utility\GeneralUtility::writeFile(
345 PATH_site . $this->additionalConfigurationFile,
346 '<?php' . LF .
347 implode(LF, $additionalConfigurationLines) . LF .
348 '?>'
349 );
350 return $result === FALSE ? FALSE : TRUE;
351 }
352
353 /**
354 * Uses FactoryConfiguration file and a possible AdditionalFactoryConfiguration
355 * file in typo3conf to create a basic LocalConfiguration.php. This is used
356 * by the install tool in an early step.
357 *
358 * @throws \RuntimeException
359 * @return void
360 * @access private
361 */
362 public function createLocalConfigurationFromFactoryConfiguration() {
363 if (file_exists($this->getLocalConfigurationFileLocation())) {
364 throw new \RuntimeException(
365 'LocalConfiguration.php exists already',
366 1364836026
367 );
368 }
369 $localConfigurationArray = require $this->getFactoryConfigurationFileLocation();
370 $additionalFactoryConfigurationFileLocation = $this->getAdditionalFactoryConfigurationFileLocation();
371 if (file_exists($additionalFactoryConfigurationFileLocation)) {
372 $additionalFactoryConfigurationArray = require $additionalFactoryConfigurationFileLocation;
373 $localConfigurationArray = Utility\GeneralUtility::array_merge_recursive_overrule(
374 $localConfigurationArray,
375 $additionalFactoryConfigurationArray
376 );
377 }
378 $this->writeLocalConfiguration($localConfigurationArray);
379 }
380
381 /**
382 * Check if access / write to given path in local configuration is allowed.
383 *
384 * @param string $path Path to search for
385 * @return boolean TRUE if access is allowed
386 */
387 protected function isValidLocalConfigurationPath($path) {
388 // Early return for white listed paths
389 foreach ($this->whiteListedLocalConfigurationPaths as $whiteListedPath) {
390 if (Utility\GeneralUtility::isFirstPartOfStr($path, $whiteListedPath)) {
391 return TRUE;
392 }
393 }
394 return Utility\ArrayUtility::isValidPath($this->getDefaultConfiguration(), $path);
395 }
396
397 }