getDefaultConfigurationFileLocation(); } /** * Get the file location of the default configuration file, * currently the path and filename. * * @return string * @internal */ public function getDefaultConfigurationFileLocation() { return $this->defaultConfigurationFile; } /** * Get the file location of the default configuration description file, * currently the path and filename. * * @return string * @internal */ public function getDefaultConfigurationDescriptionFileLocation() { return $this->defaultConfigurationDescriptionFile; } /** * Return configuration array of typo3conf/system/settings.php or config/system/settings.php * * @return array Content array of local configuration file */ public function getLocalConfiguration(): array { return require $this->getSystemConfigurationFileLocation(); } /** * Get the file location of the TYPO3-project specific settings file, * currently the path and filename. * * Path to local overload TYPO3_CONF_VARS file. * * @internal */ public function getSystemConfigurationFileLocation(bool $relativeToProjectRoot = false): string { // For composer-based installations, the file is in config/system/settings.php $path = Environment::getConfigPath() . '/system/settings.php'; if ($relativeToProjectRoot) { return substr($path, strlen(Environment::getProjectPath()) + 1); } return $path; } /** * Returns local configuration array merged with default configuration */ public function getMergedLocalConfiguration(): array { $localConfiguration = $this->getDefaultConfiguration(); ArrayUtility::mergeRecursiveWithOverrule($localConfiguration, $this->getLocalConfiguration()); return $localConfiguration; } /** * Get the file location of the additional configuration file, * currently the path and filename. * * @return string * @internal */ public function getAdditionalConfigurationFileLocation() { // For composer-based installations, the file is in config/system/additional.php return Environment::getConfigPath() . '/system/additional.php'; } /** * Get absolute file location of factory configuration file * * @return string */ protected function getFactoryConfigurationFileLocation() { return $this->factoryConfigurationFile; } /** * Get absolute file location of factory configuration file * * @return string */ protected function getAdditionalFactoryConfigurationFileLocation() { return Environment::getLegacyConfigPath() . '/' . $this->additionalFactoryConfigurationFile; } /** * Override local configuration with new values. * * @param array $configurationToMerge Override configuration array */ public function updateLocalConfiguration(array $configurationToMerge) { $newLocalConfiguration = $this->getLocalConfiguration(); ArrayUtility::mergeRecursiveWithOverrule($newLocalConfiguration, $configurationToMerge); $this->writeLocalConfiguration($newLocalConfiguration); } /** * Get a value at given path from default configuration * * @param string $path Path to search for * @return mixed Value at path */ public function getDefaultConfigurationValueByPath($path) { return ArrayUtility::getValueByPath($this->getDefaultConfiguration(), $path); } /** * Get a value at given path from local configuration * * @param string $path Path to search for * @return mixed Value at path */ public function getLocalConfigurationValueByPath($path) { return ArrayUtility::getValueByPath($this->getLocalConfiguration(), $path); } /** * Get a value from configuration, this is default configuration * merged with local configuration * * @param string $path Path to search for * @return mixed */ public function getConfigurationValueByPath($path) { $defaultConfiguration = $this->getDefaultConfiguration(); ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $this->getLocalConfiguration()); return ArrayUtility::getValueByPath($defaultConfiguration, $path); } /** * Update a given path in local configuration to a new value. * Warning: TO BE USED ONLY to update a single feature. * NOT TO BE USED within iterations to update multiple features. * To update multiple features use setLocalConfigurationValuesByPathValuePairs(). * * @param string $path Path to update * @param mixed $value Value to set * @return bool TRUE on success */ public function setLocalConfigurationValueByPath($path, $value) { $result = false; if ($this->isValidLocalConfigurationPath($path)) { $localConfiguration = $this->getLocalConfiguration(); $localConfiguration = ArrayUtility::setValueByPath($localConfiguration, $path, $value); $result = $this->writeLocalConfiguration($localConfiguration); } return $result; } /** * Update / set a list of path and value pairs in local configuration file * * @param array $pairs Key is path, value is value to set * @return bool TRUE on success */ public function setLocalConfigurationValuesByPathValuePairs(array $pairs) { $localConfiguration = $this->getLocalConfiguration(); foreach ($pairs as $path => $value) { if ($this->isValidLocalConfigurationPath($path)) { $localConfiguration = ArrayUtility::setValueByPath($localConfiguration, $path, $value); } } return $this->writeLocalConfiguration($localConfiguration); } /** * Remove keys from LocalConfiguration * * @param array $keys Array with key paths to remove from LocalConfiguration * @return bool TRUE if something was removed */ public function removeLocalConfigurationKeysByPath(array $keys): bool { $result = false; $localConfiguration = $this->getLocalConfiguration(); foreach ($keys as $path) { // Remove key if path is within LocalConfiguration if (ArrayUtility::isValidPath($localConfiguration, $path)) { $result = true; $localConfiguration = ArrayUtility::removeByPath($localConfiguration, $path); } } if ($result) { $this->writeLocalConfiguration($localConfiguration); } return $result; } /** * Enables a certain feature and writes the option to system/settings.php * Short-hand method * Warning: TO BE USED ONLY to enable a single feature. * NOT TO BE USED within iterations to enable multiple features. * To update multiple features use setLocalConfigurationValuesByPathValuePairs(). * * @param string $featureName something like "InlineSvgImages" * @return bool true on successful writing the setting */ public function enableFeature(string $featureName): bool { return $this->setLocalConfigurationValueByPath('SYS/features/' . $featureName, true); } /** * Disables a feature and writes the option to system/settings.php * Short-hand method * Warning: TO BE USED ONLY to disable a single feature. * NOT TO BE USED within iterations to disable multiple features. * To update multiple features use setLocalConfigurationValuesByPathValuePairs(). * * @param string $featureName something like "InlineSvgImages" * @return bool true on successful writing the setting */ public function disableFeature(string $featureName): bool { return $this->setLocalConfigurationValueByPath('SYS/features/' . $featureName, false); } /** * Checks if the configuration can be written. * * @return bool * @internal */ public function canWriteConfiguration() { $fileLocation = $this->getSystemConfigurationFileLocation(); return @is_writable(file_exists($fileLocation) ? $fileLocation : dirname($fileLocation)); } /** * Reads the configuration array and exports it to the global variable * * @internal * @throws \UnexpectedValueException */ public function exportConfiguration(): void { if (@is_file($this->getSystemConfigurationFileLocation())) { $localConfiguration = $this->getLocalConfiguration(); $defaultConfiguration = $this->getDefaultConfiguration(); ArrayUtility::mergeRecursiveWithOverrule($defaultConfiguration, $localConfiguration); $GLOBALS['TYPO3_CONF_VARS'] = $defaultConfiguration; } else { // No LocalConfiguration (yet), load DefaultConfiguration $GLOBALS['TYPO3_CONF_VARS'] = $this->getDefaultConfiguration(); } // Load AdditionalConfiguration if (@is_file($this->getAdditionalConfigurationFileLocation())) { require $this->getAdditionalConfigurationFileLocation(); } } /** * Write configuration array to %config-dir%/system/settings.php * * @param array $configuration The local configuration to be written * @throws \RuntimeException * @return bool TRUE on success * @internal */ public function writeLocalConfiguration(array $configuration) { $systemSettingsFile = $this->getSystemConfigurationFileLocation(); if (!$this->canWriteConfiguration()) { throw new SettingsWriteException( $this->getSystemConfigurationFileLocation(true) . ' is not writable.', 1346323822 ); } $configuration = ArrayUtility::sortByKeyRecursive($configuration); $result = GeneralUtility::writeFile( $systemSettingsFile, "clearAllActive($systemSettingsFile); return $result; } /** * Write additional configuration array to config/system/additional.php / typo3conf/system/additional.php * * @param array $additionalConfigurationLines The configuration lines to be written * @throws \RuntimeException * @return bool TRUE on success * @internal */ public function writeAdditionalConfiguration(array $additionalConfigurationLines) { return GeneralUtility::writeFile( $this->getAdditionalConfigurationFileLocation(), "getSystemConfigurationFileLocation())) { throw new \RuntimeException( basename($this->getSystemConfigurationFileLocation(true)) . ' already exists', 1364836026 ); } $localConfigurationArray = require $this->getFactoryConfigurationFileLocation(); $additionalFactoryConfigurationFileLocation = $this->getAdditionalFactoryConfigurationFileLocation(); if (file_exists($additionalFactoryConfigurationFileLocation)) { $additionalFactoryConfigurationArray = require $additionalFactoryConfigurationFileLocation; ArrayUtility::mergeRecursiveWithOverrule( $localConfigurationArray, $additionalFactoryConfigurationArray ); } $randomKey = GeneralUtility::makeInstance(Random::class)->generateRandomHexString(96); $localConfigurationArray['SYS']['encryptionKey'] = $randomKey; $this->writeLocalConfiguration($localConfigurationArray); } /** * Check if access / write to given path in local configuration is allowed. * * @param string $path Path to search for * @return bool TRUE if access is allowed */ protected function isValidLocalConfigurationPath(string $path): bool { // Early return for white listed paths foreach ($this->allowedSettingsPaths as $allowedSettingsPath) { if (str_starts_with($path, $allowedSettingsPath)) { return true; } } return ArrayUtility::isValidPath($this->getDefaultConfiguration(), $path); } }