[BUGFIX] Use correct values in settings InstallTool
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Configuration / ConfigurationManager.php
1 <?php
2 namespace TYPO3\CMS\Core\Configuration;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Service\OpcodeCacheService;
18 use TYPO3\CMS\Core\Utility\ArrayUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Handle loading and writing of global and local (instance specific)
23 * configuration.
24 *
25 * This class handles the access to the files
26 * - EXT:core/Configuration/DefaultConfiguration.php (default TYPO3_CONF_VARS)
27 * - typo3conf/LocalConfiguration.php (overrides of TYPO3_CONF_VARS)
28 * - typo3conf/AdditionalConfiguration.php (optional additional local code blocks)
29 *
30 * IMPORTANT:
31 * This class is intended for internal core use ONLY.
32 * Extensions should usually use the resulting $GLOBALS['TYPO3_CONF_VARS'] array,
33 * do not try to modify settings in LocalConfiguration.php with an extension.
34 * @internal
35 */
36 class ConfigurationManager
37 {
38 /**
39 * @var string Path to default TYPO3_CONF_VARS file, relative to PATH_site
40 */
41 protected $defaultConfigurationFile = 'typo3/sysext/core/Configuration/DefaultConfiguration.php';
42
43 /**
44 * @var string Path to description file for TYPO3_CONF_VARS, relative to PATH_site
45 */
46 protected $defaultConfigurationDescriptionFile = 'typo3/sysext/core/Configuration/DefaultConfigurationDescription.yaml';
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 * Writing to these configuration paths is always allowed,
70 * even if the requested sub path does not exist yet.
71 *
72 * @var array
73 */
74 protected $whiteListedLocalConfigurationPaths = [
75 'EXT/extConf',
76 'EXTCONF',
77 'DB',
78 'SYS/caching/cacheConfigurations',
79 'EXTENSIONS',
80 ];
81
82 /**
83 * Return default configuration array
84 *
85 * @return array
86 */
87 public function getDefaultConfiguration()
88 {
89 return require $this->getDefaultConfigurationFileLocation();
90 }
91
92 /**
93 * Get the file location of the default configuration file,
94 * currently the path and filename.
95 *
96 * @return string
97 * @access private
98 */
99 public function getDefaultConfigurationFileLocation()
100 {
101 return PATH_site . $this->defaultConfigurationFile;
102 }
103
104 /**
105 * Get the file location of the default configuration description file,
106 * currently the path and filename.
107 *
108 * @return string
109 * @access private
110 */
111 public function getDefaultConfigurationDescriptionFileLocation()
112 {
113 return PATH_site . $this->defaultConfigurationDescriptionFile;
114 }
115
116 /**
117 * Return local configuration array typo3conf/LocalConfiguration.php
118 *
119 * @return array Content array of local configuration file
120 */
121 public function getLocalConfiguration()
122 {
123 return require $this->getLocalConfigurationFileLocation();
124 }
125
126 /**
127 * Get the file location of the local configuration file,
128 * currently the path and filename.
129 *
130 * @return string
131 * @access private
132 */
133 public function getLocalConfigurationFileLocation()
134 {
135 return PATH_site . $this->localConfigurationFile;
136 }
137
138 /**
139 * Returns local configuration array merged with default configuration
140 *
141 * @return array
142 */
143 public function getMergedLocalConfiguration(): array
144 {
145 $localConfiguration = $this->getDefaultConfiguration();
146 ArrayUtility::mergeRecursiveWithOverrule($localConfiguration, $this->getLocalConfiguration());
147 return $localConfiguration;
148 }
149
150 /**
151 * Get the file location of the additional configuration file,
152 * currently the path and filename.
153 *
154 * @return string
155 * @access private
156 */
157 public function getAdditionalConfigurationFileLocation()
158 {
159 return PATH_site . $this->additionalConfigurationFile;
160 }
161
162 /**
163 * Get absolute file location of factory configuration file
164 *
165 * @return string
166 */
167 protected function getFactoryConfigurationFileLocation()
168 {
169 return PATH_site . $this->factoryConfigurationFile;
170 }
171
172 /**
173 * Get absolute file location of factory configuration file
174 *
175 * @return string
176 */
177 protected function getAdditionalFactoryConfigurationFileLocation()
178 {
179 return PATH_site . $this->additionalFactoryConfigurationFile;
180 }
181
182 /**
183 * Override local configuration with new values.
184 *
185 * @param array $configurationToMerge Override configuration array
186 */
187 public function updateLocalConfiguration(array $configurationToMerge)
188 {
189 $newLocalConfiguration = $this->getLocalConfiguration();
190 ArrayUtility::mergeRecursiveWithOverrule($newLocalConfiguration, $configurationToMerge);
191 $this->writeLocalConfiguration($newLocalConfiguration);
192 }
193
194 /**
195 * Get a value at given path from default configuration
196 *
197 * @param string $path Path to search for
198 * @return mixed Value at path
199 */
200 public function getDefaultConfigurationValueByPath($path)
201 {
202 return ArrayUtility::getValueByPath($this->getDefaultConfiguration(), $path);
203 }
204
205 /**
206 * Get a value at given path from local configuration
207 *
208 * @param string $path Path to search for
209 * @return mixed Value at path
210 */
211 public function getLocalConfigurationValueByPath($path)
212 {
213 return ArrayUtility::getValueByPath($this->getLocalConfiguration(), $path);
214 }
215
216 /**
217 * Get a value from configuration, this is default configuration
218 * merged with local configuration
219 *
220 * @param string $path Path to search for
221 * @return mixed
222 */
223 public function getConfigurationValueByPath($path)
224 {
225 $defaultConfiguration = $this->getDefaultConfiguration();
226 ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $this->getLocalConfiguration());
227 return ArrayUtility::getValueByPath($defaultConfiguration, $path);
228 }
229
230 /**
231 * Update a given path in local configuration to a new value.
232 *
233 * @param string $path Path to update
234 * @param mixed $value Value to set
235 * @return bool TRUE on success
236 */
237 public function setLocalConfigurationValueByPath($path, $value)
238 {
239 $result = false;
240 if ($this->isValidLocalConfigurationPath($path)) {
241 $localConfiguration = $this->getLocalConfiguration();
242 $localConfiguration = ArrayUtility::setValueByPath($localConfiguration, $path, $value);
243 $result = $this->writeLocalConfiguration($localConfiguration);
244 }
245 return $result;
246 }
247
248 /**
249 * Update / set a list of path and value pairs in local configuration file
250 *
251 * @param array $pairs Key is path, value is value to set
252 * @return bool TRUE on success
253 */
254 public function setLocalConfigurationValuesByPathValuePairs(array $pairs)
255 {
256 $localConfiguration = $this->getLocalConfiguration();
257 foreach ($pairs as $path => $value) {
258 if ($this->isValidLocalConfigurationPath($path)) {
259 $localConfiguration = ArrayUtility::setValueByPath($localConfiguration, $path, $value);
260 }
261 }
262 return $this->writeLocalConfiguration($localConfiguration);
263 }
264
265 /**
266 * Remove keys from LocalConfiguration
267 *
268 * @param array $keys Array with key paths to remove from LocalConfiguration
269 * @return bool TRUE if something was removed
270 */
271 public function removeLocalConfigurationKeysByPath(array $keys)
272 {
273 $result = false;
274 $localConfiguration = $this->getLocalConfiguration();
275 foreach ($keys as $path) {
276 // Remove key if path is within LocalConfiguration
277 if (ArrayUtility::isValidPath($localConfiguration, $path)) {
278 $result = true;
279 $localConfiguration = ArrayUtility::removeByPath($localConfiguration, $path);
280 }
281 }
282 if ($result) {
283 $this->writeLocalConfiguration($localConfiguration);
284 }
285 return $result;
286 }
287
288 /**
289 * Checks if the configuration can be written.
290 *
291 * @return bool
292 * @access private
293 */
294 public function canWriteConfiguration()
295 {
296 $fileLocation = $this->getLocalConfigurationFileLocation();
297 return @is_writable(file_exists($fileLocation) ? $fileLocation : PATH_site . 'typo3conf/');
298 }
299
300 /**
301 * Reads the configuration array and exports it to the global variable
302 *
303 * @access private
304 * @throws \UnexpectedValueException
305 */
306 public function exportConfiguration()
307 {
308 if (@is_file($this->getLocalConfigurationFileLocation())) {
309 $localConfiguration = $this->getLocalConfiguration();
310 if (is_array($localConfiguration)) {
311 $defaultConfiguration = $this->getDefaultConfiguration();
312 ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $localConfiguration);
313 $GLOBALS['TYPO3_CONF_VARS'] = $defaultConfiguration;
314 } else {
315 throw new \UnexpectedValueException('LocalConfiguration invalid.', 1349272276);
316 }
317 if (@is_file($this->getAdditionalConfigurationFileLocation())) {
318 require $this->getAdditionalConfigurationFileLocation();
319 }
320 } else {
321 // No LocalConfiguration (yet), load DefaultConfiguration only
322 $GLOBALS['TYPO3_CONF_VARS'] = $this->getDefaultConfiguration();
323 }
324 }
325
326 /**
327 * Write local configuration array to typo3conf/LocalConfiguration.php
328 *
329 * @param array $configuration The local configuration to be written
330 * @throws \RuntimeException
331 * @return bool TRUE on success
332 * @access private
333 */
334 public function writeLocalConfiguration(array $configuration)
335 {
336 $localConfigurationFile = $this->getLocalConfigurationFileLocation();
337 if (!$this->canWriteConfiguration()) {
338 throw new \RuntimeException(
339 $localConfigurationFile . ' is not writable.',
340 1346323822
341 );
342 }
343 $configuration = ArrayUtility::sortByKeyRecursive($configuration);
344 $result = GeneralUtility::writeFile(
345 $localConfigurationFile,
346 '<?php' . LF .
347 'return ' .
348 ArrayUtility::arrayExport(
349 ArrayUtility::renumberKeysToAvoidLeapsIfKeysAreAllNumeric($configuration)
350 ) .
351 ';' . LF,
352 true
353 );
354
355 GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive($localConfigurationFile);
356
357 return $result;
358 }
359
360 /**
361 * Write additional configuration array to typo3conf/AdditionalConfiguration.php
362 *
363 * @param array $additionalConfigurationLines The configuration lines to be written
364 * @throws \RuntimeException
365 * @return bool TRUE on success
366 * @access private
367 */
368 public function writeAdditionalConfiguration(array $additionalConfigurationLines)
369 {
370 return GeneralUtility::writeFile(
371 PATH_site . $this->additionalConfigurationFile,
372 '<?php' . LF .
373 implode(LF, $additionalConfigurationLines) . LF
374 );
375 }
376
377 /**
378 * Uses FactoryConfiguration file and a possible AdditionalFactoryConfiguration
379 * file in typo3conf to create a basic LocalConfiguration.php. This is used
380 * by the install tool in an early step.
381 *
382 * @throws \RuntimeException
383 * @access private
384 */
385 public function createLocalConfigurationFromFactoryConfiguration()
386 {
387 if (file_exists($this->getLocalConfigurationFileLocation())) {
388 throw new \RuntimeException(
389 'LocalConfiguration.php exists already',
390 1364836026
391 );
392 }
393 $localConfigurationArray = require $this->getFactoryConfigurationFileLocation();
394 $additionalFactoryConfigurationFileLocation = $this->getAdditionalFactoryConfigurationFileLocation();
395 if (file_exists($additionalFactoryConfigurationFileLocation)) {
396 $additionalFactoryConfigurationArray = require $additionalFactoryConfigurationFileLocation;
397 ArrayUtility::mergeRecursiveWithOverrule(
398 $localConfigurationArray,
399 $additionalFactoryConfigurationArray
400 );
401 }
402 $this->writeLocalConfiguration($localConfigurationArray);
403 }
404
405 /**
406 * Check if access / write to given path in local configuration is allowed.
407 *
408 * @param string $path Path to search for
409 * @return bool TRUE if access is allowed
410 */
411 protected function isValidLocalConfigurationPath($path)
412 {
413 // Early return for white listed paths
414 foreach ($this->whiteListedLocalConfigurationPaths as $whiteListedPath) {
415 if (GeneralUtility::isFirstPartOfStr($path, $whiteListedPath)) {
416 return true;
417 }
418 }
419 return ArrayUtility::isValidPath($this->getDefaultConfiguration(), $path);
420 }
421 }