From fea4b202c15b8890da671c9983a07b344c884ca2 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 18:50:02 +0100 Subject: [PATCH 01/19] Introduce ConfigFileTransformer for Config files --- .../Config/Util/ConfigFileTransformer.php | 85 +++++++++++++++++++ tests/datasets/config/A.node.config.php | 22 +++++ tests/datasets/config/B.node.config.php | 38 +++++++++ .../Config/Util/ConfigFileTransformerTest.php | 54 ++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 src/Core/Config/Util/ConfigFileTransformer.php create mode 100644 tests/datasets/config/A.node.config.php create mode 100644 tests/datasets/config/B.node.config.php create mode 100644 tests/src/Core/Config/Util/ConfigFileTransformerTest.php diff --git a/src/Core/Config/Util/ConfigFileTransformer.php b/src/Core/Config/Util/ConfigFileTransformer.php new file mode 100644 index 000000000..9b80991af --- /dev/null +++ b/src/Core/Config/Util/ConfigFileTransformer.php @@ -0,0 +1,85 @@ +. + * + */ + +namespace Friendica\Core\Config\Util; + +/** + * Util to transform back the config array into a string + */ +class ConfigFileTransformer +{ + public static function encode(array $data): string + { + $dataString = ' [" . PHP_EOL; + + if (is_array($data[$category])) { + $keys = array_keys($data[$category]); + + foreach ($keys as $key) { + $dataString .= static::mapConfigValue($key, $data[$category][$key]); + } + } + $dataString .= "\t]," . PHP_EOL; + } + + $dataString .= "];" . PHP_EOL; + + return $dataString; + } + + protected static function extractArray(array $config, int $level = 0): string + { + $string = ''; + + foreach ($config as $configKey => $configValue) { + $string .= static::mapConfigValue($configKey, $configValue, $level); + } + + return $string; + } + + protected static function mapConfigValue(string $key, $value, $level = 0): string + { + $string = str_repeat("\t", $level + 2) . "'$key' => "; + + if (is_array($value)) { + $string .= "[" . PHP_EOL; + $string .= static::extractArray($value, ++$level); + $string .= str_repeat("\t", $level + 1) . '],'; + } elseif (is_bool($value)) { + $string .= ($value ? 'true' : 'false') . ","; + } elseif (is_numeric($value)) { + $string .= $value . ","; + } else { + $string .= sprintf('\'%s\',', $value); + } + + $string .= PHP_EOL; + + return $string; + } +} diff --git a/tests/datasets/config/A.node.config.php b/tests/datasets/config/A.node.config.php new file mode 100644 index 000000000..b81e01737 --- /dev/null +++ b/tests/datasets/config/A.node.config.php @@ -0,0 +1,22 @@ + [ + 'hostname' => 'testhost', + 'username' => 'testuser', + 'password' => 'testpw', + 'database' => 'testdb', + 'charset' => 'utf8mb4', + ], + 'config' => [ + 'admin_email' => 'admin@test.it', + 'sitename' => 'Friendica Social Network', + 'register_policy' => 2, + 'register_text' => '', + ], + 'system' => [ + 'default_timezone' => 'UTC', + 'language' => 'en', + 'theme' => 'frio', + ], +]; diff --git a/tests/datasets/config/B.node.config.php b/tests/datasets/config/B.node.config.php new file mode 100644 index 000000000..94b2e3f12 --- /dev/null +++ b/tests/datasets/config/B.node.config.php @@ -0,0 +1,38 @@ + [ + 'hostname' => 'testhost', + 'username' => 'testuser', + 'password' => 'testpw', + 'database' => 'testdb', + 'charset' => 'utf8mb4', + ], + 'config' => [ + 'admin_email' => 'admin@test.it', + 'sitename' => 'Friendica Social Network', + 'register_policy' => 2, + 'register_text' => '', + 'test' => [ + 'a' => [ + 'next' => 'value', + 'bool' => false, + 'innerArray' => [ + 'a' => 4.55, + 'b' => false, + 'string2' => 'false', + ], + ], + 'v' => true, + 'v3' => 1, + 'v4' => 5.6443, + ], + ], + 'system' => [ + 'default_timezone' => 'UTC', + 'language' => 'en', + 'theme' => 'frio', + 'int' => 23, + 'float' => 2.5, + ], +]; diff --git a/tests/src/Core/Config/Util/ConfigFileTransformerTest.php b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php new file mode 100644 index 000000000..6cd5bc706 --- /dev/null +++ b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php @@ -0,0 +1,54 @@ +. + * + */ + +namespace Friendica\Test\src\Core\Config\Util; + +use Friendica\Core\Config\Util\ConfigFileTransformer; +use Friendica\Test\MockedTest; + +class ConfigFileTransformerTest extends MockedTest +{ + public function dataTests() + { + return [ + 'default' => [ + 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/A.node.config.php'), + ], + 'extended' => [ + 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/B.node.config.php'), + ], + ]; + } + + /** + * Tests if the given config will be decoded into an array and encoded into the same string again + * + * @dataProvider dataTests + */ + public function testConfigFile(string $configFile) + { + $dataArray = include $configFile; + + $newConfig = ConfigFileTransformer::encode($dataArray); + + self::assertEquals(file_get_contents($configFile), $newConfig); + } +} From 0f91d1cbde9e36b10d66fe756229ec8a9635f7eb Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 21:10:37 +0100 Subject: [PATCH 02/19] Introduce ConfigFileManager for config files --- database.sql | 14 +- doc/database.md | 1 - doc/database/db_config.md | 25 --- src/App/BaseURL.php | 43 +++-- src/Console/Maintenance.php | 8 +- src/Console/Relocate.php | 9 +- .../Config/Capability/IManageConfigValues.php | 11 +- src/Core/Config/Factory/Config.php | 26 +-- src/Core/Config/Repository/Config.php | 78 +------- src/Core/Config/Type/AbstractConfig.php | 18 +- src/Core/Config/Type/JitConfig.php | 28 ++- src/Core/Config/Type/PreloadConfig.php | 28 ++- ...igFileLoader.php => ConfigFileManager.php} | 81 +++++++-- src/Core/Config/ValueObject/Cache.php | 36 +++- .../Type/DBKeyValueStorage.php | 2 +- src/Core/Update.php | 20 +- src/Database/DBStructure.php | 9 +- src/Module/Admin/Site.php | 172 +++++++++--------- static/dbstructure.config.php | 15 +- static/dependencies.config.php | 2 +- update.php | 19 ++ 21 files changed, 343 insertions(+), 302 deletions(-) delete mode 100644 doc/database/db_config.md rename src/Core/Config/Util/{ConfigFileLoader.php => ConfigFileManager.php} (83%) diff --git a/database.sql b/database.sql index 19007e6c0..c413f2c24 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2023.03-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1507 +-- DB_UPDATE_VERSION 1508 -- ------------------------------------------ @@ -494,18 +494,6 @@ CREATE TABLE IF NOT EXISTS `cache` ( INDEX `k_expires` (`k`,`expires`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data'; --- --- TABLE config --- -CREATE TABLE IF NOT EXISTS `config` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `cat` varbinary(50) NOT NULL DEFAULT '' COMMENT '', - `k` varbinary(50) NOT NULL DEFAULT '' COMMENT '', - `v` mediumtext COMMENT '', - PRIMARY KEY(`id`), - UNIQUE INDEX `cat_k` (`cat`,`k`) -) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage'; - -- -- TABLE contact-relation -- diff --git a/doc/database.md b/doc/database.md index edfb7b822..95e0367af 100644 --- a/doc/database.md +++ b/doc/database.md @@ -18,7 +18,6 @@ Database Tables | [arrived-activity](help/database/db_arrived-activity) | Id of arrived activities | | [attach](help/database/db_attach) | file attachments | | [cache](help/database/db_cache) | Stores temporary data | -| [config](help/database/db_config) | main configuration storage | | [contact](help/database/db_contact) | contact table | | [contact-relation](help/database/db_contact-relation) | Contact relations | | [conv](help/database/db_conv) | private messages | diff --git a/doc/database/db_config.md b/doc/database/db_config.md deleted file mode 100644 index 7d7618794..000000000 --- a/doc/database/db_config.md +++ /dev/null @@ -1,25 +0,0 @@ -Table config -=========== - -main configuration storage - -Fields ------- - -| Field | Description | Type | Null | Key | Default | Extra | -| ----- | ----------- | ------------- | ---- | --- | ------- | -------------- | -| id | | int unsigned | NO | PRI | NULL | auto_increment | -| cat | | varbinary(50) | NO | | | | -| k | | varbinary(50) | NO | | | | -| v | | mediumtext | YES | | NULL | | - -Indexes ------------- - -| Name | Fields | -| ------- | -------------- | -| PRIMARY | id | -| cat_k | UNIQUE, cat, k | - - -Return to [database documentation](help/database) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index 20fd54916..f79bae38f 100644 --- a/src/App/BaseURL.php +++ b/src/App/BaseURL.php @@ -177,7 +177,7 @@ class BaseURL $currURLPath = $this->urlPath; if (!empty($hostname) && $hostname !== $this->hostname) { - if ($this->config->set('config', 'hostname', $hostname)) { + if ($this->config->set('config', 'hostname', $hostname, false)) { $this->hostname = $hostname; } else { return false; @@ -185,40 +185,45 @@ class BaseURL } if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { - if ($this->config->set('system', 'ssl_policy', $sslPolicy)) { + if ($this->config->set('system', 'ssl_policy', $sslPolicy, false)) { $this->sslPolicy = $sslPolicy; } else { $this->hostname = $currHostname; - $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('config', 'hostname', $this->hostname, false); + $this->config->save(); return false; } } if (isset($urlPath) && $urlPath !== $this->urlPath) { - if ($this->config->set('system', 'urlpath', $urlPath)) { + if ($this->config->set('system', 'urlpath', $urlPath, false)) { $this->urlPath = $urlPath; } else { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; - $this->config->set('config', 'hostname', $this->hostname); - $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('config', 'hostname', $this->hostname, false); + $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); + $this->config->save(); return false; } } $this->determineBaseUrl(); - if (!$this->config->set('system', 'url', $this->url)) { + if (!$this->config->set('system', 'url', $this->url, false)) { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; $this->urlPath = $currURLPath; $this->determineBaseUrl(); - $this->config->set('config', 'hostname', $this->hostname); - $this->config->set('system', 'ssl_policy', $this->sslPolicy); - $this->config->set('system', 'urlpath', $this->urlPath); + $this->config->set('config', 'hostname', $this->hostname, false); + $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); + $this->config->set('system', 'urlpath', $this->urlPath, false); + $this->config->save(); return false; } + $this->config->save(); + return true; } @@ -295,17 +300,21 @@ class BaseURL $this->sslPolicy = $this->config->get('system', 'ssl_policy'); $this->url = $this->config->get('system', 'url'); + $savable = false; + if (empty($this->hostname)) { $this->determineHostname(); if (!empty($this->hostname)) { - $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('config', 'hostname', $this->hostname, false); + $savable = true; } } if (!isset($this->urlPath)) { $this->determineURLPath(); - $this->config->set('system', 'urlpath', $this->urlPath); + $this->config->set('system', 'urlpath', $this->urlPath, false); + $savable = true; } if (!isset($this->sslPolicy)) { @@ -314,16 +323,22 @@ class BaseURL } else { $this->sslPolicy = self::DEFAULT_SSL_SCHEME; } - $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); + $savable = true; } if (empty($this->url)) { $this->determineBaseUrl(); if (!empty($this->url)) { - $this->config->set('system', 'url', $this->url); + $this->config->set('system', 'url', $this->url, false); + $savable = true; } } + + if ($savable) { + $this->config->save(); + } } /** diff --git a/src/Console/Maintenance.php b/src/Console/Maintenance.php index 97ce9c27f..bd3aef7c2 100644 --- a/src/Console/Maintenance.php +++ b/src/Console/Maintenance.php @@ -100,16 +100,18 @@ HELP; $enabled = intval($this->getArgument(0)); - $this->config->set('system', 'maintenance', $enabled); + $this->config->set('system', 'maintenance', $enabled, false); $reason = $this->getArgument(1); if ($enabled && $this->getArgument(1)) { - $this->config->set('system', 'maintenance_reason', $this->getArgument(1)); + $this->config->set('system', 'maintenance_reason', $this->getArgument(1), false); } else { - $this->config->set('system', 'maintenance_reason', ''); + $this->config->set('system', 'maintenance_reason', '', false); } + $this->config->save(); + if ($enabled) { $mode_str = "maintenance mode"; } else { diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 3d13e10a0..8a76c9207 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -101,8 +101,8 @@ HELP; $old_host = str_replace('http://', '@', Strings::normaliseLink($old_url)); $this->out('Entering maintenance mode'); - $this->config->set('system', 'maintenance', true); - $this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url); + $this->config->set('system', 'maintenance', true, false); + $this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url, false); try { if (!$this->database->transaction()) { @@ -189,8 +189,9 @@ HELP; return 1; } finally { $this->out('Leaving maintenance mode'); - $this->config->set('system', 'maintenance', false); - $this->config->set('system', 'maintenance_reason', ''); + $this->config->set('system', 'maintenance', false, false); + $this->config->set('system', 'maintenance_reason', '', false); + $this->config->save(); } // send relocate diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index ecfb2a7aa..27238822a 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -71,12 +71,18 @@ interface IManageConfigValues * @param string $cat The category of the configuration value * @param string $key The configuration key to set * @param mixed $value The value to store + * @param bool $autosave If true, implicit save the value * * @return bool Operation success * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function set(string $cat, string $key, $value): bool; + public function set(string $cat, string $key, $value, bool $autosave = true): bool; + + /** + * Save back the overridden values of the config cache + */ + public function save(); /** * Deletes the given key from the system configuration. @@ -85,13 +91,14 @@ interface IManageConfigValues * * @param string $cat The category of the configuration value * @param string $key The configuration key to delete + * @param bool $autosave If true, implicit save the value * * @return bool * * @throws ConfigPersistenceException In case the persistence layer throws errors * */ - public function delete(string $cat, string $key): bool; + public function delete(string $cat, string $key, bool $autosave = true): bool; /** * Returns the Config Cache diff --git a/src/Core/Config/Factory/Config.php b/src/Core/Config/Factory/Config.php index 94293dd17..fac931fac 100644 --- a/src/Core/Config/Factory/Config.php +++ b/src/Core/Config/Factory/Config.php @@ -21,12 +21,12 @@ namespace Friendica\Core\Config\Factory; -use Friendica\Core\Config\Capability; -use Friendica\Core\Config\Repository; -use Friendica\Core\Config\Type; use Friendica\Core\Config\Util; use Friendica\Core\Config\ValueObject\Cache; +/** + * The config factory for creating either the cache or the whole model + */ class Config { /** @@ -54,9 +54,9 @@ class Config * @param string $basePath The basepath of FRIENDICA * @param array $server The $_SERVER array * - * @return Util\ConfigFileLoader + * @return Util\ConfigFileManager */ - public function createConfigFileLoader(string $basePath, array $server = []): Util\ConfigFileLoader + public function createConfigFileLoader(string $basePath, array $server = []): Util\ConfigFileManager { if (!empty($server[self::CONFIG_DIR_ENV]) && is_dir($server[self::CONFIG_DIR_ENV])) { $configDir = $server[self::CONFIG_DIR_ENV]; @@ -65,19 +65,19 @@ class Config } $staticDir = $basePath . DIRECTORY_SEPARATOR . self::STATIC_DIR; - return new Util\ConfigFileLoader($basePath, $configDir, $staticDir); + return new Util\ConfigFileManager($basePath, $configDir, $staticDir, new Util\ConfigFileTransformer()); } /** - * @param Util\ConfigFileLoader $loader The Config Cache loader (INI/config/.htconfig) - * @param array $server + * @param Util\ConfigFileManager $configFileManager The Config Cache manager (INI/config/.htconfig) + * @param array $server * * @return Cache */ - public function createCache(Util\ConfigFileLoader $loader, array $server = []): Cache + public function createCache(Util\ConfigFileManager $configFileManager, array $server = []): Cache { $configCache = new Cache(); - $loader->setupCache($configCache, $server); + $configFileManager->setupCache($configCache, $server); return $configCache; } @@ -88,12 +88,12 @@ class Config * * @return Capability\IManageConfigValues */ - public function create(Cache $configCache, Repository\Config $configRepo) + public function create(Util\ConfigFileManager $loader, Cache $configCache, Repository\Config $configRepo) { if ($configCache->get('system', 'config_adapter') === 'preload') { - $configuration = new Type\PreloadConfig($configCache, $configRepo); + $configuration = new Type\PreloadConfig($loader, $configCache, $configRepo); } else { - $configuration = new Type\JitConfig($configCache, $configRepo); + $configuration = new Type\JitConfig($loader, $configCache, $configRepo); } return $configuration; diff --git a/src/Core/Config/Repository/Config.php b/src/Core/Config/Repository/Config.php index 3bec99f84..eabff9e68 100644 --- a/src/Core/Config/Repository/Config.php +++ b/src/Core/Config/Repository/Config.php @@ -51,7 +51,7 @@ class Config */ public function isConnected(): bool { - return $this->db->isConnected() && !$this->mode->isInstall(); + return true; } /** @@ -65,31 +65,7 @@ class Config */ public function load(?string $cat = null): array { - $return = []; - - try { - if (empty($cat)) { - $configs = $this->db->select(static::$table_name, ['cat', 'v', 'k']); - } else { - $configs = $this->db->select(static::$table_name, ['cat', 'v', 'k'], ['cat' => $cat]); - } - - while ($config = $this->db->fetch($configs)) { - $key = $config['k']; - $value = ValueConversion::toConfigValue($config['v']); - - // just save it in case it is set - if (isset($value)) { - $return[$config['cat']][$key] = $value; - } - } - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot load config category %s', $cat), $exception); - } finally { - $this->db->close($configs); - } - - return $return; + return []; } /** @@ -107,24 +83,6 @@ class Config */ public function get(string $cat, string $key) { - if (!$this->isConnected()) { - return null; - } - - try { - $config = $this->db->selectFirst(static::$table_name, ['v'], ['cat' => $cat, 'k' => $key]); - if ($this->db->isResult($config)) { - $value = ValueConversion::toConfigValue($config['v']); - - // just return it in case it is set - if (isset($value)) { - return $value; - } - } - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot get config with category %s and key %s', $cat, $key), $exception); - } - return null; } @@ -143,27 +101,7 @@ class Config */ public function set(string $cat, string $key, $value): bool { - if (!$this->isConnected()) { - return false; - } - - // We store our setting values in a string variable. - // So we have to do the conversion here so that the compare below works. - // The exception are array values. - $compare_value = (!is_array($value) ? (string)$value : $value); - $stored_value = $this->get($cat, $key); - - if (isset($stored_value) && ($stored_value === $compare_value)) { - return true; - } - - $dbValue = ValueConversion::toDbValue($value); - - try { - return $this->db->update(static::$table_name, ['v' => $dbValue], ['cat' => $cat, 'k' => $key], true); - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot set config with category %s and key %s', $cat, $key), $exception); - } + return true; } /** @@ -178,14 +116,6 @@ class Config */ public function delete(string $cat, string $key): bool { - if (!$this->isConnected()) { - return false; - } - - try { - return $this->db->delete(static::$table_name, ['cat' => $cat, 'k' => $key]); - } catch (\Exception $exception) { - throw new ConfigPersistenceException(sprintf('Cannot delete config with category %s and key %s', $cat, $key), $exception); - } + return true; } } diff --git a/src/Core/Config/Type/AbstractConfig.php b/src/Core/Config/Type/AbstractConfig.php index 3ab4f2c5e..fa98dd709 100644 --- a/src/Core/Config/Type/AbstractConfig.php +++ b/src/Core/Config/Type/AbstractConfig.php @@ -22,8 +22,10 @@ namespace Friendica\Core\Config\Type; use Friendica\Core\Config\Repository\Config; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\DI; /** * This class is responsible for all system-wide configuration values in Friendica @@ -43,14 +45,19 @@ abstract class AbstractConfig implements IManageConfigValues */ protected $configRepo; + /** @var ConfigFileManager */ + protected $configFileManager; + /** + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs * @param Cache $configCache The configuration cache (based on the config-files) * @param Config $configRepo The configuration repository */ - public function __construct(Cache $configCache, Config $configRepo) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) { - $this->configCache = $configCache; - $this->configRepo = $configRepo; + $this->configFileManager = $configFileManager; + $this->configCache = $configCache; + $this->configRepo = $configRepo; } /** @@ -60,4 +67,9 @@ abstract class AbstractConfig implements IManageConfigValues { return $this->configCache; } + + public function save() + { + $this->configFileManager->saveData($this->configCache); + } } diff --git a/src/Core/Config/Type/JitConfig.php b/src/Core/Config/Type/JitConfig.php index 68b437b24..1ae9abd2e 100644 --- a/src/Core/Config/Type/JitConfig.php +++ b/src/Core/Config/Type/JitConfig.php @@ -21,6 +21,7 @@ namespace Friendica\Core\Config\Type; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Repository\Config; @@ -39,12 +40,13 @@ class JitConfig extends AbstractConfig private $db_loaded; /** - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration model + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs + * @param Cache $configCache The configuration cache (based on the config-files) + * @param Config $configRepo The configuration model */ - public function __construct(Cache $configCache, Config $configRepo) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) { - parent::__construct($configCache, $configRepo); + parent::__construct($configFileManager, $configCache, $configRepo); $this->db_loaded = []; $this->load(); @@ -69,7 +71,7 @@ class JitConfig extends AbstractConfig } // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DB); + $this->configCache->load($config, Cache::SOURCE_DATA); } /** @@ -84,7 +86,7 @@ class JitConfig extends AbstractConfig $dbValue = $this->configRepo->get($cat, $key); if (isset($dbValue)) { - $this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DB); + $this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DATA); unset($dbValue); } @@ -100,10 +102,10 @@ class JitConfig extends AbstractConfig /** * {@inheritDoc} */ - public function set(string $cat, string $key, $value): bool + public function set(string $cat, string $key, $value, bool $autosave = true): bool { // set the cache first - $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB); + $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); // If there is no connected adapter, we're finished if (!$this->configRepo->isConnected()) { @@ -114,13 +116,17 @@ class JitConfig extends AbstractConfig $this->db_loaded[$cat][$key] = $stored; + if ($autosave) { + $this->save(); + } + return $cached && $stored; } /** * {@inheritDoc} */ - public function delete(string $cat, string $key): bool + public function delete(string $cat, string $key, bool $autosave = true): bool { $cacheRemoved = $this->configCache->delete($cat, $key); @@ -134,6 +140,10 @@ class JitConfig extends AbstractConfig $storeRemoved = $this->configRepo->delete($cat, $key); + if ($autosave) { + $this->save(); + } + return $cacheRemoved || $storeRemoved; } } diff --git a/src/Core/Config/Type/PreloadConfig.php b/src/Core/Config/Type/PreloadConfig.php index 57a0d439d..6eed20af8 100644 --- a/src/Core/Config/Type/PreloadConfig.php +++ b/src/Core/Config/Type/PreloadConfig.php @@ -21,6 +21,7 @@ namespace Friendica\Core\Config\Type; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Config\Repository\Config; @@ -36,12 +37,13 @@ class PreloadConfig extends AbstractConfig private $config_loaded; /** - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration model + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs + * @param Cache $configCache The configuration cache (based on the config-files) + * @param Config $configRepo The configuration model */ - public function __construct(Cache $configCache, Config $configRepo) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) { - parent::__construct($configCache, $configRepo); + parent::__construct($configFileManager, $configCache, $configRepo); $this->config_loaded = false; $this->load(); @@ -68,7 +70,7 @@ class PreloadConfig extends AbstractConfig $this->config_loaded = true; // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DB); + $this->configCache->load($config, Cache::SOURCE_DATA); } /** @@ -80,7 +82,7 @@ class PreloadConfig extends AbstractConfig if ($this->configRepo->isConnected()) { $config = $this->configRepo->get($cat, $key); if (isset($config)) { - $this->configCache->set($cat, $key, $config, Cache::SOURCE_DB); + $this->configCache->set($cat, $key, $config, Cache::SOURCE_DATA); } } } @@ -94,14 +96,14 @@ class PreloadConfig extends AbstractConfig /** * {@inheritDoc} */ - public function set(string $cat, string $key, $value): bool + public function set(string $cat, string $key, $value, bool $autosave = true): bool { if (!$this->config_loaded) { $this->load(); } // set the cache first - $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB); + $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); // If there is no connected adapter, we're finished if (!$this->configRepo->isConnected()) { @@ -110,13 +112,17 @@ class PreloadConfig extends AbstractConfig $stored = $this->configRepo->set($cat, $key, $value); + if ($autosave) { + $this->save(); + } + return $cached && $stored; } /** * {@inheritDoc} */ - public function delete(string $cat, string $key): bool + public function delete(string $cat, string $key, bool $autosave = true): bool { if ($this->config_loaded) { $this->load(); @@ -130,6 +136,10 @@ class PreloadConfig extends AbstractConfig $storeRemoved = $this->configRepo->delete($cat, $key); + if ($autosave) { + $this->save(); + } + return $cacheRemoved || $storeRemoved; } } diff --git a/src/Core/Config/Util/ConfigFileLoader.php b/src/Core/Config/Util/ConfigFileManager.php similarity index 83% rename from src/Core/Config/Util/ConfigFileLoader.php rename to src/Core/Config/Util/ConfigFileManager.php index acf6efa34..82de4a99c 100644 --- a/src/Core/Config/Util/ConfigFileLoader.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -26,22 +26,15 @@ use Friendica\Core\Config\Exception\ConfigFileException; use Friendica\Core\Config\ValueObject\Cache; /** - * The ConfigFileLoader loads config-files and stores them in a ConfigCache ( @see Cache ) + * The ConfigFileLoader loads and saves config-files and stores them in a ConfigCache ( @see Cache ) * * It is capable of loading the following config files: * - *.config.php (current) * - *.ini.php (deprecated) * - *.htconfig.php (deprecated) */ -class ConfigFileLoader +class ConfigFileManager { - /** - * The default name of the user defined ini file - * - * @var string - */ - const CONFIG_INI = 'local'; - /** * The default name of the user defined legacy config file * @@ -49,6 +42,13 @@ class ConfigFileLoader */ const CONFIG_HTCONFIG = 'htconfig'; + /** + * The config file, where overrides per admin page/console are saved at + * + * @var string + */ + const CONFIG_DATA_FILE = 'node.config.php'; + /** * The sample string inside the configs, which shouldn't get loaded * @@ -89,7 +89,7 @@ class ConfigFileLoader * * @param Cache $config The config cache to load to * @param array $server The $_SERVER array - * @param bool $raw Setup the raw config format + * @param bool $raw Set up the raw config format * * @throws ConfigFileException */ @@ -106,6 +106,9 @@ class ConfigFileLoader // Now load every other config you find inside the 'config/' directory $this->loadCoreConfig($config); + // Now load the node.config.php file with the node specific config values (based on admin gui/console actions) + $this->loadDataConfig($config); + $config->load($this->loadEnvConfig($server), Cache::SOURCE_ENV); // In case of install mode, add the found basepath (because there isn't a basepath set yet @@ -158,6 +161,50 @@ class ConfigFileLoader } } + /** + * Tries to load the data config file with the overridden data + * + * @param Cache $config The Config cache + * + * @throws ConfigFileException In case the config file isn't loadable + */ + private function loadDataConfig(Cache $config) + { + $filename = $this->configDir . '/' . self::CONFIG_DATA_FILE; + + if (file_exists($filename)) { + $dataArray = include $filename; + + if (!is_array($dataArray)) { + throw new ConfigFileException(sprintf('Error loading config file %s', $filename)); + } + + $config->load($dataArray, Cache::SOURCE_DATA); + } + } + + /** + * Saves overridden config entries back into the data.config.phpR + * + * @param Cache $config The config cache + * + * @throws ConfigFileException In case the config file isn't writeable or the data is invalid + */ + public function saveData(Cache $config) + { + $data = $config->getDataBySource(Cache::SOURCE_DATA); + + $encodedData = ConfigFileTransformer::encode($data); + + if (!$encodedData) { + throw new ConfigFileException('config source cannot get encoded'); + } + + if (!file_put_contents($this->configDir . '/' . self::CONFIG_DATA_FILE, $encodedData)) { + throw new ConfigFileException(sprintf('Cannot save data to file %s/%s', $this->configDir, self::CONFIG_DATA_FILE)); + } + } + /** * Tries to load the specified addon-configuration and returns the config array. * @@ -353,12 +400,16 @@ class ConfigFileLoader */ private function loadConfigFile(string $filepath): array { - $config = include($filepath); + if (file_exists($filepath)) { + $config = include($filepath); - if (!is_array($config)) { - throw new ConfigFileException('Error loading config file ' . $filepath); + if (!is_array($config)) { + throw new ConfigFileException('Error loading config file ' . $filepath); + } + + return $config; + } else { + return []; } - - return $config; } } diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index 00f8ad045..c427996c3 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -21,13 +21,13 @@ namespace Friendica\Core\Config\ValueObject; -use Friendica\Core\Config\Util\ConfigFileLoader; +use Friendica\Core\Config\Util\ConfigFileManager; use ParagonIE\HiddenString\HiddenString; /** * The Friendica config cache for the application * Initial, all *.config.php files are loaded into this cache with the - * ConfigFileLoader ( @see ConfigFileLoader ) + * ConfigFileManager ( @see ConfigFileManager ) */ class Cache { @@ -35,8 +35,8 @@ class Cache const SOURCE_STATIC = 0; /** @var int Indicates that the cache entry is set by file - Low Priority */ const SOURCE_FILE = 1; - /** @var int Indicates that the cache entry is set by the DB config table - Middle Priority */ - const SOURCE_DB = 2; + /** @var int Indicates that the cache entry is manually set by the application (per admin page/console) - Middle Priority */ + const SOURCE_DATA = 2; /** @var int Indicates that the cache entry is set by a server environment variable - High Priority */ const SOURCE_ENV = 3; /** @var int Indicates that the cache entry is fixed and must not be changed */ @@ -128,6 +128,34 @@ class Cache return $this->source[$cat][$key] ?? -1; } + /** + * Returns the whole config array based on the given source type + * + * @param int $source Indicates the source of the config entry + * + * @return array The config array part of the given source + */ + public function getDataBySource(int $source): array + { + $data = []; + + $categories = array_keys($this->source); + + foreach ($categories as $category) { + if (is_array($this->source[$category])) { + $keys = array_keys($this->source[$category]); + + foreach ($keys as $key) { + if ($this->source[$category][$key] === $source) { + $data[$category][$key] = $this->config[$category][$key]; + } + } + } + } + + return $data; + } + /** * Sets a value in the config cache. Accepts raw output from the config table * diff --git a/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php b/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php index 35be8b43e..d31f3c1ce 100644 --- a/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php +++ b/src/Core/KeyValueStorage/Type/DBKeyValueStorage.php @@ -21,7 +21,7 @@ namespace Friendica\Core\KeyValueStorage\Type; -use Friendica\Core\Config\Util\ValueConversion; +use Friendica\Core\PConfig\Util\ValueConversion; use Friendica\Core\KeyValueStorage\Exceptions\KeyValueStoragePersistenceException; use Friendica\Database\Database; diff --git a/src/Core/Update.php b/src/Core/Update.php index 450438118..8da473839 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -160,8 +160,9 @@ class Update Logger::warning('Pre update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); return $r; } else { Logger::notice('Pre update executed.', ['version' => $version]); @@ -181,8 +182,9 @@ class Update Logger::error('Update ERROR.', ['from' => $stored, 'to' => $current, 'retval' => $retval]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); return $retval; } else { Logger::notice('Database structure update finished.', ['from' => $stored, 'to' => $current]); @@ -198,8 +200,9 @@ class Update Logger::warning('Post update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); return $r; } else { DI::config()->set('system', 'build', $version); @@ -210,8 +213,9 @@ class Update DI::config()->set('system', 'build', $current); DI::config()->set('system', 'update', Update::SUCCESS); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); Logger::notice('Update success.', ['from' => $stored, 'to' => $current]); if ($sendMail) { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 645084a96..e3af408b9 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -74,7 +74,7 @@ class DBStructure $old_tables = ['fserver', 'gcign', 'gcontact', 'gcontact-relation', 'gfollower' ,'glink', 'item-delivery-data', 'item-activity', 'item-content', 'item_id', 'participation', 'poll', 'poll_result', 'queue', 'retriever_rule', 'deliverq', 'dsprphotoq', 'ffinder', 'sign', 'spam', 'term', 'user-item', 'thread', 'item', 'challenge', - 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact']; + 'auth_codes', 'tokens', 'clients', 'profile_check', 'host', 'conversation', 'fcontact', 'config']; $tables = DBA::selectToArray('INFORMATION_SCHEMA.TABLES', ['TABLE_NAME'], ['TABLE_SCHEMA' => DBA::databaseName(), 'TABLE_TYPE' => 'BASE TABLE']); @@ -176,14 +176,15 @@ class DBStructure public static function performUpdate(bool $enable_maintenance_mode = true, bool $verbose = false): string { if ($enable_maintenance_mode) { - DI::config()->set('system', 'maintenance', 1); + DI::config()->set('system', 'maintenance', true); } $status = self::update($verbose, true); if ($enable_maintenance_mode) { - DI::config()->set('system', 'maintenance', 0); - DI::config()->set('system', 'maintenance_reason', ''); + DI::config()->set('system', 'maintenance', false, false); + DI::config()->delete('system', 'maintenance_reason', false); + DI::config()->save(); } return $status; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 194d2cb2c..cf2f6c535 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -148,7 +148,7 @@ class Site extends BaseAdmin // Has the directory url changed? If yes, then resubmit the existing profiles there if ($global_directory != DI::config()->get('system', 'directory') && ($global_directory != '')) { - DI::config()->set('system', 'directory', $global_directory); + DI::config()->set('system', 'directory', $global_directory, false); Worker::add(Worker::PRIORITY_LOW, 'Directory'); } @@ -194,131 +194,133 @@ class Site extends BaseAdmin ); } } - DI::config()->set('system', 'ssl_policy' , $ssl_policy); - DI::config()->set('system', 'maxloadavg' , $maxloadavg); - DI::config()->set('system', 'min_memory' , $min_memory); - DI::config()->set('system', 'optimize_tables' , $optimize_tables); - DI::config()->set('system', 'contact_discovery' , $contact_discovery); - DI::config()->set('system', 'synchronize_directory' , $synchronize_directory); - DI::config()->set('system', 'poco_requery_days' , $poco_requery_days); - DI::config()->set('system', 'poco_discovery' , $poco_discovery); - DI::config()->set('system', 'poco_local_search' , $poco_local_search); - DI::config()->set('system', 'nodeinfo' , $nodeinfo); - DI::config()->set('config', 'sitename' , $sitename); - DI::config()->set('config', 'sender_email' , $sender_email); - DI::config()->set('system', 'suppress_tags' , $suppress_tags); - DI::config()->set('system', 'shortcut_icon' , $shortcut_icon); - DI::config()->set('system', 'touch_icon' , $touch_icon); + DI::config()->set('system', 'ssl_policy' , $ssl_policy, false); + DI::config()->set('system', 'maxloadavg' , $maxloadavg, false); + DI::config()->set('system', 'min_memory' , $min_memory, false); + DI::config()->set('system', 'optimize_tables' , $optimize_tables, false); + DI::config()->set('system', 'contact_discovery' , $contact_discovery, false); + DI::config()->set('system', 'synchronize_directory' , $synchronize_directory, false); + DI::config()->set('system', 'poco_requery_days' , $poco_requery_days, false); + DI::config()->set('system', 'poco_discovery' , $poco_discovery, false); + DI::config()->set('system', 'poco_local_search' , $poco_local_search, false); + DI::config()->set('system', 'nodeinfo' , $nodeinfo, false); + DI::config()->set('config', 'sitename' , $sitename, false); + DI::config()->set('config', 'sender_email' , $sender_email, false); + DI::config()->set('system', 'suppress_tags' , $suppress_tags, false); + DI::config()->set('system', 'shortcut_icon' , $shortcut_icon, false); + DI::config()->set('system', 'touch_icon' , $touch_icon, false); if ($banner == "") { - DI::config()->delete('system', 'banner'); + DI::config()->set('system', 'banner', false); } else { - DI::config()->set('system', 'banner', $banner); + DI::config()->set('system', 'banner', $banner, false); } if (empty($email_banner)) { - DI::config()->delete('system', 'email_banner'); + DI::config()->set('system', 'email_banner', false); } else { - DI::config()->set('system', 'email_banner', $email_banner); + DI::config()->set('system', 'email_banner', $email_banner, false); } if (empty($additional_info)) { - DI::config()->delete('config', 'info'); + DI::config()->set('config', 'info', false); } else { - DI::config()->set('config', 'info', $additional_info); + DI::config()->set('config', 'info', $additional_info, false); } - DI::config()->set('system', 'language', $language); - DI::config()->set('system', 'theme', $theme); + DI::config()->set('system', 'language', $language, false); + DI::config()->set('system', 'theme', $theme, false); Theme::install($theme); if ($theme_mobile == '---') { - DI::config()->delete('system', 'mobile-theme'); + DI::config()->set('system', 'mobile-theme', false); } else { - DI::config()->set('system', 'mobile-theme', $theme_mobile); + DI::config()->set('system', 'mobile-theme', $theme_mobile, false); } if ($singleuser == '---') { - DI::config()->delete('system', 'singleuser'); + DI::config()->set('system', 'singleuser', false); } else { - DI::config()->set('system', 'singleuser', $singleuser); + DI::config()->set('system', 'singleuser', $singleuser, false); } if (preg_match('/\d+(?:\s*[kmg])?/i', $maximagesize)) { - DI::config()->set('system', 'maximagesize', $maximagesize); + DI::config()->set('system', 'maximagesize', $maximagesize, false); } else { DI::sysmsg()->addNotice(DI::l10n()->t('%s is no valid input for maximum image size', $maximagesize)); } - DI::config()->set('system', 'max_image_length' , $maximagelength); - DI::config()->set('system', 'jpeg_quality' , $jpegimagequality); + DI::config()->set('system', 'max_image_length' , $maximagelength, false); + DI::config()->set('system', 'jpeg_quality' , $jpegimagequality, false); - DI::config()->set('config', 'register_policy' , $register_policy); - DI::config()->set('system', 'max_daily_registrations', $daily_registrations); - DI::config()->set('system', 'account_abandon_days' , $abandon_days); - DI::config()->set('config', 'register_text' , $register_text); - DI::config()->set('system', 'allowed_sites' , $allowed_sites); - DI::config()->set('system', 'allowed_email' , $allowed_email); - DI::config()->set('system', 'forbidden_nicknames' , $forbidden_nicknames); - DI::config()->set('system', 'system_actor_name' , $system_actor_name); - DI::config()->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content); - DI::config()->set('system', 'allowed_oembed' , $allowed_oembed); - DI::config()->set('system', 'block_public' , $block_public); - DI::config()->set('system', 'publish_all' , $force_publish); - DI::config()->set('system', 'newuser_private' , $newuser_private); - DI::config()->set('system', 'enotify_no_content' , $enotify_no_content); - DI::config()->set('system', 'disable_embedded' , $disable_embedded); - DI::config()->set('system', 'allow_users_remote_self', $allow_users_remote_self); - DI::config()->set('system', 'explicit_content' , $explicit_content); - DI::config()->set('system', 'proxify_content' , $proxify_content); - DI::config()->set('system', 'cache_contact_avatar' , $cache_contact_avatar); - DI::config()->set('system', 'check_new_version_url' , $check_new_version_url); + DI::config()->set('config', 'register_policy' , $register_policy, false); + DI::config()->set('system', 'max_daily_registrations', $daily_registrations, false); + DI::config()->set('system', 'account_abandon_days' , $abandon_days, false); + DI::config()->set('config', 'register_text' , $register_text, false); + DI::config()->set('system', 'allowed_sites' , $allowed_sites, false); + DI::config()->set('system', 'allowed_email' , $allowed_email, false); + DI::config()->set('system', 'forbidden_nicknames' , $forbidden_nicknames, false); + DI::config()->set('system', 'system_actor_name' , $system_actor_name, false); + DI::config()->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content, false); + DI::config()->set('system', 'allowed_oembed' , $allowed_oembed, false); + DI::config()->set('system', 'block_public' , $block_public, false); + DI::config()->set('system', 'publish_all' , $force_publish, false); + DI::config()->set('system', 'newuser_private' , $newuser_private, false); + DI::config()->set('system', 'enotify_no_content' , $enotify_no_content, false); + DI::config()->set('system', 'disable_embedded' , $disable_embedded, false); + DI::config()->set('system', 'allow_users_remote_self', $allow_users_remote_self, false); + DI::config()->set('system', 'explicit_content' , $explicit_content, false); + DI::config()->set('system', 'proxify_content' , $proxify_content, false); + DI::config()->set('system', 'cache_contact_avatar' , $cache_contact_avatar, false); + DI::config()->set('system', 'check_new_version_url' , $check_new_version_url, false); - DI::config()->set('system', 'block_extended_register', !$enable_multi_reg); - DI::config()->set('system', 'no_openid' , !$enable_openid); - DI::config()->set('system', 'no_regfullname' , !$enable_regfullname); - DI::config()->set('system', 'register_notification' , $register_notification); - DI::config()->set('system', 'community_page_style' , $community_page_style); - DI::config()->set('system', 'max_author_posts_community_page', $max_author_posts_community_page); - DI::config()->set('system', 'verifyssl' , $verifyssl); - DI::config()->set('system', 'proxyuser' , $proxyuser); - DI::config()->set('system', 'proxy' , $proxy); - DI::config()->set('system', 'curl_timeout' , $timeout); - DI::config()->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open')); - DI::config()->set('system', 'ostatus_disabled' , !$ostatus_enabled); - DI::config()->set('system', 'diaspora_enabled' , $diaspora_enabled); + DI::config()->set('system', 'block_extended_register', !$enable_multi_reg, false); + DI::config()->set('system', 'no_openid' , !$enable_openid, false); + DI::config()->set('system', 'no_regfullname' , !$enable_regfullname, false); + DI::config()->set('system', 'register_notification' , $register_notification, false); + DI::config()->set('system', 'community_page_style' , $community_page_style, false); + DI::config()->set('system', 'max_author_posts_community_page', $max_author_posts_community_page, false); + DI::config()->set('system', 'verifyssl' , $verifyssl, false); + DI::config()->set('system', 'proxyuser' , $proxyuser, false); + DI::config()->set('system', 'proxy' , $proxy, false); + DI::config()->set('system', 'curl_timeout' , $timeout, false); + DI::config()->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open'), false); + DI::config()->set('system', 'ostatus_disabled' , !$ostatus_enabled, false); + DI::config()->set('system', 'diaspora_enabled' , $diaspora_enabled, false); - DI::config()->set('config', 'private_addons' , $private_addons); + DI::config()->set('config', 'private_addons' , $private_addons, false); - DI::config()->set('system', 'force_ssl' , $force_ssl); - DI::config()->set('system', 'hide_help' , !$show_help); + DI::config()->set('system', 'force_ssl' , $force_ssl, false); + DI::config()->set('system', 'hide_help' , !$show_help, false); - DI::config()->set('system', 'dbclean' , $dbclean); - DI::config()->set('system', 'dbclean-expire-days' , $dbclean_expire_days); - DI::config()->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv); + DI::config()->set('system', 'dbclean' , $dbclean, false); + DI::config()->set('system', 'dbclean-expire-days' , $dbclean_expire_days, false); + DI::config()->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv, false); if ($dbclean_unclaimed == 0) { $dbclean_unclaimed = $dbclean_expire_days; } - DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed); + DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed, false); - DI::config()->set('system', 'max_comments', $max_comments); - DI::config()->set('system', 'max_display_comments', $max_display_comments); + DI::config()->set('system', 'max_comments', $max_comments, false); + DI::config()->set('system', 'max_display_comments', $max_display_comments, false); if ($temppath != '') { $temppath = BasePath::getRealPath($temppath); } - DI::config()->set('system', 'temppath', $temppath); + DI::config()->set('system', 'temppath', $temppath, false); - DI::config()->set('system', 'only_tag_search' , $only_tag_search); - DI::config()->set('system', 'compute_group_counts', $compute_group_counts); + DI::config()->set('system', 'only_tag_search' , $only_tag_search, false); + DI::config()->set('system', 'compute_group_counts', $compute_group_counts, false); - DI::config()->set('system', 'worker_queues' , $worker_queues); - DI::config()->set('system', 'worker_fastlane' , $worker_fastlane); + DI::config()->set('system', 'worker_queues' , $worker_queues, false); + DI::config()->set('system', 'worker_fastlane' , $worker_fastlane, false); - DI::config()->set('system', 'relay_directly' , $relay_directly); - DI::config()->set('system', 'relay_scope' , $relay_scope); - DI::config()->set('system', 'relay_server_tags', $relay_server_tags); - DI::config()->set('system', 'relay_deny_tags' , $relay_deny_tags); - DI::config()->set('system', 'relay_user_tags' , $relay_user_tags); + DI::config()->set('system', 'relay_directly' , $relay_directly, false); + DI::config()->set('system', 'relay_scope' , $relay_scope, false); + DI::config()->set('system', 'relay_server_tags', $relay_server_tags, false); + DI::config()->set('system', 'relay_deny_tags' , $relay_deny_tags, false); + DI::config()->set('system', 'relay_user_tags' , $relay_user_tags, false); + + DI::config()->save(); DI::baseUrl()->redirect('admin/site' . $active_panel); } @@ -332,8 +334,8 @@ class Site extends BaseAdmin if (DI::config()->get('system', 'directory_submit_url') && !DI::config()->get('system', 'directory')) { - DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url'))); - DI::config()->delete('system', 'directory_submit_url'); + DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url')), false); + DI::config()->delete('system', 'directory_submit_url', false); } /* Installed themes */ diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 48e25961c..018bbf2c3 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1507); + define('DB_UPDATE_VERSION', 1508); } return [ @@ -553,19 +553,6 @@ return [ "k_expires" => ["k", "expires"], ] ], - "config" => [ - "comment" => "main configuration storage", - "fields" => [ - "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => ""], - "cat" => ["type" => "varbinary(50)", "not null" => "1", "default" => "", "comment" => ""], - "k" => ["type" => "varbinary(50)", "not null" => "1", "default" => "", "comment" => ""], - "v" => ["type" => "mediumtext", "comment" => ""], - ], - "indexes" => [ - "PRIMARY" => ["id"], - "cat_k" => ["UNIQUE", "cat", "k"], - ] - ], "contact-relation" => [ "comment" => "Contact relations", "fields" => [ diff --git a/static/dependencies.config.php b/static/dependencies.config.php index c59d0478a..08867d86d 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -76,7 +76,7 @@ return [ $_SERVER ] ], - Config\Util\ConfigFileLoader::class => [ + Config\Util\ConfigFileManager::class => [ 'instanceOf' => Config\Factory\Config::class, 'call' => [ ['createConfigFileLoader', [ diff --git a/update.php b/update.php index 356877ff9..fda04b6ce 100644 --- a/update.php +++ b/update.php @@ -1175,3 +1175,22 @@ function update_1505() return DBA::delete('config', $conditions) ? Update::SUCCESS : Update::FAILED; } + +function update_1508() +{ + $categories = DBA::toArray(DBA::p("SELECT DISTINCT `cat` AS 'cat' FROM `config`")); + + foreach ($categories as $category) { + DI::config()->load($category['cat']); + } + + $config = DBA::selectToArray('config'); + + foreach ($config as $entry) { + DI::config()->set($entry['cat'], $entry['k'], $entry['v'], false); + } + + DI::config()->save(); + + DBA::e("DELETE FROM `config`"); +} From b871e1d264aced03c2c986fe93bff33f3575ee7f Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 00:18:29 +0100 Subject: [PATCH 03/19] Introduce lightweight Config model --- src/Core/Config/Model/Config.php | 98 ++++++++++++++++++++++++++++++++ static/dependencies.config.php | 5 +- 2 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 src/Core/Config/Model/Config.php diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php new file mode 100644 index 000000000..3af2f7e4b --- /dev/null +++ b/src/Core/Config/Model/Config.php @@ -0,0 +1,98 @@ +. + * + */ + +namespace Friendica\Core\Config\Model; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\ValueObject\Cache; + +/** + * Configuration model, which manages the whole system configuration + */ +class Config implements IManageConfigValues +{ + /** + * @var Cache + */ + protected $configCache; + + /** @var ConfigFileManager */ + protected $configFileManager; + + /** + * @param ConfigFileManager $configFileManager The configuration file manager to save back configs + * @param Cache $configCache The configuration cache (based on the config-files) + */ + public function __construct(ConfigFileManager $configFileManager, Cache $configCache) + { + $this->configFileManager = $configFileManager; + $this->configCache = $configCache; + } + + /** + * {@inheritDoc} + */ + public function getCache(): Cache + { + return $this->configCache; + } + + public function save() + { + $this->configFileManager->saveData($this->configCache); + } + + public function load(string $cat = 'config') + { + $configCache = new Cache(); + + $this->configFileManager->setupCache($configCache, $_SERVER); + $this->configCache = $configCache; + } + + public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + { + return $this->configCache->get($cat, $key) ?? $default_value; + } + + public function set(string $cat, string $key, $value, bool $autosave = true): bool + { + $stored = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); + + if ($stored && $autosave) { + $this->save(); + } + + return $stored; + } + + public function delete(string $cat, string $key, bool $autosave = true): bool + { + $removed = $this->configCache->delete($cat, $key); + + if ($removed && $autosave) { + $this->save(); + } + + return $removed; + } +} diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 08867d86d..90f01feed 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -98,10 +98,7 @@ return [ ], ], Config\Capability\IManageConfigValues::class => [ - 'instanceOf' => Config\Factory\Config::class, - 'call' => [ - ['create', [], Dice::CHAIN_CALL], - ], + 'instanceOf' => Config\Model\Config::class, ], PConfig\Capability\IManagePersonalConfigValues::class => [ 'instanceOf' => PConfig\Factory\PConfig::class, From d272e8c3c78b88c45a0eb860f8491ccd6f3d7a62 Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 00:20:20 +0100 Subject: [PATCH 04/19] Remove unnecessary classes --- src/Core/Config/Repository/Config.php | 121 -------------- src/Core/Config/Type/AbstractConfig.php | 75 --------- src/Core/Config/Type/JitConfig.php | 149 ------------------ src/Core/Config/Type/PreloadConfig.php | 145 ----------------- src/Core/PConfig/Repository/PConfig.php | 2 +- .../Util/ValueConversion.php | 2 +- 6 files changed, 2 insertions(+), 492 deletions(-) delete mode 100644 src/Core/Config/Repository/Config.php delete mode 100644 src/Core/Config/Type/AbstractConfig.php delete mode 100644 src/Core/Config/Type/JitConfig.php delete mode 100644 src/Core/Config/Type/PreloadConfig.php rename src/Core/{Config => PConfig}/Util/ValueConversion.php (98%) diff --git a/src/Core/Config/Repository/Config.php b/src/Core/Config/Repository/Config.php deleted file mode 100644 index eabff9e68..000000000 --- a/src/Core/Config/Repository/Config.php +++ /dev/null @@ -1,121 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Repository; - -use Friendica\App\Mode; -use Friendica\Core\Config\Exception\ConfigPersistenceException; -use Friendica\Core\Config\Util\ValueConversion; -use Friendica\Database\Database; - -/** - * The Config Repository, which is using the general DB-model backend for configs - */ -class Config -{ - /** @var Database */ - protected $db; - /** @var Mode */ - protected $mode; - - public function __construct(Database $db, Mode $mode) - { - $this->db = $db; - $this->mode = $mode; - } - - protected static $table_name = 'config'; - - /** - * Checks if the model is currently connected - * - * @return bool - */ - public function isConnected(): bool - { - return true; - } - - /** - * Loads all configuration values and returns the loaded category as an array. - * - * @param string|null $cat The category of the configuration values to load - * - * @return array The config array - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function load(?string $cat = null): array - { - return []; - } - - /** - * Get a particular, system-wide config variable out of the DB with the - * given category name ($cat) and a key ($key). - * - * Note: Boolean variables are defined as 0/1 in the database - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * - * @return array|string|null Stored value or null if it does not exist - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function get(string $cat, string $key) - { - return null; - } - - /** - * Stores a config value ($value) in the category ($cat) under the key ($key). - * - * Note: Please do not store booleans - convert to 0/1 integer values! - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to set - * @param mixed $value The value to store - * - * @return bool Operation success - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function set(string $cat, string $key, $value): bool - { - return true; - } - - /** - * Removes the configured value from the database. - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to delete - * - * @return bool Operation success - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - */ - public function delete(string $cat, string $key): bool - { - return true; - } -} diff --git a/src/Core/Config/Type/AbstractConfig.php b/src/Core/Config/Type/AbstractConfig.php deleted file mode 100644 index fa98dd709..000000000 --- a/src/Core/Config/Type/AbstractConfig.php +++ /dev/null @@ -1,75 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Type; - -use Friendica\Core\Config\Repository\Config; -use Friendica\Core\Config\Util\ConfigFileManager; -use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\DI; - -/** - * This class is responsible for all system-wide configuration values in Friendica - * There are two types of storage - * - The Config-Files (loaded into the FileCache @see Cache) - * - The Config-Repository (per Config-Repository @see Config ) - */ -abstract class AbstractConfig implements IManageConfigValues -{ - /** - * @var Cache - */ - protected $configCache; - - /** - * @var Config - */ - protected $configRepo; - - /** @var ConfigFileManager */ - protected $configFileManager; - - /** - * @param ConfigFileManager $configFileManager The configuration file manager to save back configs - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration repository - */ - public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) - { - $this->configFileManager = $configFileManager; - $this->configCache = $configCache; - $this->configRepo = $configRepo; - } - - /** - * {@inheritDoc} - */ - public function getCache(): Cache - { - return $this->configCache; - } - - public function save() - { - $this->configFileManager->saveData($this->configCache); - } -} diff --git a/src/Core/Config/Type/JitConfig.php b/src/Core/Config/Type/JitConfig.php deleted file mode 100644 index 1ae9abd2e..000000000 --- a/src/Core/Config/Type/JitConfig.php +++ /dev/null @@ -1,149 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Type; - -use Friendica\Core\Config\Util\ConfigFileManager; -use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Repository\Config; - -/** - * This class implements the Just-In-Time configuration, which will cache - * config values in a cache, once they are retrieved. - * - * Default Configuration type. - * Provides the best performance for pages loading few configuration variables. - */ -class JitConfig extends AbstractConfig -{ - /** - * @var array Array of already loaded db values (even if there was no value) - */ - private $db_loaded; - - /** - * @param ConfigFileManager $configFileManager The configuration file manager to save back configs - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration model - */ - public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) - { - parent::__construct($configFileManager, $configCache, $configRepo); - $this->db_loaded = []; - - $this->load(); - } - - /** - * {@inheritDoc} - */ - public function load(string $cat = 'config') - { - // If not connected, do nothing - if (!$this->configRepo->isConnected()) { - return; - } - - $config = $this->configRepo->load($cat); - - if (!empty($config[$cat])) { - foreach ($config[$cat] as $key => $value) { - $this->db_loaded[$cat][$key] = true; - } - } - - // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DATA); - } - - /** - * {@inheritDoc} - */ - public function get(string $cat, string $key, $default_value = null, bool $refresh = false) - { - // if the value isn't loaded or refresh is needed, load it to the cache - if ($this->configRepo->isConnected() && - (empty($this->db_loaded[$cat][$key]) || - $refresh)) { - $dbValue = $this->configRepo->get($cat, $key); - - if (isset($dbValue)) { - $this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DATA); - unset($dbValue); - } - - $this->db_loaded[$cat][$key] = true; - } - - // use the config cache for return - $result = $this->configCache->get($cat, $key); - - return (isset($result)) ? $result : $default_value; - } - - /** - * {@inheritDoc} - */ - public function set(string $cat, string $key, $value, bool $autosave = true): bool - { - // set the cache first - $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); - - // If there is no connected adapter, we're finished - if (!$this->configRepo->isConnected()) { - return $cached; - } - - $stored = $this->configRepo->set($cat, $key, $value); - - $this->db_loaded[$cat][$key] = $stored; - - if ($autosave) { - $this->save(); - } - - return $cached && $stored; - } - - /** - * {@inheritDoc} - */ - public function delete(string $cat, string $key, bool $autosave = true): bool - { - $cacheRemoved = $this->configCache->delete($cat, $key); - - if (isset($this->db_loaded[$cat][$key])) { - unset($this->db_loaded[$cat][$key]); - } - - if (!$this->configRepo->isConnected()) { - return $cacheRemoved; - } - - $storeRemoved = $this->configRepo->delete($cat, $key); - - if ($autosave) { - $this->save(); - } - - return $cacheRemoved || $storeRemoved; - } -} diff --git a/src/Core/Config/Type/PreloadConfig.php b/src/Core/Config/Type/PreloadConfig.php deleted file mode 100644 index 6eed20af8..000000000 --- a/src/Core/Config/Type/PreloadConfig.php +++ /dev/null @@ -1,145 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Config\Type; - -use Friendica\Core\Config\Util\ConfigFileManager; -use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Repository\Config; - -/** - * This class implements the preload configuration, which will cache - * all config values per call in a cache. - * - * Minimizes the number of database queries to retrieve configuration values at the cost of memory. - */ -class PreloadConfig extends AbstractConfig -{ - /** @var bool */ - private $config_loaded; - - /** - * @param ConfigFileManager $configFileManager The configuration file manager to save back configs - * @param Cache $configCache The configuration cache (based on the config-files) - * @param Config $configRepo The configuration model - */ - public function __construct(ConfigFileManager $configFileManager, Cache $configCache, Config $configRepo) - { - parent::__construct($configFileManager, $configCache, $configRepo); - $this->config_loaded = false; - - $this->load(); - } - - /** - * {@inheritDoc} - * - * This loads all config values everytime load is called - */ - public function load(string $cat = 'config') - { - // Don't load the whole configuration twice - if ($this->config_loaded) { - return; - } - - // If not connected, do nothing - if (!$this->configRepo->isConnected()) { - return; - } - - $config = $this->configRepo->load(); - $this->config_loaded = true; - - // load the whole category out of the DB into the cache - $this->configCache->load($config, Cache::SOURCE_DATA); - } - - /** - * {@inheritDoc} - */ - public function get(string $cat, string $key, $default_value = null, bool $refresh = false) - { - if ($refresh) { - if ($this->configRepo->isConnected()) { - $config = $this->configRepo->get($cat, $key); - if (isset($config)) { - $this->configCache->set($cat, $key, $config, Cache::SOURCE_DATA); - } - } - } - - // use the config cache for return - $result = $this->configCache->get($cat, $key); - - return (isset($result)) ? $result : $default_value; - } - - /** - * {@inheritDoc} - */ - public function set(string $cat, string $key, $value, bool $autosave = true): bool - { - if (!$this->config_loaded) { - $this->load(); - } - - // set the cache first - $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); - - // If there is no connected adapter, we're finished - if (!$this->configRepo->isConnected()) { - return $cached; - } - - $stored = $this->configRepo->set($cat, $key, $value); - - if ($autosave) { - $this->save(); - } - - return $cached && $stored; - } - - /** - * {@inheritDoc} - */ - public function delete(string $cat, string $key, bool $autosave = true): bool - { - if ($this->config_loaded) { - $this->load(); - } - - $cacheRemoved = $this->configCache->delete($cat, $key); - - if (!$this->configRepo->isConnected()) { - return $cacheRemoved; - } - - $storeRemoved = $this->configRepo->delete($cat, $key); - - if ($autosave) { - $this->save(); - } - - return $cacheRemoved || $storeRemoved; - } -} diff --git a/src/Core/PConfig/Repository/PConfig.php b/src/Core/PConfig/Repository/PConfig.php index 5c9d2d51d..506aaeb5a 100644 --- a/src/Core/PConfig/Repository/PConfig.php +++ b/src/Core/PConfig/Repository/PConfig.php @@ -22,7 +22,7 @@ namespace Friendica\Core\PConfig\Repository; use Friendica\App\Mode; -use Friendica\Core\Config\Util\ValueConversion; +use Friendica\Core\PConfig\Util\ValueConversion; use Friendica\Core\PConfig\Exception\PConfigPersistenceException; use Friendica\Database\Database; diff --git a/src/Core/Config/Util/ValueConversion.php b/src/Core/PConfig/Util/ValueConversion.php similarity index 98% rename from src/Core/Config/Util/ValueConversion.php rename to src/Core/PConfig/Util/ValueConversion.php index a3d9d0146..64a4bfc51 100644 --- a/src/Core/Config/Util/ValueConversion.php +++ b/src/Core/PConfig/Util/ValueConversion.php @@ -19,7 +19,7 @@ * */ -namespace Friendica\Core\Config\Util; +namespace Friendica\Core\PConfig\Util; /** * Util class to help to convert from/to (p)config values From 326566638fcc90ef20a87047ea55fb846c7d46e3 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 21:10:41 +0100 Subject: [PATCH 05/19] adapt tests --- tests/FixtureTest.php | 13 +- tests/Util/VFSTrait.php | 15 ++ tests/datasets/api.fixture.php | 27 -- tests/datasets/config/node.config.php | 13 + tests/functional/DependencyCheckTest.php | 10 +- tests/src/Core/Config/Cache/CacheTest.php | 70 +++-- .../Config/Cache/ConfigFileLoaderTest.php | 61 ++++- tests/src/Core/Config/ConfigTest.php | 178 ++++-------- tests/src/Core/Config/JitConfigTest.php | 255 ------------------ tests/src/Core/Config/PreloadConfigTest.php | 213 --------------- tests/src/Core/Lock/SemaphoreLockTest.php | 10 +- .../Storage/Repository/StorageManagerTest.php | 6 +- tests/src/Database/DBATest.php | 5 +- tests/src/Database/DBStructureTest.php | 28 +- .../Api/GnuSocial/GnuSocial/ConfigTest.php | 1 + tests/src/Util/BaseURLTest.php | 61 +++-- 16 files changed, 254 insertions(+), 712 deletions(-) create mode 100644 tests/datasets/config/node.config.php delete mode 100644 tests/src/Core/Config/JitConfigTest.php delete mode 100644 tests/src/Core/Config/PreloadConfigTest.php diff --git a/tests/FixtureTest.php b/tests/FixtureTest.php index a57d3aa76..efb928031 100644 --- a/tests/FixtureTest.php +++ b/tests/FixtureTest.php @@ -25,20 +25,24 @@ namespace Friendica\Test; use Dice\Dice; use Friendica\App\Arguments; use Friendica\App\Router; +use Friendica\Core\Config\Factory\Config; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; -use Friendica\Core\Config\Capability\IManageConfigValues; use Friendica\Core\Session\Capability\IHandleSessions; use Friendica\Core\Session\Type\Memory; use Friendica\Database\Database; use Friendica\Database\DBStructure; use Friendica\DI; use Friendica\Test\Util\Database\StaticDatabase; +use Friendica\Test\Util\VFSTrait; /** * Parent class for test cases requiring fixtures */ abstract class FixtureTest extends DatabaseTest { + use VFSTrait; + /** @var Dice */ protected $dice; @@ -47,6 +51,8 @@ abstract class FixtureTest extends DatabaseTest */ protected function setUp(): void { + $this->setUpVfsDir(); + parent::setUp(); $server = $_SERVER; @@ -54,6 +60,10 @@ abstract class FixtureTest extends DatabaseTest $this->dice = (new Dice()) ->addRules(include __DIR__ . '/../static/dependencies.config.php') + ->addRule(ConfigFileManager::class, [ + 'instanceOf' => Config::class, + 'call' => [['createConfigFileLoader', [$this->root->url(), $server,], + Dice::CHAIN_CALL]]]) ->addRule(Database::class, ['instanceOf' => StaticDatabase::class, 'shared' => true]) ->addRule(IHandleSessions::class, ['instanceOf' => Memory::class, 'shared' => true, 'call' => null]) ->addRule(Arguments::class, [ @@ -64,7 +74,6 @@ abstract class FixtureTest extends DatabaseTest ]); DI::init($this->dice); - /** @var IManageConfigValues $config */ $configCache = $this->dice->create(Cache::class); $configCache->set('database', 'disable_pdo', true); diff --git a/tests/Util/VFSTrait.php b/tests/Util/VFSTrait.php index 30c27451e..e36cc1234 100644 --- a/tests/Util/VFSTrait.php +++ b/tests/Util/VFSTrait.php @@ -54,6 +54,21 @@ trait VFSTrait $this->setConfigFile('defaults.config.php', true); $this->setConfigFile('settings.config.php', true); $this->setConfigFile('local.config.php'); + $this->setDataFile('node.config.php'); + } + + protected function setDataFile(string $filename) + { + $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR . + $filename; + + if (file_exists($file)) { + vfsStream::newFile($filename) + ->at($this->root->getChild('config')) + ->setContent(file_get_contents($file)); + } } /** diff --git a/tests/datasets/api.fixture.php b/tests/datasets/api.fixture.php index a8119106e..b50f70625 100644 --- a/tests/datasets/api.fixture.php +++ b/tests/datasets/api.fixture.php @@ -36,33 +36,6 @@ return [ 'mail', 'post-delivery-data', // Base test config to avoid notice messages - 'config' => [ - [ - 'cat' => 'system', - 'k' => 'url', - 'v' => 'http://localhost', - ], - [ - 'cat' => 'config', - 'k' => 'hostname', - 'v' => 'localhost', - ], - [ - 'cat' => 'system', - 'k' => 'worker_dont_fork', - 'v' => '1', - ], - [ - 'cat' => 'system', - 'k' => 'curl_timeout', - 'v' => '1', - ], - [ - 'cat' => 'system', - 'k' => 'xrd_timeout', - 'v' => '1', - ], - ], 'user' => [ [ 'uid' => 42, diff --git a/tests/datasets/config/node.config.php b/tests/datasets/config/node.config.php new file mode 100644 index 000000000..335ef19c9 --- /dev/null +++ b/tests/datasets/config/node.config.php @@ -0,0 +1,13 @@ + [ + 'hostname' => 'localhost', + ], + 'system' => [ + 'url' => 'http://localhost', + "worker_dont_fork" => 1, + "curl_timeout"=> 1, + "xrd_timeout"=> 1, + ], +]; diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php index 3b21f7584..9bc7624d6 100644 --- a/tests/functional/DependencyCheckTest.php +++ b/tests/functional/DependencyCheckTest.php @@ -31,7 +31,7 @@ use Friendica\Core\Lock\Capability\ICanLock; use Friendica\Database\Database; use Friendica\Test\Util\VFSTrait; use Friendica\Util\BasePath; -use Friendica\Core\Config\Util\ConfigFileLoader; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Util\Profiler; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -73,13 +73,13 @@ class DependencyCheckTest extends TestCase */ public function testConfigFileLoader() { - /** @var ConfigFileLoader $configFileLoader */ - $configFileLoader = $this->dice->create(ConfigFileLoader::class); + /** @var ConfigFileManager $configFileManager */ + $configFileManager = $this->dice->create(ConfigFileManager::class); - self::assertInstanceOf(ConfigFileLoader::class, $configFileLoader); + self::assertInstanceOf(ConfigFileManager::class, $configFileManager); $configCache = new Cache(); - $configFileLoader->setupCache($configCache); + $configFileManager->setupCache($configCache); self::assertNotEmpty($configCache->getAll()); self::assertArrayHasKey('database', $configCache->getAll()); diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 6de87be5d..8b2c24da3 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -21,7 +21,7 @@ namespace Friendica\Test\src\Core\Config\Cache; -use Friendica\Core\Config\Cache; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use ParagonIE\HiddenString\HiddenString; use stdClass; @@ -49,7 +49,7 @@ class CacheTest extends MockedTest ]; } - private function assertConfigValues($data, \Friendica\Core\Config\ValueObject\Cache $configCache) + private function assertConfigValues($data, Cache $configCache) { foreach ($data as $cat => $values) { foreach ($values as $key => $value) { @@ -64,7 +64,7 @@ class CacheTest extends MockedTest */ public function testLoadConfigArray($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configCache->load($data); self::assertConfigValues($data, $configCache); @@ -83,27 +83,27 @@ class CacheTest extends MockedTest ] ]; - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB); + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_DATA); // doesn't override - Low Priority due Config file - $configCache->load($override, \Friendica\Core\Config\ValueObject\Cache::SOURCE_FILE); + $configCache->load($override, Cache::SOURCE_FILE); self::assertConfigValues($data, $configCache); // override the value - High Prio due Server Env - $configCache->load($override, \Friendica\Core\Config\ValueObject\Cache::SOURCE_ENV); + $configCache->load($override, Cache::SOURCE_ENV); self::assertEquals($override['system']['test'], $configCache->get('system', 'test')); self::assertEquals($override['system']['boolTrue'], $configCache->get('system', 'boolTrue')); // Don't overwrite server ENV variables - even in load mode - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB); + $configCache->load($data, Cache::SOURCE_DATA); self::assertEquals($override['system']['test'], $configCache->get('system', 'test')); self::assertEquals($override['system']['boolTrue'], $configCache->get('system', 'boolTrue')); // Overwrite ENV variables with ENV variables - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_ENV); + $configCache->load($data, Cache::SOURCE_ENV); self::assertConfigValues($data, $configCache); self::assertNotEquals($override['system']['test'], $configCache->get('system', 'test')); @@ -115,7 +115,7 @@ class CacheTest extends MockedTest */ public function testLoadConfigArrayWrong() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); // empty dataset $configCache->load([]); @@ -136,7 +136,7 @@ class CacheTest extends MockedTest */ public function testGetAll($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configCache->load($data); $all = $configCache->getAll(); @@ -151,7 +151,7 @@ class CacheTest extends MockedTest */ public function testSetGet($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); foreach ($data as $cat => $values) { foreach ($values as $key => $value) { @@ -167,7 +167,7 @@ class CacheTest extends MockedTest */ public function testGetEmpty() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); self::assertNull($configCache->get('something', 'value')); } @@ -177,7 +177,7 @@ class CacheTest extends MockedTest */ public function testGetCat() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'system' => [ 'key1' => 'value1', 'key2' => 'value2', @@ -205,7 +205,7 @@ class CacheTest extends MockedTest */ public function testDelete($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache($data); + $configCache = new Cache($data); foreach ($data as $cat => $values) { foreach ($values as $key => $value) { @@ -222,7 +222,7 @@ class CacheTest extends MockedTest */ public function testKeyDiffWithResult($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache($data); + $configCache = new Cache($data); $diffConfig = [ 'fakeCat' => [ @@ -239,7 +239,7 @@ class CacheTest extends MockedTest */ public function testKeyDiffWithoutResult($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache($data); + $configCache = new Cache($data); $diffConfig = $configCache->getAll(); @@ -251,7 +251,7 @@ class CacheTest extends MockedTest */ public function testPasswordHide() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => 'supersecure', 'username' => 'notsecured', @@ -268,7 +268,7 @@ class CacheTest extends MockedTest */ public function testPasswordShow() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => 'supersecure', 'username' => 'notsecured', @@ -285,7 +285,7 @@ class CacheTest extends MockedTest */ public function testEmptyPassword() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => '', 'username' => '', @@ -299,7 +299,7 @@ class CacheTest extends MockedTest public function testWrongTypePassword() { - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => new stdClass(), 'username' => '', @@ -309,7 +309,7 @@ class CacheTest extends MockedTest self::assertNotEmpty($configCache->get('database', 'password')); self::assertEmpty($configCache->get('database', 'username')); - $configCache = new \Friendica\Core\Config\ValueObject\Cache([ + $configCache = new Cache([ 'database' => [ 'password' => 23, 'username' => '', @@ -327,19 +327,35 @@ class CacheTest extends MockedTest public function testSetOverrides($data) { - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); - $configCache->load($data, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB); + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_DATA); // test with wrong override - self::assertFalse($configCache->set('system', 'test', '1234567', \Friendica\Core\Config\ValueObject\Cache::SOURCE_FILE)); + self::assertFalse($configCache->set('system', 'test', '1234567', Cache::SOURCE_FILE)); self::assertEquals($data['system']['test'], $configCache->get('system', 'test')); // test with override (equal) - self::assertTrue($configCache->set('system', 'test', '8910', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DB)); + self::assertTrue($configCache->set('system', 'test', '8910', Cache::SOURCE_DATA)); self::assertEquals('8910', $configCache->get('system', 'test')); // test with override (over) - self::assertTrue($configCache->set('system', 'test', '111213', \Friendica\Core\Config\ValueObject\Cache::SOURCE_ENV)); + self::assertTrue($configCache->set('system', 'test', '111213', Cache::SOURCE_ENV)); self::assertEquals('111213', $configCache->get('system', 'test')); } + + /** + * @dataProvider dataTests + * + * @return void + */ + public function testSetData($data) + { + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_FILE); + + $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); + + $this->assertEquals(['system' => ['test_2' => 'with_data']], $configCache->getDataBySource(Cache::SOURCE_DATA)); + $this->assertEquals($data, $configCache->getDataBySource(Cache::SOURCE_FILE)); + } } diff --git a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php index be1c7def6..e8443611f 100644 --- a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php @@ -25,7 +25,7 @@ use Friendica\Core\Config\Cache; use Friendica\Core\Config\Factory\Config; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; -use Friendica\Core\Config\Util\ConfigFileLoader; +use Friendica\Core\Config\Util\ConfigFileManager; use org\bovigo\vfs\vfsStream; class ConfigFileLoaderTest extends MockedTest @@ -46,7 +46,7 @@ class ConfigFileLoaderTest extends MockedTest { $this->delConfigFile('local.config.php'); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -72,7 +72,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent('root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -101,7 +101,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -138,7 +138,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -174,7 +174,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -228,7 +228,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('addon')->getChild('test')->getChild('config')) ->setContent(file_get_contents($file)); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -265,7 +265,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($fileDir . 'B.config.php')); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -299,7 +299,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($fileDir . 'B.ini.php')); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -333,7 +333,7 @@ class ConfigFileLoaderTest extends MockedTest ->at($this->root->getChild('config')) ->setContent(file_get_contents($fileDir . 'B.ini.php')); - $configFileLoader = new ConfigFileLoader( + $configFileLoader = new ConfigFileManager( $this->root->url(), $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR @@ -386,4 +386,45 @@ class ConfigFileLoaderTest extends MockedTest self::assertEquals('newValue', $configCache->get('system', 'newKey')); } + + public function testSaveData() + { + $this->delConfigFile('local.config.php'); + + $fileDir = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . + 'datasets' . DIRECTORY_SEPARATOR . + 'config' . DIRECTORY_SEPARATOR; + + vfsStream::newFile('B.config.php') + ->at($this->root->getChild('config2')) + ->setContent(file_get_contents($fileDir . 'B.config.php')); + + $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); + $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + + $configFileLoader->setupCache($configCache); + + // overwrite some data and save it back to the config file + $configCache->set('system', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configCache->set('config', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configCache->set('system', 'test_2', 2, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configFileLoader->saveData($configCache); + + // Reload the configCache with the new values + $configCache2 = new \Friendica\Core\Config\ValueObject\Cache(); + $configFileLoader->setupCache($configCache2); + + self::assertEquals($configCache, $configCache2); + self::assertEquals([ + 'system' => [ + 'test' => 'it', + 'test_2' => 2 + ], + 'config' => [ + 'test' => 'it' + ]], $configCache2->getDataBySource(\Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA)); + } } diff --git a/tests/src/Core/Config/ConfigTest.php b/tests/src/Core/Config/ConfigTest.php index bdcba724e..c85cf3f70 100644 --- a/tests/src/Core/Config/ConfigTest.php +++ b/tests/src/Core/Config/ConfigTest.php @@ -23,22 +23,25 @@ namespace Friendica\Test\src\Core\Config; use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Repository\Config as ConfigModel; +use Friendica\Core\Config\Model\Config; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\Util\ConfigFileTransformer; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; -use Mockery\MockInterface; -use Mockery; +use Friendica\Test\Util\VFSTrait; +use org\bovigo\vfs\vfsStream; -abstract class ConfigTest extends MockedTest +class ConfigTest extends MockedTest { use ArraySubsetAsserts; - - /** @var ConfigModel|MockInterface */ - protected $configModel; + use VFSTrait; /** @var Cache */ protected $configCache; + /** @var ConfigFileManager */ + protected $configFileManager; + /** @var IManageConfigValues */ protected $testedConfig; @@ -60,17 +63,22 @@ abstract class ConfigTest extends MockedTest protected function setUp(): void { + $this->setUpVfsDir(); + parent::setUp(); - // Create the config model - $this->configModel = Mockery::mock(ConfigModel::class); $this->configCache = new Cache(); + $this->configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/'); } /** * @return IManageConfigValues */ - abstract public function getInstance(); + public function getInstance() + { + $this->configFileManager->setupCache($this->configCache, []); + return new Config($this->configFileManager, $this->configCache); + } public function dataTests() { @@ -156,12 +164,13 @@ abstract class ConfigTest extends MockedTest /** * Test the configuration initialization + * @dataProvider dataConfigLoad */ public function testSetUp(array $data) { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->once(); + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data)); $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); @@ -171,19 +180,23 @@ abstract class ConfigTest extends MockedTest } /** - * Test the configuration load() method + * Test the configuration reload() method * * @param array $data * @param array $load + * + * @dataProvider dataConfigLoad */ - public function testLoad(array $data, array $load) + public function testReload(array $data, array $load) { + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data)); + $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - foreach ($load as $loadedCats) { - $this->testedConfig->load($loadedCats); - } + $this->testedConfig->reload(); // Assert at least loaded cats are loaded foreach ($load as $loadedCats) { @@ -256,23 +269,31 @@ abstract class ConfigTest extends MockedTest /** * Test the configuration load() method with overwrite + * + * @dataProvider dataDoubleLoad */ public function testCacheLoadDouble(array $data1, array $data2, array $expect = []) { + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data1)); + $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - foreach ($data1 as $cat => $data) { - $this->testedConfig->load($cat); - } - // Assert at least loaded cats are loaded foreach ($data1 as $cat => $data) { self::assertConfig($cat, $data); } + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode($data2)); + + $this->testedConfig->reload(); + foreach ($data2 as $cat => $data) { - $this->testedConfig->load($cat); + self::assertConfig($cat, $data); } } @@ -281,44 +302,19 @@ abstract class ConfigTest extends MockedTest */ public function testLoadWrong() { - $this->configModel->shouldReceive('isConnected')->andReturn(true)->once(); - $this->configModel->shouldReceive('load')->withAnyArgs()->andReturn([])->once(); - - $this->testedConfig = $this->getInstance(); + $this->testedConfig = new Config($this->configFileManager, new Cache()); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); self::assertEmpty($this->testedConfig->getCache()->getAll()); } /** - * Test the configuration get() and set() methods without adapter + * Test the configuration get() and set() methods * * @dataProvider dataTests */ - public function testSetGetWithoutDB($data) + public function testSetGet($data) { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(3); - - $this->testedConfig = $this->getInstance(); - self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - - self::assertTrue($this->testedConfig->set('test', 'it', $data)); - - self::assertEquals($data, $this->testedConfig->get('test', 'it')); - self::assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); - } - - /** - * Test the configuration get() and set() methods with a model/db - * - * @dataProvider dataTests - */ - public function testSetGetWithDB($data) - { - $this->configModel->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); - $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); @@ -349,41 +345,16 @@ abstract class ConfigTest extends MockedTest self::assertEquals('default', $this->testedConfig->get('test', 'it', 'default', true)); } - /** - * Test the configuration get() method with refresh - * - * @dataProvider dataTests - */ - public function testGetWithRefresh($data) - { - $this->configCache->load(['test' => ['it' => 'now']], Cache::SOURCE_FILE); - - $this->testedConfig = $this->getInstance(); - self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - - // without refresh - self::assertEquals('now', $this->testedConfig->get('test', 'it')); - self::assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); - - // with refresh - self::assertEquals($data, $this->testedConfig->get('test', 'it', null, true)); - self::assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); - - // without refresh and wrong value and default - self::assertEquals('default', $this->testedConfig->get('test', 'not', 'default')); - self::assertNull($this->testedConfig->getCache()->get('test', 'not')); - } - /** * Test the configuration delete() method without a model/db * * @dataProvider dataTests */ - public function testDeleteWithoutDB($data) + public function testDelete($data) { $this->configCache->load(['test' => ['it' => $data]], Cache::SOURCE_FILE); - $this->testedConfig = $this->getInstance(); + $this->testedConfig = new Config($this->configFileManager, $this->configCache); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); self::assertEquals($data, $this->testedConfig->get('test', 'it')); @@ -396,51 +367,6 @@ abstract class ConfigTest extends MockedTest self::assertEmpty($this->testedConfig->getCache()->getAll()); } - /** - * Test the configuration delete() method with a model/db - */ - public function testDeleteWithDB() - { - $this->configCache->load(['test' => ['it' => 'now', 'quarter' => 'true']], Cache::SOURCE_FILE); - - $this->configModel->shouldReceive('delete') - ->with('test', 'it') - ->andReturn(false) - ->once(); - $this->configModel->shouldReceive('delete') - ->with('test', 'second') - ->andReturn(true) - ->once(); - $this->configModel->shouldReceive('delete') - ->with('test', 'third') - ->andReturn(false) - ->once(); - $this->configModel->shouldReceive('delete') - ->with('test', 'quarter') - ->andReturn(true) - ->once(); - - $this->testedConfig = $this->getInstance(); - self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); - - // directly set the value to the cache - $this->testedConfig->getCache()->set('test', 'it', 'now'); - - self::assertEquals('now', $this->testedConfig->get('test', 'it')); - self::assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); - - // delete from cache only - self::assertTrue($this->testedConfig->delete('test', 'it')); - // delete from db only - self::assertTrue($this->testedConfig->delete('test', 'second')); - // no delete - self::assertFalse($this->testedConfig->delete('test', 'third')); - // delete both - self::assertTrue($this->testedConfig->delete('test', 'quarter')); - - self::assertEmpty($this->testedConfig->getCache()->getAll()); - } - /** * Test the configuration get() and set() method where the db value has a higher prio than the config file */ @@ -462,6 +388,12 @@ abstract class ConfigTest extends MockedTest */ public function testSetGetLowPrio() { + vfsStream::newFile(ConfigFileManager::CONFIG_DATA_FILE) + ->at($this->root->getChild('config')) + ->setContent(ConfigFileTransformer::encode([ + 'config' => ['test' => 'it'], + ])); + $this->testedConfig = $this->getInstance(); self::assertInstanceOf(Cache::class, $this->testedConfig->getCache()); self::assertEquals('it', $this->testedConfig->get('config', 'test')); diff --git a/tests/src/Core/Config/JitConfigTest.php b/tests/src/Core/Config/JitConfigTest.php deleted file mode 100644 index a468ae727..000000000 --- a/tests/src/Core/Config/JitConfigTest.php +++ /dev/null @@ -1,255 +0,0 @@ -. - * - */ - -namespace Friendica\Test\src\Core\Config; - -use Friendica\Core\Config\Type\JitConfig; - -class JitConfigTest extends ConfigTest -{ - public function getInstance() - { - return new JitConfig($this->configCache, $this->configModel); - } - - /** - * @dataProvider dataConfigLoad - */ - public function testSetUp(array $data) - { - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => $data['config']]) - ->once(); - - parent::testSetUp($data); - } - - /** - * @dataProvider dataConfigLoad - * - * @param array $data - * @param array $load - */ - public function testLoad(array $data, array $load) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(count($load) + 1); - - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => $data['config']]) - ->once(); - - foreach ($load as $loadCat) { - $this->configModel->shouldReceive('load') - ->with($loadCat) - ->andReturn([$loadCat => $data[$loadCat]]) - ->once(); - } - - parent::testLoad($data, $load); - } - - /** - * @dataProvider dataDoubleLoad - */ - public function testCacheLoadDouble(array $data1, array $data2, array $expect = []) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(count($data1) + count($data2) + 1); - - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => $data1['config']]) - ->once(); - - foreach ($data1 as $cat => $data) { - $this->configModel->shouldReceive('load') - ->with($cat) - ->andReturn([$cat => $data]) - ->once(); - } - - - foreach ($data2 as $cat => $data) { - $this->configModel->shouldReceive('load') - ->with($cat) - ->andReturn([$cat => $data]) - ->once(); - } - - parent::testCacheLoadDouble($data1, $data2); - - // Assert the expected categories - foreach ($data2 as $cat => $data) { - self::assertConfig($cat, $expect[$cat]); - } - } - - /** - * @dataProvider dataTests - */ - public function testSetGetWithDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(3); - - $this->configModel->shouldReceive('load')->with('config')->andReturn(['config' => []])->once(); - - parent::testSetGetWithDB($data); - } - - /** - * @dataProvider dataTests - */ - public function testGetWithRefresh($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(4); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => []]) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn(null) - ->once(); - - // mocking the data get - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn($data) - ->once(); - - // mocking second get - $this->configModel->shouldReceive('get') - ->with('test', 'not') - ->andReturn(null) - ->once(); - - parent::testGetWithRefresh($data); - } - - public function testGetWrongWithoutDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(4); - - parent::testGetWrongWithoutDB(); - } - - /** - * @dataProvider dataTests - */ - public function testDeleteWithoutDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(4); - - parent::testDeleteWithoutDB($data); - } - - public function testDeleteWithDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(6); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => []]) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn(null) - ->once(); - - parent::testDeleteWithDB(); - } - - public function testSetGetHighPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => []]) - ->once(); - - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('prio') - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('123') - ->once(); - - parent::testSetGetHighPrio(); - } - - public function testSetGetLowPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->with('config') - ->andReturn(['config' => ['test' => 'it']]) - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('it') - ->once(); - - parent::testSetGetLowPrio(); - } -} diff --git a/tests/src/Core/Config/PreloadConfigTest.php b/tests/src/Core/Config/PreloadConfigTest.php deleted file mode 100644 index 1f5142761..000000000 --- a/tests/src/Core/Config/PreloadConfigTest.php +++ /dev/null @@ -1,213 +0,0 @@ -. - * - */ - -namespace Friendica\Test\src\Core\Config; - -use Friendica\Core\Config\Type\PreloadConfig; - -class PreloadConfigTest extends ConfigTest -{ - public function getInstance() - { - return new PreloadConfig($this->configCache, $this->configModel); - } - - /** - * @dataProvider dataConfigLoad - */ - public function testSetUp(array $data) - { - $this->configModel->shouldReceive('load') - ->andReturn($data) - ->once(); - - parent::testSetUp($data); - } - - /** - * @dataProvider dataConfigLoad - * - * @param array $data - * @param array $load - */ - public function testLoad(array $data, array $load) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('load') - ->andReturn($data) - ->once(); - - parent::testLoad($data, $load); - - // Assert that every category is loaded everytime - foreach ($data as $cat => $values) { - self::assertConfig($cat, $values); - } - } - - /** - * @dataProvider dataDoubleLoad - * - * @param array $data1 - * @param array $data2 - */ - public function testCacheLoadDouble(array $data1, array $data2, array $expect = []) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('load') - ->andReturn($data1) - ->once(); - - parent::testCacheLoadDouble($data1, $data2); - - // Assert that every category is loaded everytime and is NOT overwritten - foreach ($data1 as $cat => $values) { - self::assertConfig($cat, $values); - } - } - - /** - * @dataProvider dataTests - */ - public function testSetGetWithDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(2); - - $this->configModel->shouldReceive('load')->andReturn(['config' => []])->once(); - - parent::testSetGetWithDB($data); - } - - /** - * @dataProvider dataTests - */ - public function testGetWithRefresh($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(2); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => []]) - ->once(); - - // mocking one get - $this->configModel->shouldReceive('get') - ->with('test', 'it') - ->andReturn($data) - ->once(); - - parent::testGetWithRefresh($data); - } - - - public function testGetWrongWithoutDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(2); - - parent::testGetWrongWithoutDB(); - } - - /** - * @dataProvider dataTests - */ - public function testDeleteWithoutDB($data) - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(false) - ->times(2); - - parent::testDeleteWithoutDB($data); - } - - public function testDeleteWithDB() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true) - ->times(5); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => []]) - ->once(); - - parent::testDeleteWithDB(); - } - - - public function testSetGetHighPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => []]) - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('123') - ->once(); - - parent::testSetGetHighPrio(); - } - - public function testSetGetLowPrio() - { - $this->configModel->shouldReceive('isConnected') - ->andReturn(true); - - // constructor loading - $this->configModel->shouldReceive('load') - ->andReturn(['config' => ['test' => 'it']]) - ->once(); - - $this->configModel->shouldReceive('set') - ->with('config', 'test', '123') - ->andReturn(true) - ->once(); - - // mocking one get without result - $this->configModel->shouldReceive('get') - ->with('config', 'test') - ->andReturn('it') - ->once(); - - parent::testSetGetLowPrio(); - } -} diff --git a/tests/src/Core/Lock/SemaphoreLockTest.php b/tests/src/Core/Lock/SemaphoreLockTest.php index a8f95f6fc..6473935ca 100644 --- a/tests/src/Core/Lock/SemaphoreLockTest.php +++ b/tests/src/Core/Lock/SemaphoreLockTest.php @@ -24,7 +24,10 @@ namespace Friendica\Test\src\Core\Lock; use Dice\Dice; use Friendica\App; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Model\Config; use Friendica\Core\Config\Type\JitConfig; +use Friendica\Core\Config\Util\ConfigFileManager; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Core\Lock\Type\SemaphoreLock; use Friendica\Core\System; use Friendica\DI; @@ -42,11 +45,8 @@ class SemaphoreLockTest extends LockTest $app->shouldReceive('getHostname')->andReturn('friendica.local'); $dice->shouldReceive('create')->with(App::class)->andReturn($app); - $configMock = Mockery::mock(JitConfig::class); - $configMock - ->shouldReceive('get') - ->with('system', 'temppath') - ->andReturn('/tmp/'); + $configCache = new Cache(['system' => ['temppath' => '/tmp']]); + $configMock = new Config(Mockery::mock(ConfigFileManager::class), $configCache); $dice->shouldReceive('create')->with(IManageConfigValues::class)->andReturn($configMock); // @todo Because "get_temppath()" is using static methods, we have to initialize the BaseObject diff --git a/tests/src/Core/Storage/Repository/StorageManagerTest.php b/tests/src/Core/Storage/Repository/StorageManagerTest.php index af198706f..db4ca8ef2 100644 --- a/tests/src/Core/Storage/Repository/StorageManagerTest.php +++ b/tests/src/Core/Storage/Repository/StorageManagerTest.php @@ -22,9 +22,7 @@ namespace Friendica\Test\src\Core\Storage\Repository; use Dice\Dice; -use Friendica\App\Mode; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Type\PreloadConfig; use Friendica\Core\Hook; use Friendica\Core\L10n; use Friendica\Core\Session\Capability\IHandleSessions; @@ -41,7 +39,6 @@ use Friendica\Database\Definition\DbaDefinition; use Friendica\Database\Definition\ViewDefinition; use Friendica\DI; use Friendica\Core\Config\Factory\Config; -use Friendica\Core\Config\Repository; use Friendica\Core\Storage\Type; use Friendica\Test\DatabaseTest; use Friendica\Test\Util\Database\StaticDatabase; @@ -89,8 +86,7 @@ class StorageManagerTest extends DatabaseTest $this->dba = new StaticDatabase($configCache, $profiler, $dbaDefinition, $viewDefinition); - $configModel = new Repository\Config($this->dba, new Mode(Mode::DBCONFIGAVAILABLE)); - $this->config = new PreloadConfig($configCache, $configModel); + $this->config = new \Friendica\Core\Config\Model\Config($loader, $configCache); $this->config->set('storage', 'name', 'Database'); $this->config->set('storage', 'filesystem_path', $this->root->getChild(Type\FilesystemConfig::DEFAULT_BASE_FOLDER)->url()); diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index 44a6e01d3..0c3c5e160 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -52,10 +52,7 @@ class DBATest extends DatabaseTest */ public function testExists() { - self::assertTrue(DBA::exists('config', [])); + self::assertTrue(DBA::exists('user', [])); self::assertFalse(DBA::exists('notable', [])); - - self::assertTrue(DBA::exists('config', ['k' => 'hostname'])); - self::assertFalse(DBA::exists('config', ['k' => 'nonsense'])); } } diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index 67455f851..dfb46514f 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -44,30 +44,30 @@ class DBStructureTest extends DatabaseTest * @small */ public function testExists() { - self::assertTrue(DBStructure::existsTable('config')); + self::assertTrue(DBStructure::existsTable('user')); self::assertFalse(DBStructure::existsTable('notatable')); - self::assertTrue(DBStructure::existsColumn('config', ['k'])); - self::assertFalse(DBStructure::existsColumn('config', ['nonsense'])); - self::assertFalse(DBStructure::existsColumn('config', ['k', 'nonsense'])); + self::assertTrue(DBStructure::existsColumn('user', ['uid'])); + self::assertFalse(DBStructure::existsColumn('user', ['nonsense'])); + self::assertFalse(DBStructure::existsColumn('user', ['uid', 'nonsense'])); } /** * @small */ public function testRename() { - $fromColumn = 'k'; - $toColumn = 'key'; - $fromType = 'varbinary(255) not null'; - $toType = 'varbinary(255) not null comment \'Test To Type\''; + $fromColumn = 'email'; + $toColumn = 'email_key'; + $fromType = 'varchar(255) NOT NULL DEFAULT \'\' COMMENT \'the users email address\''; + $toType = 'varchar(255) NOT NULL DEFAULT \'\' COMMENT \'Adapted column\''; - self::assertTrue(DBStructure::rename('config', [ $fromColumn => [ $toColumn, $toType ]])); - self::assertTrue(DBStructure::existsColumn('config', [ $toColumn ])); - self::assertFalse(DBStructure::existsColumn('config', [ $fromColumn ])); + self::assertTrue(DBStructure::rename('user', [ $fromColumn => [ $toColumn, $toType ]])); + self::assertTrue(DBStructure::existsColumn('user', [ $toColumn ])); + self::assertFalse(DBStructure::existsColumn('user', [ $fromColumn ])); - self::assertTrue(DBStructure::rename('config', [ $toColumn => [ $fromColumn, $fromType ]])); - self::assertTrue(DBStructure::existsColumn('config', [ $fromColumn ])); - self::assertFalse(DBStructure::existsColumn('config', [ $toColumn ])); + self::assertTrue(DBStructure::rename('user', [ $toColumn => [ $fromColumn, $fromType ]])); + self::assertTrue(DBStructure::existsColumn('user', [ $fromColumn ])); + self::assertFalse(DBStructure::existsColumn('user', [ $toColumn ])); } /** diff --git a/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php b/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php index 034e1fc99..0d0afa64b 100644 --- a/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php +++ b/tests/src/Module/Api/GnuSocial/GnuSocial/ConfigTest.php @@ -26,6 +26,7 @@ use Friendica\App\Router; use Friendica\DI; use Friendica\Module\Api\GNUSocial\GNUSocial\Config; use Friendica\Test\src\Module\Api\ApiTest; +use Friendica\Test\Util\VFSTrait; class ConfigTest extends ApiTest { diff --git a/tests/src/Util/BaseURLTest.php b/tests/src/Util/BaseURLTest.php index 0be83be0a..2733096e3 100644 --- a/tests/src/Util/BaseURLTest.php +++ b/tests/src/Util/BaseURLTest.php @@ -199,24 +199,34 @@ class BaseURLTest extends MockedTest $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']); $configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']); + $savable = false; + // If we don't have an urlPath as an input, we assert it, we will save it to the DB for the next time if (!isset($input['urlPath']) && isset($assert['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'])->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'], false)->once(); + $savable = true; } // If we don't have the ssl_policy as an input, we assert it, we will save it to the DB for the next time if (!isset($input['sslPolicy']) && isset($assert['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'])->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'], false)->once(); + $savable = true; } // If we don't have the hostname as an input, we assert it, we will save it to the DB for the next time if (empty($input['hostname']) && !empty($assert['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'])->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'], false)->once(); + $savable = true; } // If we don't have an URL at first, but we assert it, we will save it to the DB for the next time if (empty($input['url']) && !empty($assert['url'])) { - $configMock->shouldReceive('set')->with('system', 'url', $assert['url'])->once(); + $configMock->shouldReceive('set')->with('system', 'url', $assert['url'], false)->once(); + $savable = true; + } + + if ($savable) { + $configMock->shouldReceive('save')->once(); } $baseUrl = new BaseURL($configMock, $server); @@ -325,18 +335,20 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); + + $configMock->shouldReceive('save')->once(); $baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']); @@ -363,18 +375,20 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); + + $configMock->shouldReceive('save')->once(); $baseUrl->saveByURL($url); @@ -531,22 +545,25 @@ class BaseURLTest extends MockedTest switch ($fail) { case 'hostname': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(false)->once(); break; case 'sslPolicy': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('save')->once(); break; case 'urlPath': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('save')->once(); break; case 'url': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any())->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('save')->once(); break; } From 1e574d5383e18de5b80445558a77b5330e7663be Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 02:07:38 +0100 Subject: [PATCH 06/19] Refactor IManageConfigValues interface --- bin/daemon.php | 2 +- src/App.php | 3 +- src/Console/Config.php | 4 +-- .../Config/Capability/IManageConfigValues.php | 12 +++---- src/Core/Config/Factory/Config.php | 17 ---------- src/Core/Config/Model/Config.php | 32 +++++++++++++++---- src/Core/Update.php | 8 ++--- src/Core/Worker.php | 2 +- src/Module/Friendica.php | 2 +- src/Worker/DBUpdate.php | 2 +- static/dependencies.config.php | 3 ++ update.php | 6 ---- view/theme/frio/style.php | 2 +- 13 files changed, 46 insertions(+), 49 deletions(-) diff --git a/bin/daemon.php b/bin/daemon.php index 385f725e3..f9ed693f3 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -71,7 +71,7 @@ if (DI::mode()->isInstall()) { DI::mode()->setExecutor(Mode::DAEMON); -DI::config()->load(); +DI::config()->reload(); if (empty(DI::config()->get('system', 'pidfile'))) { die(<<config->getCache()->get('system', 'basepath'); + return $this->config->get('system', 'basepath'); } /** diff --git a/src/Console/Config.php b/src/Console/Config.php index b57cdbeec..8df7c28f2 100644 --- a/src/Console/Config.php +++ b/src/Console/Config.php @@ -157,7 +157,7 @@ HELP; if (count($this->args) == 1) { $cat = $this->getArgument(0); - $this->config->load($cat); + $this->config->reload(); $configCache = $this->config->getCache(); if ($configCache->get($cat) !== null) { @@ -178,7 +178,7 @@ HELP; } if (count($this->args) == 0) { - $this->config->load(); + $this->config->reload(); if ($this->config->get('system', 'config_adapter') == 'jit' && $this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { $this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only'); diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 27238822a..88fa96314 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -30,36 +30,32 @@ use Friendica\Core\Config\ValueObject\Cache; interface IManageConfigValues { /** - * Loads all configuration values of family into a cached storage. + * Reloads all configuration values (from filesystem and environment variables) * * All configuration values of the system are stored in the cache. * - * @param string $cat The category of the configuration value - * * @return void * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function load(string $cat = 'config'); + public function reload(); /** * Get a particular user's config variable given the category name * ($cat) and a $key. * * Get a particular config value from the given category ($cat) - * and the $key from a cached storage either from the database or from the cache. * * @param string $cat The category of the configuration value * @param string $key The configuration key to query * @param mixed $default_value Deprecated, use `Config->get($cat, $key, null, $refresh) ?? $default_value` instead - * @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) * * @return mixed Stored value or null if it does not exist * * @throws ConfigPersistenceException In case the persistence layer throws errors * */ - public function get(string $cat, string $key, $default_value = null, bool $refresh = false); + public function get(string $cat, string $key, $default_value = null); /** * Sets a configuration value for system config @@ -81,6 +77,8 @@ interface IManageConfigValues /** * Save back the overridden values of the config cache + * + * @throws ConfigPersistenceException In case the persistence layer throws errors */ public function save(); diff --git a/src/Core/Config/Factory/Config.php b/src/Core/Config/Factory/Config.php index fac931fac..e1094fd70 100644 --- a/src/Core/Config/Factory/Config.php +++ b/src/Core/Config/Factory/Config.php @@ -81,21 +81,4 @@ class Config return $configCache; } - - /** - * @param Cache $configCache The config cache of this adapter - * @param Repository\Config $configRepo The configuration repository - * - * @return Capability\IManageConfigValues - */ - public function create(Util\ConfigFileManager $loader, Cache $configCache, Repository\Config $configRepo) - { - if ($configCache->get('system', 'config_adapter') === 'preload') { - $configuration = new Type\PreloadConfig($loader, $configCache, $configRepo); - } else { - $configuration = new Type\JitConfig($loader, $configCache, $configRepo); - } - - return $configuration; - } } diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 3af2f7e4b..3de97158f 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -22,6 +22,8 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Exception\ConfigFileException; +use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; @@ -38,14 +40,19 @@ class Config implements IManageConfigValues /** @var ConfigFileManager */ protected $configFileManager; + /** @var array */ + protected $server; + /** * @param ConfigFileManager $configFileManager The configuration file manager to save back configs - * @param Cache $configCache The configuration cache (based on the config-files) + * @param Cache $configCache The configuration cache (based on the config-files) + * @param array $server The $_SERVER variable */ - public function __construct(ConfigFileManager $configFileManager, Cache $configCache) + public function __construct(ConfigFileManager $configFileManager, Cache $configCache, array $server = []) { $this->configFileManager = $configFileManager; $this->configCache = $configCache; + $this->server = $server; } /** @@ -56,24 +63,36 @@ class Config implements IManageConfigValues return $this->configCache; } + /** {@inheritDoc} */ public function save() { - $this->configFileManager->saveData($this->configCache); + try { + $this->configFileManager->saveData($this->configCache); + } catch (ConfigFileException $e) { + throw new ConfigPersistenceException('Cannot save config', $e); + } } - public function load(string $cat = 'config') + /** {@inheritDoc} */ + public function reload() { $configCache = new Cache(); - $this->configFileManager->setupCache($configCache, $_SERVER); + try { + $this->configFileManager->setupCache($configCache, $this->server); + } catch (ConfigFileException $e) { + throw new ConfigPersistenceException('Cannot reload config', $e); + } $this->configCache = $configCache; } - public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + /** {@inheritDoc} */ + public function get(string $cat, string $key, $default_value = null) { return $this->configCache->get($cat, $key) ?? $default_value; } + /** {@inheritDoc} */ public function set(string $cat, string $key, $value, bool $autosave = true): bool { $stored = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); @@ -85,6 +104,7 @@ class Config implements IManageConfigValues return $stored; } + /** {@inheritDoc} */ public function delete(string $cat, string $key, bool $autosave = true): bool { $removed = $this->configCache->delete($cat, $key); diff --git a/src/Core/Update.php b/src/Core/Update.php index 8da473839..9a2ebe1bb 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -54,7 +54,7 @@ class Update } // Don't check the status if the last update was failed - if (DI::config()->get('system', 'update', Update::SUCCESS, true) == Update::FAILED) { + if (DI::config()->get('system', 'update', Update::SUCCESS) == Update::FAILED) { return; } @@ -119,7 +119,7 @@ class Update DI::lock()->release('dbupdate', true); } - $build = DI::config()->get('system', 'build', null, true); + $build = DI::config()->get('system', 'build', null); if (empty($build) || ($build > DB_UPDATE_VERSION)) { $build = DB_UPDATE_VERSION - 1; @@ -132,7 +132,7 @@ class Update $stored = intval($build); $current = intval(DB_UPDATE_VERSION); if ($stored < $current || $force) { - DI::config()->load('database'); + DI::config()->reload(); // Compare the current structure with the defined structure // If the Lock is acquired, never release it automatically to avoid double updates @@ -141,7 +141,7 @@ class Update Logger::notice('Update starting.', ['from' => $stored, 'to' => $current]); // Checks if the build changed during Lock acquiring (so no double update occurs) - $retryBuild = DI::config()->get('system', 'build', null, true); + $retryBuild = DI::config()->get('system', 'build', null); if ($retryBuild !== $build) { Logger::notice('Update already done.', ['from' => $stored, 'to' => $current]); DI::lock()->release('dbupdate'); diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 8fc74f1d8..2db9256d1 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -331,7 +331,7 @@ class Worker $mypid = getmypid(); // Quit when in maintenance - if (DI::config()->get('system', 'maintenance', false, true)) { + if (DI::config()->get('system', 'maintenance', false)) { Logger::notice('Maintenance mode - quit process', ['pid' => $mypid]); return false; } diff --git a/src/Module/Friendica.php b/src/Module/Friendica.php index 72360ddca..bbcccd7da 100644 --- a/src/Module/Friendica.php +++ b/src/Module/Friendica.php @@ -157,7 +157,7 @@ class Friendica extends BaseModule $visible_addons = Addon::getVisibleList(); - $config->load('feature_lock'); + $config->reload(); $locked_features = []; $featureLocks = $config->get('config', 'feature_lock'); if (isset($featureLocks)) { diff --git a/src/Worker/DBUpdate.php b/src/Worker/DBUpdate.php index e11f7bf40..7b7c3b8c9 100644 --- a/src/Worker/DBUpdate.php +++ b/src/Worker/DBUpdate.php @@ -32,7 +32,7 @@ class DBUpdate public static function execute() { // Just in case the last update wasn't failed - if (DI::config()->get('system', 'update', Update::SUCCESS, true) != Update::FAILED) { + if (DI::config()->get('system', 'update', Update::SUCCESS) != Update::FAILED) { Update::run(DI::app()->getBasePath()); } } diff --git a/static/dependencies.config.php b/static/dependencies.config.php index 90f01feed..1844001bb 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -99,6 +99,9 @@ return [ ], Config\Capability\IManageConfigValues::class => [ 'instanceOf' => Config\Model\Config::class, + 'constructParams' => [ + $_SERVER, + ], ], PConfig\Capability\IManagePersonalConfigValues::class => [ 'instanceOf' => PConfig\Factory\PConfig::class, diff --git a/update.php b/update.php index fda04b6ce..b9edbabd7 100644 --- a/update.php +++ b/update.php @@ -1178,12 +1178,6 @@ function update_1505() function update_1508() { - $categories = DBA::toArray(DBA::p("SELECT DISTINCT `cat` AS 'cat' FROM `config`")); - - foreach ($categories as $category) { - DI::config()->load($category['cat']); - } - $config = DBA::selectToArray('config'); foreach ($config as $entry) { diff --git a/view/theme/frio/style.php b/view/theme/frio/style.php index 6e05c6b32..8feefdc93 100644 --- a/view/theme/frio/style.php +++ b/view/theme/frio/style.php @@ -49,7 +49,7 @@ $login_bg_color = ''; $modified = time(); if (DI::mode()->has(\Friendica\App\Mode::MAINTENANCEDISABLED)) { - DI::config()->load('frio'); + DI::config()->reload('frio'); // Default to hard-coded values for empty settings $scheme = DI::config()->get('frio', 'scheme', DI::config()->get('frio', 'schema')); From 10f3de0aa2efca1c94b5780b2da21e06cd5129be Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 02:57:57 +0100 Subject: [PATCH 07/19] Remove deprecated Mode::DBCONFIGAVAILABLE --- src/App/Mode.php | 16 +++--------- src/Console/Cache.php | 2 +- src/Console/Config.php | 8 ------ src/Console/Lock.php | 2 +- tests/functional/DependencyCheckTest.php | 1 - tests/src/App/ModeTest.php | 31 ------------------------ 6 files changed, 5 insertions(+), 55 deletions(-) diff --git a/src/App/Mode.php b/src/App/Mode.php index ca78ebd5b..514df91c6 100644 --- a/src/App/Mode.php +++ b/src/App/Mode.php @@ -149,16 +149,7 @@ class Mode $mode |= Mode::DBAVAILABLE; - if ($database->fetchFirst("SHOW TABLES LIKE 'config'") === false) { - return new Mode($mode); - } - - $mode |= Mode::DBCONFIGAVAILABLE; - - if (!empty($configCache->get('system', 'maintenance')) || - // Don't use Config or Configuration here because we're possibly BEFORE initializing the Configuration, - // so this could lead to a dependency circle - !empty($database->selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])['v'])) { + if (!empty($configCache->get('system', 'maintenance'))) { return new Mode($mode); } @@ -232,14 +223,14 @@ class Mode } /** - * Install mode is when the local config file is missing or the DB schema hasn't been installed yet. + * Install mode is when the local config file is missing or the database isn't available. * * @return bool Whether installation mode is active (local/database configuration files present or not) */ public function isInstall(): bool { return !$this->has(Mode::LOCALCONFIGPRESENT) || - !$this->has(MODE::DBCONFIGAVAILABLE); + !$this->has(MODE::DBAVAILABLE); } /** @@ -251,7 +242,6 @@ class Mode { return $this->has(Mode::LOCALCONFIGPRESENT) && $this->has(Mode::DBAVAILABLE) && - $this->has(Mode::DBCONFIGAVAILABLE) && $this->has(Mode::MAINTENANCEDISABLED); } diff --git a/src/Console/Cache.php b/src/Console/Cache.php index e4d113533..c55371699 100644 --- a/src/Console/Cache.php +++ b/src/Console/Cache.php @@ -99,7 +99,7 @@ HELP; $this->out('Options: ' . var_export($this->options, true)); } - if (!$this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { + if (!$this->appMode->has(App\Mode::DBAVAILABLE)) { $this->out('Database isn\'t ready or populated yet, database cache won\'t be available'); } diff --git a/src/Console/Config.php b/src/Console/Config.php index 8df7c28f2..efb795360 100644 --- a/src/Console/Config.php +++ b/src/Console/Config.php @@ -115,10 +115,6 @@ HELP; throw new CommandArgsException('Too many arguments'); } - if (!$this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { - $this->out('Database isn\'t ready or populated yet, showing file config only'); - } - if (count($this->args) == 3) { $cat = $this->getArgument(0); $key = $this->getArgument(1); @@ -180,10 +176,6 @@ HELP; if (count($this->args) == 0) { $this->config->reload(); - if ($this->config->get('system', 'config_adapter') == 'jit' && $this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { - $this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only'); - } - $config = $this->config->getCache()->getAll(); foreach ($config as $cat => $section) { if (is_array($section)) { diff --git a/src/Console/Lock.php b/src/Console/Lock.php index 4e324691c..0acede5df 100644 --- a/src/Console/Lock.php +++ b/src/Console/Lock.php @@ -93,7 +93,7 @@ HELP; $this->out('Options: ' . var_export($this->options, true)); } - if (!$this->appMode->has(App\Mode::DBCONFIGAVAILABLE)) { + if (!$this->appMode->has(App\Mode::DBAVAILABLE)) { $this->out('Database isn\'t ready or populated yet, database cache won\'t be available'); } diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php index 9bc7624d6..a371ffea4 100644 --- a/tests/functional/DependencyCheckTest.php +++ b/tests/functional/DependencyCheckTest.php @@ -150,7 +150,6 @@ class DependencyCheckTest extends TestCase self::assertTrue($mode->has(App\Mode::LOCALCONFIGPRESENT), 'No local config present'); self::assertTrue($mode->has(App\Mode::DBAVAILABLE), 'Database is not available'); - self::assertTrue($mode->has(App\Mode::DBCONFIGAVAILABLE), 'Database config is not available'); self::assertTrue($mode->has(App\Mode::MAINTENANCEDISABLED), 'In maintenance mode'); self::assertTrue($mode->isNormal(), 'Not in normal mode'); diff --git a/tests/src/App/ModeTest.php b/tests/src/App/ModeTest.php index 86a6c7763..aecd2b175 100644 --- a/tests/src/App/ModeTest.php +++ b/tests/src/App/ModeTest.php @@ -102,29 +102,11 @@ class ModeTest extends MockedTest self::assertFalse($mode->has(Mode::DBAVAILABLE)); } - public function testWithoutDatabaseSetup() - { - $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); - - $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(false)->once(); - - $mode = (new Mode())->determine($this->basePathMock, $this->databaseMock, $this->configCacheMock); - - self::assertFalse($mode->isNormal()); - self::assertTrue($mode->isInstall()); - - self::assertTrue($mode->has(Mode::LOCALCONFIGPRESENT)); - } - public function testWithMaintenanceMode() { $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once(); $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance') ->andReturn(true)->once(); @@ -133,7 +115,6 @@ class ModeTest extends MockedTest self::assertFalse($mode->isNormal()); self::assertFalse($mode->isInstall()); - self::assertTrue($mode->has(Mode::DBCONFIGAVAILABLE)); self::assertFalse($mode->has(Mode::MAINTENANCEDISABLED)); } @@ -142,20 +123,14 @@ class ModeTest extends MockedTest $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once(); $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance') ->andReturn(false)->once(); - $this->databaseMock->shouldReceive('selectFirst') - ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance']) - ->andReturn(['v' => null])->once(); $mode = (new Mode())->determine($this->basePathMock, $this->databaseMock, $this->configCacheMock); self::assertTrue($mode->isNormal()); self::assertFalse($mode->isInstall()); - self::assertTrue($mode->has(Mode::DBCONFIGAVAILABLE)); self::assertTrue($mode->has(Mode::MAINTENANCEDISABLED)); } @@ -167,20 +142,14 @@ class ModeTest extends MockedTest $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once(); $this->databaseMock->shouldReceive('connected')->andReturn(true)->once(); - $this->databaseMock->shouldReceive('fetchFirst') - ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once(); $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance') ->andReturn(false)->once(); - $this->databaseMock->shouldReceive('selectFirst') - ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance']) - ->andReturn(['v' => '0'])->once(); $mode = (new Mode())->determine($this->basePathMock, $this->databaseMock, $this->configCacheMock); self::assertTrue($mode->isNormal()); self::assertFalse($mode->isInstall()); - self::assertTrue($mode->has(Mode::DBCONFIGAVAILABLE)); self::assertTrue($mode->has(Mode::MAINTENANCEDISABLED)); } From 88b3effc180c5477bc8002b9d9ea8f3f628588dd Mon Sep 17 00:00:00 2001 From: Philipp Date: Wed, 28 Dec 2022 03:32:52 +0100 Subject: [PATCH 08/19] Use toConfigValue in case of serialized, legacy data --- src/Core/Config/ValueObject/Cache.php | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index c427996c3..2bac625ad 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -182,6 +182,8 @@ class Cache $key == 'password' && is_string($value)) { $this->config[$cat][$key] = new HiddenString((string)$value); + } else if (is_string($value)) { + $this->config[$cat][$key] = self::toConfigValue($value); } else { $this->config[$cat][$key] = $value; } @@ -191,6 +193,35 @@ class Cache return true; } + /** + * Formats a DB value to a config value + * - null = The db-value isn't set + * - bool = The db-value is either '0' or '1' + * - array = The db-value is a serialized array + * - string = The db-value is a string + * + * Keep in mind that there aren't any numeric/integer config values in the database + * + * @param string|null $value + * + * @return null|array|string + */ + public static function toConfigValue(?string $value) + { + if (!isset($value)) { + return null; + } + + switch (true) { + // manage array value + case preg_match("|^a:[0-9]+:{.*}$|s", $value): + return unserialize($value); + + default: + return $value; + } + } + /** * Deletes a value from the config cache. * From 4585335db93777463bf917604a8c8339b61eac91 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 22:09:40 +0100 Subject: [PATCH 09/19] Check if table exists --- update.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/update.php b/update.php index b9edbabd7..ad33dde01 100644 --- a/update.php +++ b/update.php @@ -1148,6 +1148,10 @@ function update_1502() function update_1505() { + if (!DBStructure::existsTable('config')) { + return Update::SUCCESS; + } + $conditions = [ "((`cat` = ?) AND ((`k` LIKE ?) OR (`k` = ?) OR (`k` LIKE ?) OR (`k` = ?))) OR " . "((`cat` != ?) AND (`k` LIKE ?)) OR " . From 5ff1d431aa313c8e69d97dcdf23c69a38b69ba02 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 1 Jan 2023 22:18:57 +0100 Subject: [PATCH 10/19] add another test --- tests/datasets/config/C.node.config.php | 48 +++++++++++++++++++ .../Config/Util/ConfigFileTransformerTest.php | 3 ++ 2 files changed, 51 insertions(+) create mode 100644 tests/datasets/config/C.node.config.php diff --git a/tests/datasets/config/C.node.config.php b/tests/datasets/config/C.node.config.php new file mode 100644 index 000000000..6d93d8d7a --- /dev/null +++ b/tests/datasets/config/C.node.config.php @@ -0,0 +1,48 @@ + [ + 'hostname' => 'friendica.local', + ], + 'system' => [ + 'disable_email_validation' => 1, + 'disable_password_exposed' => 1, + 'throttle_limit_day' => 100, + 'throttle_limit_week' => 100, + 'throttle_limit_month' => 100, + 'temppath' => '/tmp/friendica.local', + 'theme' => 'frio', + 'url' => 'https://friendica.local', + 'urlpath' => '', + 'build' => 1508, + 'maintenance' => false, + 'dbupdate' => 1, + 'update' => 0, + 'spoolpath' => '/tmp/localhost/spool', + 'actor_name' => 'friendica', + 'site_prvkey' => '-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALgypZoZ2X7rYCHT +pXZRPKZYOJrtzAZoAD6a2FESfax/mW7tGF8/XGcsu4E8a0WUs18CDb09iDlECs0r +WFkyxOsS55FyDPVNOVheU6ziqmjTNggr1qR8iIpPW2xHAnFjCfvJxgaUfszdoeUV +mhA++BrxFGRpfcH49O+dVcjisJEVAgMBAAECgYEAq0QsRkSSvjgMgmdQCdsvEVwm +BafldG9vCsbfK0KOJ73c5A8AAk/fku88yMVs2J2SylwWekakStrBUFNlKkrSXEv3 +r1l0b+dfniaTGJkawqgRh+U/0G9LN+cdZYt5EuhNhCbmIQB+FOI12jAx6COwEENi +824zrrwn0BU1VTOMwwECQQDnBqq0J9JCJAtjqX+3xd8DvTRYjYvtvXlQ8fwrGxBc +GwURNG8aMGTaj+sn2kVWNt4wLQqj/CTJ42y0bkKYMJftAkEAzBwSqfuMZD/+nEkM +wDQb1G2z+BaxLh5JJQo80WX9tORbspOkbEuPgFprO6NB0/vNH5m4AaL3jymokH1p +JfVoyQJBAN+GSsmOMdf+qepeiA0V7OXgPXJkWXvHtEZGK1bFk7maBvgThF+RbTMu +xjZD8IwvACEao03wWuPfIEEe4V4Avi0CQCc5FdUYg+gX7CO4XfzphpeR5U29fprw +MvotN3a99L04TO7KNISjGJZ/ya+SNeo4rzhtX9DgslYOmVf64aPrvxECQQDB79vF +Kx2HyacBSErXrlqqkPdFo8KxhKCAVhrs0VBU1WPswolzsZvRdFuielhGP49DjjE7 +JV1Als1hl1xTduNb +-----END PRIVATE KEY----- + ', + 'site_pubkey' => '-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4MqWaGdl+62Ah06V2UTymWDia +7cwGaAA+mthREn2sf5lu7RhfP1xnLLuBPGtFlLNfAg29PYg5RArNK1hZMsTrEueR +cgz1TTlYXlOs4qpo0zYIK9akfIiKT1tsRwJxYwn7ycYGlH7M3aHlFZoQPvga8RRk +aX3B+PTvnVXI4rCRFQIDAQAB +-----END PUBLIC KEY----- + ', + ], +]; diff --git a/tests/src/Core/Config/Util/ConfigFileTransformerTest.php b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php index 6cd5bc706..5a0ab96a4 100644 --- a/tests/src/Core/Config/Util/ConfigFileTransformerTest.php +++ b/tests/src/Core/Config/Util/ConfigFileTransformerTest.php @@ -35,6 +35,9 @@ class ConfigFileTransformerTest extends MockedTest 'extended' => [ 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/B.node.config.php'), ], + 'friendica.local' => [ + 'configFile' => (dirname(__DIR__, 4) . '/datasets/config/C.node.config.php'), + ], ]; } From ae1533e31269b0ecab121ea82cf3b77eede96ba8 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 2 Jan 2023 02:25:41 +0100 Subject: [PATCH 11/19] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- src/Core/Config/Model/Config.php | 4 +--- src/Core/Config/Util/ConfigFileManager.php | 2 +- src/Core/Config/ValueObject/Cache.php | 11 ++++------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 3de97158f..7829b75ff 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -32,9 +32,7 @@ use Friendica\Core\Config\ValueObject\Cache; */ class Config implements IManageConfigValues { - /** - * @var Cache - */ + /** @var Cache */ protected $configCache; /** @var ConfigFileManager */ diff --git a/src/Core/Config/Util/ConfigFileManager.php b/src/Core/Config/Util/ConfigFileManager.php index 82de4a99c..378d7fbd4 100644 --- a/src/Core/Config/Util/ConfigFileManager.php +++ b/src/Core/Config/Util/ConfigFileManager.php @@ -401,7 +401,7 @@ class ConfigFileManager private function loadConfigFile(string $filepath): array { if (file_exists($filepath)) { - $config = include($filepath); + $config = include $filepath; if (!is_array($config)) { throw new ConfigFileException('Error loading config file ' . $filepath); diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index 2bac625ad..a074414bf 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -212,13 +212,10 @@ class Cache return null; } - switch (true) { - // manage array value - case preg_match("|^a:[0-9]+:{.*}$|s", $value): - return unserialize($value); - - default: - return $value; + if (preg_match("|^a:[0-9]+:{.*}$|s", $value)) { + return unserialize($value); + } else { + return $value; } } From 376e0a93972efb84bd8e5fbfb87d2670105a97aa Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 2 Jan 2023 02:27:05 +0100 Subject: [PATCH 12/19] Fix Admin\Site --- src/Module/Admin/Site.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index cf2f6c535..f572d3e72 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -211,19 +211,19 @@ class Site extends BaseAdmin DI::config()->set('system', 'touch_icon' , $touch_icon, false); if ($banner == "") { - DI::config()->set('system', 'banner', false); + DI::config()->delete('system', 'banner', false); } else { DI::config()->set('system', 'banner', $banner, false); } if (empty($email_banner)) { - DI::config()->set('system', 'email_banner', false); + DI::config()->delete('system', 'email_banner', false); } else { DI::config()->set('system', 'email_banner', $email_banner, false); } if (empty($additional_info)) { - DI::config()->set('config', 'info', false); + DI::config()->delete('config', 'info', false); } else { DI::config()->set('config', 'info', $additional_info, false); } @@ -232,12 +232,12 @@ class Site extends BaseAdmin Theme::install($theme); if ($theme_mobile == '---') { - DI::config()->set('system', 'mobile-theme', false); + DI::config()->delete('system', 'mobile-theme', false); } else { DI::config()->set('system', 'mobile-theme', $theme_mobile, false); } if ($singleuser == '---') { - DI::config()->set('system', 'singleuser', false); + DI::config()->delete('system', 'singleuser', false); } else { DI::config()->set('system', 'singleuser', $singleuser, false); } From 4d4b4a8858cfb7edb47adda460720e3655f104ca Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 13:16:19 +0100 Subject: [PATCH 13/19] Revert BaseURL --- src/App/BaseURL.php | 43 ++++++++---------------- tests/src/Util/BaseURLTest.php | 61 ++++++++++++---------------------- 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/src/App/BaseURL.php b/src/App/BaseURL.php index f79bae38f..20fd54916 100644 --- a/src/App/BaseURL.php +++ b/src/App/BaseURL.php @@ -177,7 +177,7 @@ class BaseURL $currURLPath = $this->urlPath; if (!empty($hostname) && $hostname !== $this->hostname) { - if ($this->config->set('config', 'hostname', $hostname, false)) { + if ($this->config->set('config', 'hostname', $hostname)) { $this->hostname = $hostname; } else { return false; @@ -185,45 +185,40 @@ class BaseURL } if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) { - if ($this->config->set('system', 'ssl_policy', $sslPolicy, false)) { + if ($this->config->set('system', 'ssl_policy', $sslPolicy)) { $this->sslPolicy = $sslPolicy; } else { $this->hostname = $currHostname; - $this->config->set('config', 'hostname', $this->hostname, false); - $this->config->save(); + $this->config->set('config', 'hostname', $this->hostname); return false; } } if (isset($urlPath) && $urlPath !== $this->urlPath) { - if ($this->config->set('system', 'urlpath', $urlPath, false)) { + if ($this->config->set('system', 'urlpath', $urlPath)) { $this->urlPath = $urlPath; } else { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; - $this->config->set('config', 'hostname', $this->hostname, false); - $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); - $this->config->save(); + $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('system', 'ssl_policy', $this->sslPolicy); return false; } } $this->determineBaseUrl(); - if (!$this->config->set('system', 'url', $this->url, false)) { + if (!$this->config->set('system', 'url', $this->url)) { $this->hostname = $currHostname; $this->sslPolicy = $currSSLPolicy; $this->urlPath = $currURLPath; $this->determineBaseUrl(); - $this->config->set('config', 'hostname', $this->hostname, false); - $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); - $this->config->set('system', 'urlpath', $this->urlPath, false); - $this->config->save(); + $this->config->set('config', 'hostname', $this->hostname); + $this->config->set('system', 'ssl_policy', $this->sslPolicy); + $this->config->set('system', 'urlpath', $this->urlPath); return false; } - $this->config->save(); - return true; } @@ -300,21 +295,17 @@ class BaseURL $this->sslPolicy = $this->config->get('system', 'ssl_policy'); $this->url = $this->config->get('system', 'url'); - $savable = false; - if (empty($this->hostname)) { $this->determineHostname(); if (!empty($this->hostname)) { - $this->config->set('config', 'hostname', $this->hostname, false); - $savable = true; + $this->config->set('config', 'hostname', $this->hostname); } } if (!isset($this->urlPath)) { $this->determineURLPath(); - $this->config->set('system', 'urlpath', $this->urlPath, false); - $savable = true; + $this->config->set('system', 'urlpath', $this->urlPath); } if (!isset($this->sslPolicy)) { @@ -323,22 +314,16 @@ class BaseURL } else { $this->sslPolicy = self::DEFAULT_SSL_SCHEME; } - $this->config->set('system', 'ssl_policy', $this->sslPolicy, false); - $savable = true; + $this->config->set('system', 'ssl_policy', $this->sslPolicy); } if (empty($this->url)) { $this->determineBaseUrl(); if (!empty($this->url)) { - $this->config->set('system', 'url', $this->url, false); - $savable = true; + $this->config->set('system', 'url', $this->url); } } - - if ($savable) { - $this->config->save(); - } } /** diff --git a/tests/src/Util/BaseURLTest.php b/tests/src/Util/BaseURLTest.php index 2733096e3..0be83be0a 100644 --- a/tests/src/Util/BaseURLTest.php +++ b/tests/src/Util/BaseURLTest.php @@ -199,34 +199,24 @@ class BaseURLTest extends MockedTest $configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']); $configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']); - $savable = false; - // If we don't have an urlPath as an input, we assert it, we will save it to the DB for the next time if (!isset($input['urlPath']) && isset($assert['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'], false)->once(); - $savable = true; + $configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'])->once(); } // If we don't have the ssl_policy as an input, we assert it, we will save it to the DB for the next time if (!isset($input['sslPolicy']) && isset($assert['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'], false)->once(); - $savable = true; + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'])->once(); } // If we don't have the hostname as an input, we assert it, we will save it to the DB for the next time if (empty($input['hostname']) && !empty($assert['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'], false)->once(); - $savable = true; + $configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'])->once(); } // If we don't have an URL at first, but we assert it, we will save it to the DB for the next time if (empty($input['url']) && !empty($assert['url'])) { - $configMock->shouldReceive('set')->with('system', 'url', $assert['url'], false)->once(); - $savable = true; - } - - if ($savable) { - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('system', 'url', $assert['url'])->once(); } $baseUrl = new BaseURL($configMock, $server); @@ -335,20 +325,18 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); - - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); $baseUrl->save($save['hostname'], $save['sslPolicy'], $save['urlPath']); @@ -375,20 +363,18 @@ class BaseURLTest extends MockedTest $baseUrl = new BaseURL($configMock, []); if (isset($save['hostname'])) { - $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', $save['hostname'])->andReturn(true)->once(); } if (isset($save['urlPath'])) { - $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'urlpath', $save['urlPath'])->andReturn(true)->once(); } if (isset($save['sslPolicy'])) { - $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'], false)->andReturn(true)->once(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', $save['sslPolicy'])->andReturn(true)->once(); } - $configMock->shouldReceive('set')->with('system', 'url', $url, false)->andReturn(true)->once(); - - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('system', 'url', $url)->andReturn(true)->once(); $baseUrl->saveByURL($url); @@ -545,25 +531,22 @@ class BaseURLTest extends MockedTest switch ($fail) { case 'hostname': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(false)->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(false)->once(); break; case 'sslPolicy': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(false)->once(); - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(false)->once(); break; case 'urlPath': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(false)->once(); - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(false)->once(); break; case 'url': - $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any(), false)->andReturn(true)->twice(); - $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any(), false)->andReturn(false)->once(); - $configMock->shouldReceive('save')->once(); + $configMock->shouldReceive('set')->with('config', 'hostname', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'ssl_policy', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'urlpath', \Mockery::any())->andReturn(true)->twice(); + $configMock->shouldReceive('set')->with('system', 'url', \Mockery::any())->andReturn(false)->once(); break; } From 65d79d4c9350665fedbf434799fed335de64688e Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 14:18:53 +0100 Subject: [PATCH 14/19] Introduce ISetConfigValuesTransactional for transactional config behaviour --- src/Console/Maintenance.php | 10 +- src/Console/Relocate.php | 7 +- .../Config/Capability/IManageConfigValues.php | 29 ++- .../ISetConfigValuesTransactional.php | 84 +++++++++ src/Core/Config/Model/Config.php | 43 +++-- src/Core/Config/Model/TransactionalConfig.php | 89 +++++++++ src/Core/Config/ValueObject/Cache.php | 67 +++++++ src/Core/Update.php | 28 +-- src/Database/DBStructure.php | 7 +- src/Module/Admin/Site.php | 176 +++++++++--------- tests/src/Core/Config/Cache/CacheTest.php | 48 +++++ .../Config/Cache/ConfigFileLoaderTest.php | 34 ++-- .../Core/Config/TransactionalConfigTest.php | 110 +++++++++++ update.php | 6 +- 14 files changed, 588 insertions(+), 150 deletions(-) create mode 100644 src/Core/Config/Capability/ISetConfigValuesTransactional.php create mode 100644 src/Core/Config/Model/TransactionalConfig.php create mode 100644 tests/src/Core/Config/TransactionalConfigTest.php diff --git a/src/Console/Maintenance.php b/src/Console/Maintenance.php index bd3aef7c2..6a11eb2bb 100644 --- a/src/Console/Maintenance.php +++ b/src/Console/Maintenance.php @@ -100,17 +100,19 @@ HELP; $enabled = intval($this->getArgument(0)); - $this->config->set('system', 'maintenance', $enabled, false); + $transactionConfig = $this->config->transactional(); + + $transactionConfig->set('system', 'maintenance', $enabled); $reason = $this->getArgument(1); if ($enabled && $this->getArgument(1)) { - $this->config->set('system', 'maintenance_reason', $this->getArgument(1), false); + $transactionConfig->set('system', 'maintenance_reason', $this->getArgument(1)); } else { - $this->config->set('system', 'maintenance_reason', '', false); + $transactionConfig->delete('system', 'maintenance_reason'); } - $this->config->save(); + $transactionConfig->save(); if ($enabled) { $mode_str = "maintenance mode"; diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 8a76c9207..7a2ef1d07 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -189,9 +189,10 @@ HELP; return 1; } finally { $this->out('Leaving maintenance mode'); - $this->config->set('system', 'maintenance', false, false); - $this->config->set('system', 'maintenance_reason', '', false); - $this->config->save(); + $this->config->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); } // send relocate diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 88fa96314..42ebea000 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -22,6 +22,7 @@ namespace Friendica\Core\Config\Capability; use Friendica\Core\Config\Exception\ConfigPersistenceException; +use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; /** @@ -57,6 +58,20 @@ interface IManageConfigValues */ public function get(string $cat, string $key, $default_value = null); + /** + * Load all configuration values from a given cache and saves it back in the configuration node store + * @see ConfigFileManager::CONFIG_DATA_FILE + * + * All configuration values of the system are stored in the cache. + * + * @param Cache $cache a new cache + * + * @return void + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + */ + public function load(Cache $cache); + /** * Sets a configuration value for system config * @@ -67,20 +82,21 @@ interface IManageConfigValues * @param string $cat The category of the configuration value * @param string $key The configuration key to set * @param mixed $value The value to store - * @param bool $autosave If true, implicit save the value * * @return bool Operation success * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function set(string $cat, string $key, $value, bool $autosave = true): bool; + public function set(string $cat, string $key, $value): bool; /** - * Save back the overridden values of the config cache + * Creates a transactional config value store, which is used to set a bunch of values at once * - * @throws ConfigPersistenceException In case the persistence layer throws errors + * It relies on the current instance, so after save(), the values of this config class will get altered at once too. + * + * @return ISetConfigValuesTransactional */ - public function save(); + public function transactional(): ISetConfigValuesTransactional; /** * Deletes the given key from the system configuration. @@ -89,14 +105,13 @@ interface IManageConfigValues * * @param string $cat The category of the configuration value * @param string $key The configuration key to delete - * @param bool $autosave If true, implicit save the value * * @return bool * * @throws ConfigPersistenceException In case the persistence layer throws errors * */ - public function delete(string $cat, string $key, bool $autosave = true): bool; + public function delete(string $cat, string $key): bool; /** * Returns the Config Cache diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactional.php b/src/Core/Config/Capability/ISetConfigValuesTransactional.php new file mode 100644 index 000000000..9c58427a0 --- /dev/null +++ b/src/Core/Config/Capability/ISetConfigValuesTransactional.php @@ -0,0 +1,84 @@ +. + * + */ + +namespace Friendica\Core\Config\Capability; + +use Friendica\Core\Config\Exception\ConfigPersistenceException; + +/** + * Interface for transactional saving of config values + * It buffers every set/delete until "save()" is called + */ +interface ISetConfigValuesTransactional +{ + /** + * Get a particular user's config variable given the category name + * ($cat) and a $key. + * + * Get a particular config value from the given category ($cat) + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * + * @return mixed Stored value or null if it does not exist + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + * + */ + public function get(string $cat, string $key); + + /** + * Sets a configuration value for system config + * + * Stores a config value ($value) in the category ($cat) under the key ($key) + * + * Note: Please do not store booleans - convert to 0/1 integer values! + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to set + * @param mixed $value The value to store + * + * @return static the current instance + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + */ + public function set(string $cat, string $key, $value): self; + + /** + * Deletes the given key from the system configuration. + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to delete + * + * @return static the current instance + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + * + */ + public function delete(string $cat, string $key): self; + + /** + * Saves the node specific config values + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + */ + public function save(): void; +} diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 7829b75ff..24f5fd3b5 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -22,6 +22,7 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; use Friendica\Core\Config\Exception\ConfigFileException; use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\Util\ConfigFileManager; @@ -61,8 +62,17 @@ class Config implements IManageConfigValues return $this->configCache; } - /** {@inheritDoc} */ - public function save() + /** {@inheritDoc} */ + public function transactional(): ISetConfigValuesTransactional + { + return new TransactionalConfig($this); + } + + /** + * Saves the current Configuration back into the data config. + * @see ConfigFileManager::CONFIG_DATA_FILE + */ + protected function save() { try { $this->configFileManager->saveData($this->configCache); @@ -84,6 +94,13 @@ class Config implements IManageConfigValues $this->configCache = $configCache; } + /** {@inheritDoc} */ + public function load(Cache $cache) + { + $this->configCache = $cache; + $this->save(); + } + /** {@inheritDoc} */ public function get(string $cat, string $key, $default_value = null) { @@ -91,26 +108,24 @@ class Config implements IManageConfigValues } /** {@inheritDoc} */ - public function set(string $cat, string $key, $value, bool $autosave = true): bool + public function set(string $cat, string $key, $value): bool { - $stored = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA); - - if ($stored && $autosave) { + if ($this->configCache->set($cat, $key, $value, Cache::SOURCE_DATA)) { $this->save(); + return true; + } else { + return false; } - - return $stored; } /** {@inheritDoc} */ - public function delete(string $cat, string $key, bool $autosave = true): bool + public function delete(string $cat, string $key): bool { - $removed = $this->configCache->delete($cat, $key); - - if ($removed && $autosave) { + if ($this->configCache->delete($cat, $key)) { $this->save(); + return true; + } else { + return false; } - - return $removed; } } diff --git a/src/Core/Config/Model/TransactionalConfig.php b/src/Core/Config/Model/TransactionalConfig.php new file mode 100644 index 000000000..e9aa71160 --- /dev/null +++ b/src/Core/Config/Model/TransactionalConfig.php @@ -0,0 +1,89 @@ +. + * + */ + +namespace Friendica\Core\Config\Model; + +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Exception\ConfigPersistenceException; +use Friendica\Core\Config\ValueObject\Cache; + +/** + * config class, which sets values into a temporary buffer until "save()" is called + */ +class TransactionalConfig implements ISetConfigValuesTransactional +{ + /** @var IManageConfigValues */ + protected $config; + /** @var Cache */ + protected $cache; + /** @var Cache */ + protected $delCache; + + public function __construct(IManageConfigValues $config) + { + $this->config = $config; + $this->cache = new Cache(); + $this->delCache = new Cache(); + } + + /** {@inheritDoc} */ + public function get(string $cat, string $key) + { + return !$this->delCache->get($cat, $key) ? + ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) : + null; + } + + /** {@inheritDoc} */ + public function set(string $cat, string $key, $value): ISetConfigValuesTransactional + { + $this->cache->set($cat, $key, $value, Cache::SOURCE_DATA); + + return $this; + } + + + /** {@inheritDoc} */ + public function delete(string $cat, string $key): ISetConfigValuesTransactional + { + $this->cache->delete($cat, $key); + $this->delCache->set($cat, $key, 'deleted'); + + return $this; + } + + /** {@inheritDoc} */ + public function save(): void + { + try { + $newCache = $this->config->getCache()->merge($this->cache); + $newCache = $newCache->diff($this->delCache); + $this->config->load($newCache); + + // flush current cache + $this->cache = new Cache(); + $this->delCache = new Cache(); + } catch (\Exception $e) { + throw new ConfigPersistenceException('Cannot save config', $e); + } + } +} diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index a074414bf..305c00d33 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -279,4 +279,71 @@ class Cache return $return; } + + /** + * Merges a new Cache into the existing one and returns the merged Cache + * + * @param Cache $cache The cache, which should get merged into this Cache + * + * @return Cache The merged Cache + */ + public function merge(Cache $cache): Cache + { + $newConfig = $this->config; + $newSource = $this->source; + + $categories = array_keys($cache->config); + + foreach ($categories as $category) { + if (is_array($cache->config[$category])) { + $keys = array_keys($cache->config[$category]); + + foreach ($keys as $key) { + $newConfig[$category][$key] = $cache->config[$category][$key]; + $newSource[$category][$key] = $cache->source[$category][$key]; + } + } + } + + $newCache = new Cache(); + $newCache->config = $newConfig; + $newCache->source = $newSource; + + return $newCache; + } + + + /** + * Diffs a new Cache into the existing one and returns the diffed Cache + * + * @param Cache $cache The cache, which should get deleted for the current Cache + * + * @return Cache The diffed Cache + */ + public function diff(Cache $cache): Cache + { + $newConfig = $this->config; + $newSource = $this->source; + + $categories = array_keys($cache->config); + + foreach ($categories as $category) { + if (is_array($cache->config[$category])) { + $keys = array_keys($cache->config[$category]); + + foreach ($keys as $key) { + if (!is_null($newConfig[$category][$key] ?? null)) { + unset($newConfig[$category][$key]); + unset($newSource[$category][$key]); + } + } + } + } + + $newCache = new Cache(); + $newCache->config = $newConfig; + $newCache->source = $newSource; + + return $newCache; + } } diff --git a/src/Core/Update.php b/src/Core/Update.php index 9a2ebe1bb..a02645783 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -160,9 +160,10 @@ class Update Logger::warning('Pre update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); return $r; } else { Logger::notice('Pre update executed.', ['version' => $version]); @@ -182,9 +183,10 @@ class Update Logger::error('Update ERROR.', ['from' => $stored, 'to' => $current, 'retval' => $retval]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); return $retval; } else { Logger::notice('Database structure update finished.', ['from' => $stored, 'to' => $current]); @@ -200,9 +202,10 @@ class Update Logger::warning('Post update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); return $r; } else { DI::config()->set('system', 'build', $version); @@ -213,9 +216,10 @@ class Update DI::config()->set('system', 'build', $current); DI::config()->set('system', 'update', Update::SUCCESS); DI::lock()->release('dbupdate'); - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); Logger::notice('Update success.', ['from' => $stored, 'to' => $current]); if ($sendMail) { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index e3af408b9..ed2a5e30e 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -182,9 +182,10 @@ class DBStructure $status = self::update($verbose, true); if ($enable_maintenance_mode) { - DI::config()->set('system', 'maintenance', false, false); - DI::config()->delete('system', 'maintenance_reason', false); - DI::config()->save(); + DI::config()->transactional() + ->set('system', 'maintenance', false) + ->delete('system', 'maintenance_reason') + ->save(); } return $status; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index f572d3e72..50a7ee868 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -48,8 +48,6 @@ class Site extends BaseAdmin self::checkFormSecurityTokenRedirectOnError('/admin/site', 'admin_site'); - $a = DI::app(); - if (!empty($_POST['republish_directory'])) { Worker::add(Worker::PRIORITY_LOW, 'Directory'); return; @@ -146,9 +144,11 @@ class Site extends BaseAdmin $relay_user_tags = !empty($_POST['relay_user_tags']); $active_panel = (!empty($_POST['active_panel']) ? "#" . trim($_POST['active_panel']) : ''); + $transactionConfig = DI::config()->transactional(); + // Has the directory url changed? If yes, then resubmit the existing profiles there if ($global_directory != DI::config()->get('system', 'directory') && ($global_directory != '')) { - DI::config()->set('system', 'directory', $global_directory, false); + $transactionConfig->set('system', 'directory', $global_directory); Worker::add(Worker::PRIORITY_LOW, 'Directory'); } @@ -194,133 +194,133 @@ class Site extends BaseAdmin ); } } - DI::config()->set('system', 'ssl_policy' , $ssl_policy, false); - DI::config()->set('system', 'maxloadavg' , $maxloadavg, false); - DI::config()->set('system', 'min_memory' , $min_memory, false); - DI::config()->set('system', 'optimize_tables' , $optimize_tables, false); - DI::config()->set('system', 'contact_discovery' , $contact_discovery, false); - DI::config()->set('system', 'synchronize_directory' , $synchronize_directory, false); - DI::config()->set('system', 'poco_requery_days' , $poco_requery_days, false); - DI::config()->set('system', 'poco_discovery' , $poco_discovery, false); - DI::config()->set('system', 'poco_local_search' , $poco_local_search, false); - DI::config()->set('system', 'nodeinfo' , $nodeinfo, false); - DI::config()->set('config', 'sitename' , $sitename, false); - DI::config()->set('config', 'sender_email' , $sender_email, false); - DI::config()->set('system', 'suppress_tags' , $suppress_tags, false); - DI::config()->set('system', 'shortcut_icon' , $shortcut_icon, false); - DI::config()->set('system', 'touch_icon' , $touch_icon, false); + $transactionConfig->set('system', 'ssl_policy' , $ssl_policy); + $transactionConfig->set('system', 'maxloadavg' , $maxloadavg); + $transactionConfig->set('system', 'min_memory' , $min_memory); + $transactionConfig->set('system', 'optimize_tables' , $optimize_tables); + $transactionConfig->set('system', 'contact_discovery' , $contact_discovery); + $transactionConfig->set('system', 'synchronize_directory' , $synchronize_directory); + $transactionConfig->set('system', 'poco_requery_days' , $poco_requery_days); + $transactionConfig->set('system', 'poco_discovery' , $poco_discovery); + $transactionConfig->set('system', 'poco_local_search' , $poco_local_search); + $transactionConfig->set('system', 'nodeinfo' , $nodeinfo); + $transactionConfig->set('config', 'sitename' , $sitename); + $transactionConfig->set('config', 'sender_email' , $sender_email); + $transactionConfig->set('system', 'suppress_tags' , $suppress_tags); + $transactionConfig->set('system', 'shortcut_icon' , $shortcut_icon); + $transactionConfig->set('system', 'touch_icon' , $touch_icon); if ($banner == "") { - DI::config()->delete('system', 'banner', false); + $transactionConfig->delete('system', 'banner'); } else { - DI::config()->set('system', 'banner', $banner, false); + $transactionConfig->set('system', 'banner', $banner); } if (empty($email_banner)) { - DI::config()->delete('system', 'email_banner', false); + $transactionConfig->delete('system', 'email_banner'); } else { - DI::config()->set('system', 'email_banner', $email_banner, false); + $transactionConfig->set('system', 'email_banner', $email_banner); } if (empty($additional_info)) { - DI::config()->delete('config', 'info', false); + $transactionConfig->delete('config', 'info'); } else { - DI::config()->set('config', 'info', $additional_info, false); + $transactionConfig->set('config', 'info', $additional_info); } - DI::config()->set('system', 'language', $language, false); - DI::config()->set('system', 'theme', $theme, false); + $transactionConfig->set('system', 'language', $language); + $transactionConfig->set('system', 'theme', $theme); Theme::install($theme); if ($theme_mobile == '---') { - DI::config()->delete('system', 'mobile-theme', false); + $transactionConfig->delete('system', 'mobile-theme'); } else { - DI::config()->set('system', 'mobile-theme', $theme_mobile, false); + $transactionConfig->set('system', 'mobile-theme', $theme_mobile); } if ($singleuser == '---') { - DI::config()->delete('system', 'singleuser', false); + $transactionConfig->delete('system', 'singleuser'); } else { - DI::config()->set('system', 'singleuser', $singleuser, false); + $transactionConfig->set('system', 'singleuser', $singleuser); } if (preg_match('/\d+(?:\s*[kmg])?/i', $maximagesize)) { - DI::config()->set('system', 'maximagesize', $maximagesize, false); + $transactionConfig->set('system', 'maximagesize', $maximagesize); } else { DI::sysmsg()->addNotice(DI::l10n()->t('%s is no valid input for maximum image size', $maximagesize)); } - DI::config()->set('system', 'max_image_length' , $maximagelength, false); - DI::config()->set('system', 'jpeg_quality' , $jpegimagequality, false); + $transactionConfig->set('system', 'max_image_length' , $maximagelength); + $transactionConfig->set('system', 'jpeg_quality' , $jpegimagequality); - DI::config()->set('config', 'register_policy' , $register_policy, false); - DI::config()->set('system', 'max_daily_registrations', $daily_registrations, false); - DI::config()->set('system', 'account_abandon_days' , $abandon_days, false); - DI::config()->set('config', 'register_text' , $register_text, false); - DI::config()->set('system', 'allowed_sites' , $allowed_sites, false); - DI::config()->set('system', 'allowed_email' , $allowed_email, false); - DI::config()->set('system', 'forbidden_nicknames' , $forbidden_nicknames, false); - DI::config()->set('system', 'system_actor_name' , $system_actor_name, false); - DI::config()->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content, false); - DI::config()->set('system', 'allowed_oembed' , $allowed_oembed, false); - DI::config()->set('system', 'block_public' , $block_public, false); - DI::config()->set('system', 'publish_all' , $force_publish, false); - DI::config()->set('system', 'newuser_private' , $newuser_private, false); - DI::config()->set('system', 'enotify_no_content' , $enotify_no_content, false); - DI::config()->set('system', 'disable_embedded' , $disable_embedded, false); - DI::config()->set('system', 'allow_users_remote_self', $allow_users_remote_self, false); - DI::config()->set('system', 'explicit_content' , $explicit_content, false); - DI::config()->set('system', 'proxify_content' , $proxify_content, false); - DI::config()->set('system', 'cache_contact_avatar' , $cache_contact_avatar, false); - DI::config()->set('system', 'check_new_version_url' , $check_new_version_url, false); + $transactionConfig->set('config', 'register_policy' , $register_policy); + $transactionConfig->set('system', 'max_daily_registrations', $daily_registrations); + $transactionConfig->set('system', 'account_abandon_days' , $abandon_days); + $transactionConfig->set('config', 'register_text' , $register_text); + $transactionConfig->set('system', 'allowed_sites' , $allowed_sites); + $transactionConfig->set('system', 'allowed_email' , $allowed_email); + $transactionConfig->set('system', 'forbidden_nicknames' , $forbidden_nicknames); + $transactionConfig->set('system', 'system_actor_name' , $system_actor_name); + $transactionConfig->set('system', 'no_oembed_rich_content' , $no_oembed_rich_content); + $transactionConfig->set('system', 'allowed_oembed' , $allowed_oembed); + $transactionConfig->set('system', 'block_public' , $block_public); + $transactionConfig->set('system', 'publish_all' , $force_publish); + $transactionConfig->set('system', 'newuser_private' , $newuser_private); + $transactionConfig->set('system', 'enotify_no_content' , $enotify_no_content); + $transactionConfig->set('system', 'disable_embedded' , $disable_embedded); + $transactionConfig->set('system', 'allow_users_remote_self', $allow_users_remote_self); + $transactionConfig->set('system', 'explicit_content' , $explicit_content); + $transactionConfig->set('system', 'proxify_content' , $proxify_content); + $transactionConfig->set('system', 'cache_contact_avatar' , $cache_contact_avatar); + $transactionConfig->set('system', 'check_new_version_url' , $check_new_version_url); - DI::config()->set('system', 'block_extended_register', !$enable_multi_reg, false); - DI::config()->set('system', 'no_openid' , !$enable_openid, false); - DI::config()->set('system', 'no_regfullname' , !$enable_regfullname, false); - DI::config()->set('system', 'register_notification' , $register_notification, false); - DI::config()->set('system', 'community_page_style' , $community_page_style, false); - DI::config()->set('system', 'max_author_posts_community_page', $max_author_posts_community_page, false); - DI::config()->set('system', 'verifyssl' , $verifyssl, false); - DI::config()->set('system', 'proxyuser' , $proxyuser, false); - DI::config()->set('system', 'proxy' , $proxy, false); - DI::config()->set('system', 'curl_timeout' , $timeout, false); - DI::config()->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open'), false); - DI::config()->set('system', 'ostatus_disabled' , !$ostatus_enabled, false); - DI::config()->set('system', 'diaspora_enabled' , $diaspora_enabled, false); + $transactionConfig->set('system', 'block_extended_register', !$enable_multi_reg); + $transactionConfig->set('system', 'no_openid' , !$enable_openid); + $transactionConfig->set('system', 'no_regfullname' , !$enable_regfullname); + $transactionConfig->set('system', 'register_notification' , $register_notification); + $transactionConfig->set('system', 'community_page_style' , $community_page_style); + $transactionConfig->set('system', 'max_author_posts_community_page', $max_author_posts_community_page); + $transactionConfig->set('system', 'verifyssl' , $verifyssl); + $transactionConfig->set('system', 'proxyuser' , $proxyuser); + $transactionConfig->set('system', 'proxy' , $proxy); + $transactionConfig->set('system', 'curl_timeout' , $timeout); + $transactionConfig->set('system', 'imap_disabled' , !$mail_enabled && function_exists('imap_open')); + $transactionConfig->set('system', 'ostatus_disabled' , !$ostatus_enabled); + $transactionConfig->set('system', 'diaspora_enabled' , $diaspora_enabled); - DI::config()->set('config', 'private_addons' , $private_addons, false); + $transactionConfig->set('config', 'private_addons' , $private_addons); - DI::config()->set('system', 'force_ssl' , $force_ssl, false); - DI::config()->set('system', 'hide_help' , !$show_help, false); + $transactionConfig->set('system', 'force_ssl' , $force_ssl); + $transactionConfig->set('system', 'hide_help' , !$show_help); - DI::config()->set('system', 'dbclean' , $dbclean, false); - DI::config()->set('system', 'dbclean-expire-days' , $dbclean_expire_days, false); - DI::config()->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv, false); + $transactionConfig->set('system', 'dbclean' , $dbclean); + $transactionConfig->set('system', 'dbclean-expire-days' , $dbclean_expire_days); + $transactionConfig->set('system', 'dbclean_expire_conversation', $dbclean_expire_conv); if ($dbclean_unclaimed == 0) { $dbclean_unclaimed = $dbclean_expire_days; } - DI::config()->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed, false); + $transactionConfig->set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed); - DI::config()->set('system', 'max_comments', $max_comments, false); - DI::config()->set('system', 'max_display_comments', $max_display_comments, false); + $transactionConfig->set('system', 'max_comments', $max_comments); + $transactionConfig->set('system', 'max_display_comments', $max_display_comments); if ($temppath != '') { $temppath = BasePath::getRealPath($temppath); } - DI::config()->set('system', 'temppath', $temppath, false); + $transactionConfig->set('system', 'temppath', $temppath); - DI::config()->set('system', 'only_tag_search' , $only_tag_search, false); - DI::config()->set('system', 'compute_group_counts', $compute_group_counts, false); + $transactionConfig->set('system', 'only_tag_search' , $only_tag_search); + $transactionConfig->set('system', 'compute_group_counts', $compute_group_counts); - DI::config()->set('system', 'worker_queues' , $worker_queues, false); - DI::config()->set('system', 'worker_fastlane' , $worker_fastlane, false); + $transactionConfig->set('system', 'worker_queues' , $worker_queues); + $transactionConfig->set('system', 'worker_fastlane' , $worker_fastlane); - DI::config()->set('system', 'relay_directly' , $relay_directly, false); - DI::config()->set('system', 'relay_scope' , $relay_scope, false); - DI::config()->set('system', 'relay_server_tags', $relay_server_tags, false); - DI::config()->set('system', 'relay_deny_tags' , $relay_deny_tags, false); - DI::config()->set('system', 'relay_user_tags' , $relay_user_tags, false); + $transactionConfig->set('system', 'relay_directly' , $relay_directly); + $transactionConfig->set('system', 'relay_scope' , $relay_scope); + $transactionConfig->set('system', 'relay_server_tags', $relay_server_tags); + $transactionConfig->set('system', 'relay_deny_tags' , $relay_deny_tags); + $transactionConfig->set('system', 'relay_user_tags' , $relay_user_tags); - DI::config()->save(); + $transactionConfig->save(); DI::baseUrl()->redirect('admin/site' . $active_panel); } @@ -334,8 +334,8 @@ class Site extends BaseAdmin if (DI::config()->get('system', 'directory_submit_url') && !DI::config()->get('system', 'directory')) { - DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url')), false); - DI::config()->delete('system', 'directory_submit_url', false); + DI::config()->set('system', 'directory', dirname(DI::config()->get('system', 'directory_submit_url'))); + DI::config()->delete('system', 'directory_submit_url'); } /* Installed themes */ diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 8b2c24da3..9d72774c4 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -358,4 +358,52 @@ class CacheTest extends MockedTest $this->assertEquals(['system' => ['test_2' => 'with_data']], $configCache->getDataBySource(Cache::SOURCE_DATA)); $this->assertEquals($data, $configCache->getDataBySource(Cache::SOURCE_FILE)); } + + /** + * @dataProvider dataTests + */ + public function testMerge($data) + { + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_FILE); + + $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); + $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); + + $newCache = new Cache(); + $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); + $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); + + $mergedCache = $configCache->merge($newCache); + + self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); + self::assertEquals('override it again', $mergedCache->get('config', 'test_override')); + self::assertEquals('new value', $mergedCache->get('system', 'test_3')); + } + + /** + * @dataProvider dataTests + */ + public function testDiff($data) + { + $configCache = new Cache(); + $configCache->load($data, Cache::SOURCE_FILE); + + $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); + $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); + + $newCache = new Cache(); + $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); + $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); + + $mergedCache = $configCache->diff($newCache); + + print_r($mergedCache); + + self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); + // existing entry was dropped + self::assertNull($mergedCache->get('config', 'test_override')); + // the newCache entry wasn't set, because we Diff + self::assertNull($mergedCache->get('system', 'test_3')); + } } diff --git a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php index e8443611f..aed55f429 100644 --- a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php @@ -21,8 +21,8 @@ namespace Friendica\Test\src\Core\Config\Cache; -use Friendica\Core\Config\Cache; use Friendica\Core\Config\Factory\Config; +use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; use Friendica\Core\Config\Util\ConfigFileManager; @@ -51,7 +51,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -77,7 +77,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); } @@ -106,7 +106,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -143,7 +143,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -179,7 +179,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -270,7 +270,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -304,7 +304,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -338,7 +338,7 @@ class ConfigFileLoaderTest extends MockedTest $this->root->url() . DIRECTORY_SEPARATOR . Config::CONFIG_DIR, $this->root->url() . DIRECTORY_SEPARATOR . Config::STATIC_DIR ); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -354,7 +354,7 @@ class ConfigFileLoaderTest extends MockedTest $this->delConfigFile('local.config.php'); $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => '/a/wrong/dir/']); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -380,7 +380,7 @@ class ConfigFileLoaderTest extends MockedTest ->setContent(file_get_contents($fileDir . 'B.config.php')); $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); @@ -403,18 +403,18 @@ class ConfigFileLoaderTest extends MockedTest ->setContent(file_get_contents($fileDir . 'B.config.php')); $configFileLoader = (new Config())->createConfigFileLoader($this->root->url(), ['FRIENDICA_CONFIG_DIR' => $this->root->getChild('config2')->url()]); - $configCache = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache = new Cache(); $configFileLoader->setupCache($configCache); // overwrite some data and save it back to the config file - $configCache->set('system', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); - $configCache->set('config', 'test', 'it', \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); - $configCache->set('system', 'test_2', 2, \Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA); + $configCache->set('system', 'test', 'it', Cache::SOURCE_DATA); + $configCache->set('config', 'test', 'it', Cache::SOURCE_DATA); + $configCache->set('system', 'test_2', 2, Cache::SOURCE_DATA); $configFileLoader->saveData($configCache); // Reload the configCache with the new values - $configCache2 = new \Friendica\Core\Config\ValueObject\Cache(); + $configCache2 = new Cache(); $configFileLoader->setupCache($configCache2); self::assertEquals($configCache, $configCache2); @@ -425,6 +425,6 @@ class ConfigFileLoaderTest extends MockedTest ], 'config' => [ 'test' => 'it' - ]], $configCache2->getDataBySource(\Friendica\Core\Config\ValueObject\Cache::SOURCE_DATA)); + ]], $configCache2->getDataBySource(Cache::SOURCE_DATA)); } } diff --git a/tests/src/Core/Config/TransactionalConfigTest.php b/tests/src/Core/Config/TransactionalConfigTest.php new file mode 100644 index 000000000..b42fee97c --- /dev/null +++ b/tests/src/Core/Config/TransactionalConfigTest.php @@ -0,0 +1,110 @@ +setUpVfsDir(); + + $this->configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/'); + } + + public function dataTests(): array + { + return [ + 'default' => [ + 'data' => include dirname(__FILE__, 4) . '/datasets/B.node.config.php', + ] + ]; + } + + public function testInstance() + { + $config = new Config($this->configFileManager, new Cache()); + $transactionalConfig = new TransactionalConfig($config); + + self::assertInstanceOf(ISetConfigValuesTransactional::class, $transactionalConfig); + self::assertInstanceOf(TransactionalConfig::class, $transactionalConfig); + } + + public function testTransactionalConfig() + { + $config = new Config($this->configFileManager, new Cache()); + $config->set('config', 'key1', 'value1'); + $config->set('system', 'key2', 'value2'); + $config->set('system', 'keyDel', 'valueDel'); + $config->set('delete', 'keyDel', 'catDel'); + + $transactionalConfig = new TransactionalConfig($config); + self::assertEquals('value1', $transactionalConfig->get('config', 'key1')); + self::assertEquals('value2', $transactionalConfig->get('system', 'key2')); + self::assertEquals('valueDel', $transactionalConfig->get('system', 'keyDel')); + self::assertEquals('catDel', $transactionalConfig->get('delete', 'keyDel')); + // the config file knows it as well immediately + $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; + self::assertEquals('value1', $tempData['config']['key1'] ?? null); + self::assertEquals('value2', $tempData['system']['key2'] ?? null); + + // new key-value + $transactionalConfig->set('transaction', 'key3', 'value3'); + // overwrite key-value + $transactionalConfig->set('config', 'key1', 'changedValue1'); + // delete key-value + $transactionalConfig->delete('system', 'keyDel'); + // delete last key of category - so the category is gone + $transactionalConfig->delete('delete', 'keyDel'); + + // The main config still doesn't know about the change + self::assertNull($config->get('transaction', 'key3')); + self::assertEquals('value1', $config->get('config', 'key1')); + self::assertEquals('valueDel', $config->get('system', 'keyDel')); + self::assertEquals('catDel', $config->get('delete', 'keyDel')); + // but the transaction config of course knows it + self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); + self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); + self::assertNull($transactionalConfig->get('system', 'keyDel')); + self::assertNull($transactionalConfig->get('delete', 'keyDel')); + // The config file still doesn't know it either + $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; + self::assertEquals('value1', $tempData['config']['key1'] ?? null); + self::assertEquals('value2', $tempData['system']['key2'] ?? null); + self::assertEquals('catDel', $tempData['delete']['keyDel'] ?? null); + self::assertNull($tempData['transaction']['key3'] ?? null); + + // save it back! + $transactionalConfig->save(); + + // Now every config and file knows the change + self::assertEquals('changedValue1', $config->get('config', 'key1')); + self::assertEquals('value3', $config->get('transaction', 'key3')); + self::assertNull($config->get('system', 'keyDel')); + self::assertNull($config->get('delete', 'keyDel')); + self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); + self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); + self::assertNull($transactionalConfig->get('system', 'keyDel')); + $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; + self::assertEquals('changedValue1', $tempData['config']['key1'] ?? null); + self::assertEquals('value2', $tempData['system']['key2'] ?? null); + self::assertEquals('value3', $tempData['transaction']['key3'] ?? null); + self::assertNull($tempData['system']['keyDel'] ?? null); + self::assertNull($tempData['delete']['keyDel'] ?? null); + // the whole category should be gone + self::assertNull($tempData['delete'] ?? null); + } +} diff --git a/update.php b/update.php index ad33dde01..7ad6e432f 100644 --- a/update.php +++ b/update.php @@ -1184,11 +1184,13 @@ function update_1508() { $config = DBA::selectToArray('config'); + $newConfig = DI::config()->transactional(); + foreach ($config as $entry) { - DI::config()->set($entry['cat'], $entry['k'], $entry['v'], false); + $newConfig->set($entry['cat'], $entry['k'], $entry['v']); } - DI::config()->save(); + $newConfig->save(); DBA::e("DELETE FROM `config`"); } From dd88d193b9e01f4518a21d9cd1d25b7575677c39 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 15:36:36 +0100 Subject: [PATCH 15/19] Escape single quotes and backslashes --- src/Core/Config/Util/ConfigFileTransformer.php | 2 +- tests/datasets/config/B.node.config.php | 1 + ...figFileLoaderTest.php => ConfigFileManagerTest.php} | 10 ++++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) rename tests/src/Core/Config/Cache/{ConfigFileLoaderTest.php => ConfigFileManagerTest.php} (98%) diff --git a/src/Core/Config/Util/ConfigFileTransformer.php b/src/Core/Config/Util/ConfigFileTransformer.php index 9b80991af..282714df2 100644 --- a/src/Core/Config/Util/ConfigFileTransformer.php +++ b/src/Core/Config/Util/ConfigFileTransformer.php @@ -75,7 +75,7 @@ class ConfigFileTransformer } elseif (is_numeric($value)) { $string .= $value . ","; } else { - $string .= sprintf('\'%s\',', $value); + $string .= sprintf('\'%s\',', addcslashes($value, '\'\\')); } $string .= PHP_EOL; diff --git a/tests/datasets/config/B.node.config.php b/tests/datasets/config/B.node.config.php index 94b2e3f12..499e092a4 100644 --- a/tests/datasets/config/B.node.config.php +++ b/tests/datasets/config/B.node.config.php @@ -34,5 +34,6 @@ return [ 'theme' => 'frio', 'int' => 23, 'float' => 2.5, + 'with special chars' => 'I can\'t follow this "$&§%"$%§$%&\'[),', ], ]; diff --git a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php similarity index 98% rename from tests/src/Core/Config/Cache/ConfigFileLoaderTest.php rename to tests/src/Core/Config/Cache/ConfigFileManagerTest.php index aed55f429..99049426b 100644 --- a/tests/src/Core/Config/Cache/ConfigFileLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigFileManagerTest.php @@ -28,7 +28,7 @@ use Friendica\Test\Util\VFSTrait; use Friendica\Core\Config\Util\ConfigFileManager; use org\bovigo\vfs\vfsStream; -class ConfigFileLoaderTest extends MockedTest +class ConfigFileManagerTest extends MockedTest { use VFSTrait; @@ -407,10 +407,13 @@ class ConfigFileLoaderTest extends MockedTest $configFileLoader->setupCache($configCache); + $specialChars = '!"§$%&/()(/&%$\'>set('system', 'test', 'it', Cache::SOURCE_DATA); $configCache->set('config', 'test', 'it', Cache::SOURCE_DATA); $configCache->set('system', 'test_2', 2, Cache::SOURCE_DATA); + $configCache->set('special_chars', 'special', $specialChars, Cache::SOURCE_DATA); $configFileLoader->saveData($configCache); // Reload the configCache with the new values @@ -424,7 +427,10 @@ class ConfigFileLoaderTest extends MockedTest 'test_2' => 2 ], 'config' => [ - 'test' => 'it' + 'test' => 'it', + ], + 'special_chars' => [ + 'special' => $specialChars, ]], $configCache2->getDataBySource(Cache::SOURCE_DATA)); } } From 072464119a353f4f0cbfb03b5e6253df0e2e4dde Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 15:40:57 +0100 Subject: [PATCH 16/19] Make PHP-CS happy --- tests/src/Core/Config/TransactionalConfigTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/src/Core/Config/TransactionalConfigTest.php b/tests/src/Core/Config/TransactionalConfigTest.php index b42fee97c..e2fdc633c 100644 --- a/tests/src/Core/Config/TransactionalConfigTest.php +++ b/tests/src/Core/Config/TransactionalConfigTest.php @@ -1,6 +1,7 @@ configFileManager, new Cache()); + $config = new Config($this->configFileManager, new Cache()); $transactionalConfig = new TransactionalConfig($config); self::assertInstanceOf(ISetConfigValuesTransactional::class, $transactionalConfig); From b439df892a3c57617365cd02b1d239fdb63cc550 Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 17:24:05 +0100 Subject: [PATCH 17/19] Apply suggestions --- src/Console/Maintenance.php | 4 +- src/Console/Relocate.php | 11 ++-- .../Config/Capability/IManageConfigValues.php | 4 +- ...hp => ISetConfigValuesTransactionally.php} | 6 +-- src/Core/Config/Model/Config.php | 6 +-- ...tionalConfig.php => ConfigTransaction.php} | 12 ++--- src/Core/Config/ValueObject/Cache.php | 6 +-- src/Core/Update.php | 16 +++--- src/Database/DBStructure.php | 4 +- src/Module/Admin/Site.php | 4 +- tests/src/Core/Config/Cache/CacheTest.php | 4 ++ ...nfigTest.php => ConfigTransactionTest.php} | 50 +++++++++---------- update.php | 6 +-- view/theme/frio/style.php | 2 +- 14 files changed, 69 insertions(+), 66 deletions(-) rename src/Core/Config/Capability/{ISetConfigValuesTransactional.php => ISetConfigValuesTransactionally.php} (95%) rename src/Core/Config/Model/{TransactionalConfig.php => ConfigTransaction.php} (90%) rename tests/src/Core/Config/{TransactionalConfigTest.php => ConfigTransactionTest.php} (66%) diff --git a/src/Console/Maintenance.php b/src/Console/Maintenance.php index 6a11eb2bb..076b89db8 100644 --- a/src/Console/Maintenance.php +++ b/src/Console/Maintenance.php @@ -100,7 +100,7 @@ HELP; $enabled = intval($this->getArgument(0)); - $transactionConfig = $this->config->transactional(); + $transactionConfig = $this->config->beginTransaction(); $transactionConfig->set('system', 'maintenance', $enabled); @@ -112,7 +112,7 @@ HELP; $transactionConfig->delete('system', 'maintenance_reason'); } - $transactionConfig->save(); + $transactionConfig->commit(); if ($enabled) { $mode_str = "maintenance mode"; diff --git a/src/Console/Relocate.php b/src/Console/Relocate.php index 7a2ef1d07..c63434cbb 100644 --- a/src/Console/Relocate.php +++ b/src/Console/Relocate.php @@ -101,9 +101,10 @@ HELP; $old_host = str_replace('http://', '@', Strings::normaliseLink($old_url)); $this->out('Entering maintenance mode'); - $this->config->set('system', 'maintenance', true, false); - $this->config->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url, false); - + $this->config->beginTransaction() + ->set('system', 'maintenance', true) + ->set('system', 'maintenance_reason', 'Relocating node to ' . $new_url) + ->commit(); try { if (!$this->database->transaction()) { throw new \Exception('Unable to start a transaction, please retry later.'); @@ -189,10 +190,10 @@ HELP; return 1; } finally { $this->out('Leaving maintenance mode'); - $this->config->transactional() + $this->config->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); } // send relocate diff --git a/src/Core/Config/Capability/IManageConfigValues.php b/src/Core/Config/Capability/IManageConfigValues.php index 42ebea000..715887ddf 100644 --- a/src/Core/Config/Capability/IManageConfigValues.php +++ b/src/Core/Config/Capability/IManageConfigValues.php @@ -94,9 +94,9 @@ interface IManageConfigValues * * It relies on the current instance, so after save(), the values of this config class will get altered at once too. * - * @return ISetConfigValuesTransactional + * @return ISetConfigValuesTransactionally */ - public function transactional(): ISetConfigValuesTransactional; + public function beginTransaction(): ISetConfigValuesTransactionally; /** * Deletes the given key from the system configuration. diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactional.php b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php similarity index 95% rename from src/Core/Config/Capability/ISetConfigValuesTransactional.php rename to src/Core/Config/Capability/ISetConfigValuesTransactionally.php index 9c58427a0..ae193f2ce 100644 --- a/src/Core/Config/Capability/ISetConfigValuesTransactional.php +++ b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php @@ -27,7 +27,7 @@ use Friendica\Core\Config\Exception\ConfigPersistenceException; * Interface for transactional saving of config values * It buffers every set/delete until "save()" is called */ -interface ISetConfigValuesTransactional +interface ISetConfigValuesTransactionally { /** * Get a particular user's config variable given the category name @@ -76,9 +76,9 @@ interface ISetConfigValuesTransactional public function delete(string $cat, string $key): self; /** - * Saves the node specific config values + * Commits the changes of the current transaction * * @throws ConfigPersistenceException In case the persistence layer throws errors */ - public function save(): void; + public function commit(): void; } diff --git a/src/Core/Config/Model/Config.php b/src/Core/Config/Model/Config.php index 24f5fd3b5..29ea6b12d 100644 --- a/src/Core/Config/Model/Config.php +++ b/src/Core/Config/Model/Config.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; use Friendica\Core\Config\Exception\ConfigFileException; use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\Util\ConfigFileManager; @@ -63,9 +63,9 @@ class Config implements IManageConfigValues } /** {@inheritDoc} */ - public function transactional(): ISetConfigValuesTransactional + public function beginTransaction(): ISetConfigValuesTransactionally { - return new TransactionalConfig($this); + return new ConfigTransaction($this); } /** diff --git a/src/Core/Config/Model/TransactionalConfig.php b/src/Core/Config/Model/ConfigTransaction.php similarity index 90% rename from src/Core/Config/Model/TransactionalConfig.php rename to src/Core/Config/Model/ConfigTransaction.php index e9aa71160..7ec5784ad 100644 --- a/src/Core/Config/Model/TransactionalConfig.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -22,14 +22,14 @@ namespace Friendica\Core\Config\Model; use Friendica\Core\Config\Capability\IManageConfigValues; -use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; use Friendica\Core\Config\Exception\ConfigPersistenceException; use Friendica\Core\Config\ValueObject\Cache; /** - * config class, which sets values into a temporary buffer until "save()" is called + * Transaction class for configurations, which sets values into a temporary buffer until "save()" is called */ -class TransactionalConfig implements ISetConfigValuesTransactional +class ConfigTransaction implements ISetConfigValuesTransactionally { /** @var IManageConfigValues */ protected $config; @@ -54,7 +54,7 @@ class TransactionalConfig implements ISetConfigValuesTransactional } /** {@inheritDoc} */ - public function set(string $cat, string $key, $value): ISetConfigValuesTransactional + public function set(string $cat, string $key, $value): ISetConfigValuesTransactionally { $this->cache->set($cat, $key, $value, Cache::SOURCE_DATA); @@ -63,7 +63,7 @@ class TransactionalConfig implements ISetConfigValuesTransactional /** {@inheritDoc} */ - public function delete(string $cat, string $key): ISetConfigValuesTransactional + public function delete(string $cat, string $key): ISetConfigValuesTransactionally { $this->cache->delete($cat, $key); $this->delCache->set($cat, $key, 'deleted'); @@ -72,7 +72,7 @@ class TransactionalConfig implements ISetConfigValuesTransactional } /** {@inheritDoc} */ - public function save(): void + public function commit(): void { try { $newCache = $this->config->getCache()->merge($this->cache); diff --git a/src/Core/Config/ValueObject/Cache.php b/src/Core/Config/ValueObject/Cache.php index 305c00d33..b5af3280c 100644 --- a/src/Core/Config/ValueObject/Cache.php +++ b/src/Core/Config/ValueObject/Cache.php @@ -332,10 +332,8 @@ class Cache $keys = array_keys($cache->config[$category]); foreach ($keys as $key) { - if (!is_null($newConfig[$category][$key] ?? null)) { - unset($newConfig[$category][$key]); - unset($newSource[$category][$key]); - } + unset($newConfig[$category][$key]); + unset($newSource[$category][$key]); } } } diff --git a/src/Core/Update.php b/src/Core/Update.php index a02645783..e5ee587dc 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -160,10 +160,10 @@ class Update Logger::warning('Pre update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); return $r; } else { Logger::notice('Pre update executed.', ['version' => $version]); @@ -183,10 +183,10 @@ class Update Logger::error('Update ERROR.', ['from' => $stored, 'to' => $current, 'retval' => $retval]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); return $retval; } else { Logger::notice('Database structure update finished.', ['from' => $stored, 'to' => $current]); @@ -202,10 +202,10 @@ class Update Logger::warning('Post update failed', ['version' => $version]); DI::config()->set('system', 'update', Update::FAILED); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); return $r; } else { DI::config()->set('system', 'build', $version); @@ -216,10 +216,10 @@ class Update DI::config()->set('system', 'build', $current); DI::config()->set('system', 'update', Update::SUCCESS); DI::lock()->release('dbupdate'); - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); Logger::notice('Update success.', ['from' => $stored, 'to' => $current]); if ($sendMail) { diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index ed2a5e30e..7b284bf6d 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -182,10 +182,10 @@ class DBStructure $status = self::update($verbose, true); if ($enable_maintenance_mode) { - DI::config()->transactional() + DI::config()->beginTransaction() ->set('system', 'maintenance', false) ->delete('system', 'maintenance_reason') - ->save(); + ->commit(); } return $status; diff --git a/src/Module/Admin/Site.php b/src/Module/Admin/Site.php index 50a7ee868..39dc9f26d 100644 --- a/src/Module/Admin/Site.php +++ b/src/Module/Admin/Site.php @@ -144,7 +144,7 @@ class Site extends BaseAdmin $relay_user_tags = !empty($_POST['relay_user_tags']); $active_panel = (!empty($_POST['active_panel']) ? "#" . trim($_POST['active_panel']) : ''); - $transactionConfig = DI::config()->transactional(); + $transactionConfig = DI::config()->beginTransaction(); // Has the directory url changed? If yes, then resubmit the existing profiles there if ($global_directory != DI::config()->get('system', 'directory') && ($global_directory != '')) { @@ -320,7 +320,7 @@ class Site extends BaseAdmin $transactionConfig->set('system', 'relay_deny_tags' , $relay_deny_tags); $transactionConfig->set('system', 'relay_user_tags' , $relay_user_tags); - $transactionConfig->save(); + $transactionConfig->commit(); DI::baseUrl()->redirect('admin/site' . $active_panel); } diff --git a/tests/src/Core/Config/Cache/CacheTest.php b/tests/src/Core/Config/Cache/CacheTest.php index 9d72774c4..2db6196b7 100644 --- a/tests/src/Core/Config/Cache/CacheTest.php +++ b/tests/src/Core/Config/Cache/CacheTest.php @@ -369,16 +369,20 @@ class CacheTest extends MockedTest $configCache->set('system', 'test_2','with_data', Cache::SOURCE_DATA); $configCache->set('config', 'test_override','with_another_data', Cache::SOURCE_DATA); + $configCache->set('old_category', 'test_45','given category', Cache::SOURCE_DATA); $newCache = new Cache(); $newCache->set('config', 'test_override','override it again', Cache::SOURCE_DATA); $newCache->set('system', 'test_3','new value', Cache::SOURCE_DATA); + $newCache->set('new_category', 'test_23','added category', Cache::SOURCE_DATA); $mergedCache = $configCache->merge($newCache); self::assertEquals('with_data', $mergedCache->get('system', 'test_2')); self::assertEquals('override it again', $mergedCache->get('config', 'test_override')); self::assertEquals('new value', $mergedCache->get('system', 'test_3')); + self::assertEquals('given category', $mergedCache->get('old_category', 'test_45')); + self::assertEquals('added category', $mergedCache->get('new_category', 'test_23')); } /** diff --git a/tests/src/Core/Config/TransactionalConfigTest.php b/tests/src/Core/Config/ConfigTransactionTest.php similarity index 66% rename from tests/src/Core/Config/TransactionalConfigTest.php rename to tests/src/Core/Config/ConfigTransactionTest.php index e2fdc633c..2eec9b68f 100644 --- a/tests/src/Core/Config/TransactionalConfigTest.php +++ b/tests/src/Core/Config/ConfigTransactionTest.php @@ -2,15 +2,15 @@ namespace Friendica\Test\src\Core\Config; -use Friendica\Core\Config\Capability\ISetConfigValuesTransactional; +use Friendica\Core\Config\Capability\ISetConfigValuesTransactionally; use Friendica\Core\Config\Model\Config; -use Friendica\Core\Config\Model\TransactionalConfig; +use Friendica\Core\Config\Model\ConfigTransaction; use Friendica\Core\Config\Util\ConfigFileManager; use Friendica\Core\Config\ValueObject\Cache; use Friendica\Test\MockedTest; use Friendica\Test\Util\VFSTrait; -class TransactionalConfigTest extends MockedTest +class ConfigTransactionTest extends MockedTest { use VFSTrait; @@ -37,14 +37,14 @@ class TransactionalConfigTest extends MockedTest public function testInstance() { - $config = new Config($this->configFileManager, new Cache()); - $transactionalConfig = new TransactionalConfig($config); + $config = new Config($this->configFileManager, new Cache()); + $configTransaction = new ConfigTransaction($config); - self::assertInstanceOf(ISetConfigValuesTransactional::class, $transactionalConfig); - self::assertInstanceOf(TransactionalConfig::class, $transactionalConfig); + self::assertInstanceOf(ISetConfigValuesTransactionally::class, $configTransaction); + self::assertInstanceOf(ConfigTransaction::class, $configTransaction); } - public function testTransactionalConfig() + public function testConfigTransaction() { $config = new Config($this->configFileManager, new Cache()); $config->set('config', 'key1', 'value1'); @@ -52,24 +52,24 @@ class TransactionalConfigTest extends MockedTest $config->set('system', 'keyDel', 'valueDel'); $config->set('delete', 'keyDel', 'catDel'); - $transactionalConfig = new TransactionalConfig($config); - self::assertEquals('value1', $transactionalConfig->get('config', 'key1')); - self::assertEquals('value2', $transactionalConfig->get('system', 'key2')); - self::assertEquals('valueDel', $transactionalConfig->get('system', 'keyDel')); - self::assertEquals('catDel', $transactionalConfig->get('delete', 'keyDel')); + $configTransaction = new ConfigTransaction($config); + self::assertEquals('value1', $configTransaction->get('config', 'key1')); + self::assertEquals('value2', $configTransaction->get('system', 'key2')); + self::assertEquals('valueDel', $configTransaction->get('system', 'keyDel')); + self::assertEquals('catDel', $configTransaction->get('delete', 'keyDel')); // the config file knows it as well immediately $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('value1', $tempData['config']['key1'] ?? null); self::assertEquals('value2', $tempData['system']['key2'] ?? null); // new key-value - $transactionalConfig->set('transaction', 'key3', 'value3'); + $configTransaction->set('transaction', 'key3', 'value3'); // overwrite key-value - $transactionalConfig->set('config', 'key1', 'changedValue1'); + $configTransaction->set('config', 'key1', 'changedValue1'); // delete key-value - $transactionalConfig->delete('system', 'keyDel'); + $configTransaction->delete('system', 'keyDel'); // delete last key of category - so the category is gone - $transactionalConfig->delete('delete', 'keyDel'); + $configTransaction->delete('delete', 'keyDel'); // The main config still doesn't know about the change self::assertNull($config->get('transaction', 'key3')); @@ -77,10 +77,10 @@ class TransactionalConfigTest extends MockedTest self::assertEquals('valueDel', $config->get('system', 'keyDel')); self::assertEquals('catDel', $config->get('delete', 'keyDel')); // but the transaction config of course knows it - self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); - self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); - self::assertNull($transactionalConfig->get('system', 'keyDel')); - self::assertNull($transactionalConfig->get('delete', 'keyDel')); + self::assertEquals('value3', $configTransaction->get('transaction', 'key3')); + self::assertEquals('changedValue1', $configTransaction->get('config', 'key1')); + self::assertNull($configTransaction->get('system', 'keyDel')); + self::assertNull($configTransaction->get('delete', 'keyDel')); // The config file still doesn't know it either $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('value1', $tempData['config']['key1'] ?? null); @@ -89,16 +89,16 @@ class TransactionalConfigTest extends MockedTest self::assertNull($tempData['transaction']['key3'] ?? null); // save it back! - $transactionalConfig->save(); + $configTransaction->commit(); // Now every config and file knows the change self::assertEquals('changedValue1', $config->get('config', 'key1')); self::assertEquals('value3', $config->get('transaction', 'key3')); self::assertNull($config->get('system', 'keyDel')); self::assertNull($config->get('delete', 'keyDel')); - self::assertEquals('value3', $transactionalConfig->get('transaction', 'key3')); - self::assertEquals('changedValue1', $transactionalConfig->get('config', 'key1')); - self::assertNull($transactionalConfig->get('system', 'keyDel')); + self::assertEquals('value3', $configTransaction->get('transaction', 'key3')); + self::assertEquals('changedValue1', $configTransaction->get('config', 'key1')); + self::assertNull($configTransaction->get('system', 'keyDel')); $tempData = include $this->root->url() . '/config/' . ConfigFileManager::CONFIG_DATA_FILE; self::assertEquals('changedValue1', $tempData['config']['key1'] ?? null); self::assertEquals('value2', $tempData['system']['key2'] ?? null); diff --git a/update.php b/update.php index 7ad6e432f..1dbf78e06 100644 --- a/update.php +++ b/update.php @@ -1184,13 +1184,13 @@ function update_1508() { $config = DBA::selectToArray('config'); - $newConfig = DI::config()->transactional(); + $newConfig = DI::config()->beginTransaction(); foreach ($config as $entry) { $newConfig->set($entry['cat'], $entry['k'], $entry['v']); } - $newConfig->save(); + $newConfig->commit(); - DBA::e("DELETE FROM `config`"); + DBA::e("TRUNCATE TABLE `config`"); } diff --git a/view/theme/frio/style.php b/view/theme/frio/style.php index 8feefdc93..479793b59 100644 --- a/view/theme/frio/style.php +++ b/view/theme/frio/style.php @@ -49,7 +49,7 @@ $login_bg_color = ''; $modified = time(); if (DI::mode()->has(\Friendica\App\Mode::MAINTENANCEDISABLED)) { - DI::config()->reload('frio'); + DI::config()->reload(); // Default to hard-coded values for empty settings $scheme = DI::config()->get('frio', 'scheme', DI::config()->get('frio', 'schema')); From a46cd2fb36f57547586d6eda77128d1bdb44cf0b Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 17:26:48 +0100 Subject: [PATCH 18/19] Remove get() from config transaction interface --- .../ISetConfigValuesTransactionally.php | 16 ---------------- src/Core/Config/Model/ConfigTransaction.php | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Core/Config/Capability/ISetConfigValuesTransactionally.php b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php index ae193f2ce..501e24f73 100644 --- a/src/Core/Config/Capability/ISetConfigValuesTransactionally.php +++ b/src/Core/Config/Capability/ISetConfigValuesTransactionally.php @@ -29,22 +29,6 @@ use Friendica\Core\Config\Exception\ConfigPersistenceException; */ interface ISetConfigValuesTransactionally { - /** - * Get a particular user's config variable given the category name - * ($cat) and a $key. - * - * Get a particular config value from the given category ($cat) - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to query - * - * @return mixed Stored value or null if it does not exist - * - * @throws ConfigPersistenceException In case the persistence layer throws errors - * - */ - public function get(string $cat, string $key); - /** * Sets a configuration value for system config * diff --git a/src/Core/Config/Model/ConfigTransaction.php b/src/Core/Config/Model/ConfigTransaction.php index 7ec5784ad..d8c7d7d43 100644 --- a/src/Core/Config/Model/ConfigTransaction.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -45,8 +45,20 @@ class ConfigTransaction implements ISetConfigValuesTransactionally $this->delCache = new Cache(); } - /** {@inheritDoc} */ - public function get(string $cat, string $key) + /** + * Get a particular user's config variable given the category name + * ($cat) and a $key from the current transaction. + * + * Isn't part of the interface because of it's rare use case + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * + * @return mixed Stored value or null if it does not exist + * + * @throws ConfigPersistenceException In case the persistence layer throws errors + * + */ public function get(string $cat, string $key) { return !$this->delCache->get($cat, $key) ? ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) : From beff759c82a5afc5d26289daf3ba0d84feefb2aa Mon Sep 17 00:00:00 2001 From: Philipp Date: Tue, 3 Jan 2023 17:39:32 +0100 Subject: [PATCH 19/19] Update src/Core/Config/Model/ConfigTransaction.php Co-authored-by: Hypolite Petovan --- src/Core/Config/Model/ConfigTransaction.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Core/Config/Model/ConfigTransaction.php b/src/Core/Config/Model/ConfigTransaction.php index d8c7d7d43..296a469c0 100644 --- a/src/Core/Config/Model/ConfigTransaction.php +++ b/src/Core/Config/Model/ConfigTransaction.php @@ -58,7 +58,8 @@ class ConfigTransaction implements ISetConfigValuesTransactionally * * @throws ConfigPersistenceException In case the persistence layer throws errors * - */ public function get(string $cat, string $key) + */ + public function get(string $cat, string $key) { return !$this->delCache->get($cat, $key) ? ($this->cache->get($cat, $key) ?? $this->config->get($cat, $key)) :