diff --git a/include/api.php b/include/api.php index 6c6e6f7dcd..59a95659fb 100644 --- a/include/api.php +++ b/include/api.php @@ -4207,7 +4207,7 @@ function api_fr_photos_list($type) $r = q( "SELECT `resource-id`, MAX(scale) AS `scale`, `album`, `filename`, `type`, MAX(`created`) AS `created`, MAX(`edited`) AS `edited`, MAX(`desc`) AS `desc` FROM `photo` - WHERE `uid` = %d AND `album` != 'Contact Photos' GROUP BY `resource-id`", + WHERE `uid` = %d AND `album` != 'Contact Photos' GROUP BY `resource-id`, `album`, `filename`, `type`", intval(local_user()) ); $typetoext = [ @@ -4888,7 +4888,9 @@ function prepare_photo_data($type, $scale, $photo_id) "SELECT %s `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`, `type`, `height`, `width`, `datasize`, `profile`, `allow_cid`, `deny_cid`, `allow_gid`, `deny_gid`, MIN(`scale`) AS `minscale`, MAX(`scale`) AS `maxscale` - FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s GROUP BY `resource-id`", + FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s GROUP BY + `resource-id`, `created`, `edited`, `title`, `desc`, `album`, `filename`, + `type`, `height`, `width`, `datasize`, `profile`, `allow_cid`, `deny_cid`, `allow_gid`, `deny_gid`", $data_sql, intval(local_user()), DBA::escape($photo_id), diff --git a/src/Core/Config/Adapter/IConfigAdapter.php b/src/Core/Config/Adapter/IConfigAdapter.php deleted file mode 100644 index 892c476e7c..0000000000 --- a/src/Core/Config/Adapter/IConfigAdapter.php +++ /dev/null @@ -1,73 +0,0 @@ - - */ -interface IConfigAdapter -{ - /** - * Loads all configuration values and returns the loaded category as an array. - * - * @param string $cat The category of the configuration values to load - * - * @return array - */ - public function load($cat = "config"); - - /** - * Get a particular system-wide config variable given the category name - * ($family) and a 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 null|mixed Stored value or null if it does not exist - */ - public function get($cat, $key); - - /** - * Stores a config value ($value) in the category ($family) 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 - */ - public function set($cat, $key, $value); - - /** - * Removes the configured value from the stored cache - * and removes it from the database. - * - * @param string $cat The category of the configuration value - * @param string $key The configuration key to delete - * - * @return bool Operation success - */ - public function delete($cat, $key); - - /** - * Checks, if the current adapter is connected to the backend - * - * @return bool - */ - public function isConnected(); - - /** - * Checks, if a config key ($key) in the category ($cat) is already loaded. - * - * @param string $cat The configuration category - * @param string $key The configuration key - * - * @return bool - */ - public function isLoaded($cat, $key); -} diff --git a/src/Core/Config/Adapter/JITConfigAdapter.php b/src/Core/Config/Adapter/JITConfigAdapter.php deleted file mode 100644 index d125f7d400..0000000000 --- a/src/Core/Config/Adapter/JITConfigAdapter.php +++ /dev/null @@ -1,145 +0,0 @@ - - */ -class JITConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter -{ - private $in_db; - - /** - * {@inheritdoc} - */ - public function load($cat = "config") - { - $return = []; - - if (!$this->isConnected()) { - return $return; - } - - // We don't preload "system" anymore. - // This reduces the number of database reads a lot. - if ($cat === 'system') { - return $return; - } - - $configs = DBA::select('config', ['v', 'k'], ['cat' => $cat]); - while ($config = DBA::fetch($configs)) { - $key = $config['k']; - $value = $this->toConfigValue($config['v']); - - // The value was in the db, so don't check it again (unless you have to) - $this->in_db[$cat][$key] = true; - - // just save it in case it is set - if (isset($value)) { - $return[$key] = $value; - } - } - DBA::close($configs); - - return [$cat => $return]; - } - - /** - * {@inheritdoc} - * - * @param bool $mark if true, mark the selection of the current cat/key pair - */ - public function get($cat, $key, $mark = true) - { - if (!$this->isConnected()) { - return null; - } - - // The value got checked, so mark it to avoid checking it over and over again - if ($mark) { - $this->in_db[$cat][$key] = true; - } - - $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); - if (DBA::isResult($config)) { - $value = $this->toConfigValue($config['v']); - - // just return it in case it is set - if (isset($value)) { - return $value; - } - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function set($cat, $key, $value) - { - 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, false); - - if (!isset($this->in_db[$cat])) { - $this->in_db[$cat] = []; - } - if (!isset($this->in_db[$cat][$key])) { - $this->in_db[$cat][$key] = false; - } - - if (isset($stored_value) && ($stored_value === $compare_value) && $this->in_db[$cat][$key]) { - return true; - } - - $dbvalue = $this->toDbValue($value); - - $result = DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); - - $this->in_db[$cat][$key] = $result; - - return $result; - } - - /** - * {@inheritdoc} - */ - public function delete($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - if (isset($this->cache[$cat][$key])) { - unset($this->in_db[$cat][$key]); - } - - $result = DBA::delete('config', ['cat' => $cat, 'k' => $key]); - - return $result; - } - - /** - * {@inheritdoc} - */ - public function isLoaded($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return (isset($this->in_db[$cat][$key])) && $this->in_db[$cat][$key]; - } -} diff --git a/src/Core/Config/Adapter/PreloadConfigAdapter.php b/src/Core/Config/Adapter/PreloadConfigAdapter.php deleted file mode 100644 index c691c88bc5..0000000000 --- a/src/Core/Config/Adapter/PreloadConfigAdapter.php +++ /dev/null @@ -1,115 +0,0 @@ - - */ -class PreloadConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter -{ - private $config_loaded = false; - - /** - * {@inheritdoc} - */ - public function load($cat = 'config') - { - $return = []; - - if (!$this->isConnected()) { - return $return; - } - - if ($this->config_loaded) { - return $return; - } - - $configs = DBA::select('config', ['cat', 'v', 'k']); - while ($config = DBA::fetch($configs)) { - $value = $this->toConfigValue($config['v']); - if (isset($value)) { - $return[$config['cat']][$config['k']] = $value; - } - } - DBA::close($configs); - - $this->config_loaded = true; - - return $return; - } - - /** - * {@inheritdoc} - */ - public function get($cat, $key) - { - if (!$this->isConnected()) { - return null; - } - - $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); - if (DBA::isResult($config)) { - $value = $this->toConfigValue($config['v']); - - if (isset($value)) { - return $value; - } - } - - return null; - } - - /** - * {@inheritdoc} - */ - public function set($cat, $key, $value) - { - if (!$this->isConnected()) { - return false; - } - - // We store our setting values as strings. - // 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 = $this->toDbValue($value); - - return DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); - } - - /** - * {@inheritdoc} - */ - public function delete($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return DBA::delete('config', ['cat' => $cat, 'k' => $key]); - } - - /** - * {@inheritdoc} - */ - public function isLoaded($cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return $this->config_loaded; - } -} diff --git a/src/Core/Config/Cache/ConfigCache.php b/src/Core/Config/Cache/ConfigCache.php index b8175452bf..3119b5db12 100644 --- a/src/Core/Config/Cache/ConfigCache.php +++ b/src/Core/Config/Cache/ConfigCache.php @@ -25,7 +25,7 @@ class ConfigCache * @param array $config A initial config array * @param bool $hidePasswordOutput True, if cache variables should take extra care of password values */ - public function __construct(array $config = [], $hidePasswordOutput = true) + public function __construct(array $config = [], bool $hidePasswordOutput = true) { $this->hidePasswordOutput = $hidePasswordOutput; $this->load($config); @@ -38,7 +38,7 @@ class ConfigCache * @param array $config * @param bool $overwrite Force value overwrite if the config key already exists */ - public function load(array $config, $overwrite = false) + public function load(array $config, bool $overwrite = false) { $categories = array_keys($config); @@ -68,7 +68,7 @@ class ConfigCache * * @return null|mixed Returns the value of the Config entry or null if not set */ - public function get($cat, $key = null) + public function get(string $cat, string $key = null) { if (isset($this->config[$cat][$key])) { return $this->config[$cat][$key]; @@ -82,14 +82,14 @@ class ConfigCache /** * Sets a default value in the config cache. Ignores already existing keys. * - * @param string $cat Config category - * @param string $k Config key - * @param mixed $v Default value to set + * @param string $cat Config category + * @param string $key Config key + * @param mixed $value Default value to set */ - private function setDefault($cat, $k, $v) + private function setDefault(string $cat, string $key, $value) { - if (!isset($this->config[$cat][$k])) { - $this->set($cat, $k, $v); + if (!isset($this->config[$cat][$key])) { + $this->set($cat, $key, $value); } } @@ -102,7 +102,7 @@ class ConfigCache * * @return bool True, if the value is set */ - public function set($cat, $key, $value) + public function set(string $cat, string $key, $value) { if (!isset($this->config[$cat])) { $this->config[$cat] = []; @@ -111,7 +111,7 @@ class ConfigCache if ($this->hidePasswordOutput && $key == 'password' && is_string($value)) { - $this->config[$cat][$key] = new HiddenString((string) $value); + $this->config[$cat][$key] = new HiddenString((string)$value); } else { $this->config[$cat][$key] = $value; } @@ -126,7 +126,7 @@ class ConfigCache * * @return bool true, if deleted */ - public function delete($cat, $key) + public function delete(string $cat, string $key) { if (isset($this->config[$cat][$key])) { unset($this->config[$cat][$key]); diff --git a/src/Core/Config/Cache/PConfigCache.php b/src/Core/Config/Cache/PConfigCache.php index 98adfa2ce1..b03d86f41e 100644 --- a/src/Core/Config/Cache/PConfigCache.php +++ b/src/Core/Config/Cache/PConfigCache.php @@ -20,9 +20,9 @@ class PConfigCache private $hidePasswordOutput; /** - * @param bool $hidePasswordOutput True, if cache variables should take extra care of password values + * @param bool $hidePasswordOutput True, if cache variables should take extra care of password values */ - public function __construct($hidePasswordOutput = true) + public function __construct(bool $hidePasswordOutput = true) { $this->hidePasswordOutput = $hidePasswordOutput; } @@ -34,7 +34,7 @@ class PConfigCache * @param int $uid * @param array $config */ - public function load($uid, array $config) + public function load(int $uid, array $config) { $categories = array_keys($config); @@ -56,13 +56,13 @@ class PConfigCache /** * Retrieves a value from the user config cache * - * @param int $uid User Id - * @param string $cat Config category - * @param string $key Config key + * @param int $uid User Id + * @param string $cat Config category + * @param string $key Config key * * @return null|string The value of the config entry or null if not set */ - public function get($uid, $cat, $key = null) + public function get(int $uid, string $cat, string $key = null) { if (isset($this->config[$uid][$cat][$key])) { return $this->config[$uid][$cat][$key]; @@ -85,7 +85,7 @@ class PConfigCache * * @return bool Set successful */ - public function set($uid, $cat, $key, $value) + public function set(int $uid, string $cat, string $key, $value) { if (!isset($this->config[$uid]) || !is_array($this->config[$uid])) { $this->config[$uid] = []; @@ -98,7 +98,7 @@ class PConfigCache if ($this->hidePasswordOutput && $key == 'password' && !empty($value) && is_string($value)) { - $this->config[$uid][$cat][$key] = new HiddenString((string) $value); + $this->config[$uid][$cat][$key] = new HiddenString((string)$value); } else { $this->config[$uid][$cat][$key] = $value; } @@ -116,7 +116,7 @@ class PConfigCache * * @return bool true, if deleted */ - public function delete($uid, $cat, $key) + public function delete(int $uid, string $cat, string $key) { if (isset($this->config[$uid][$cat][$key])) { unset($this->config[$uid][$cat][$key]); diff --git a/src/Core/Config/Configuration.php b/src/Core/Config/Configuration.php index 1489d91de0..37b947d11b 100644 --- a/src/Core/Config/Configuration.php +++ b/src/Core/Config/Configuration.php @@ -2,34 +2,34 @@ namespace Friendica\Core\Config; +use Friendica\Model; + /** * 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\IConfigCache ) - * - The Config-DB-Table (per Config-DB-adapter @see Adapter\IConfigAdapter ) + * - The Config-Files (loaded into the FileCache @see Cache\ConfigCache ) + * - The Config-DB-Table (per Config-DB-model @see Model\Config\Config ) */ -class Configuration +abstract class Configuration { /** * @var Cache\ConfigCache */ - private $configCache; + protected $configCache; /** - * @var Adapter\IConfigAdapter + * @var Model\Config\Config */ - private $configAdapter; + protected $configModel; /** - * @param Cache\ConfigCache $configCache The configuration cache (based on the config-files) - * @param Adapter\IConfigAdapter $configAdapter The configuration DB-backend + * @param Cache\ConfigCache $configCache The configuration cache (based on the config-files) + * @param Model\Config\Config $configModel The configuration model */ - public function __construct(Cache\ConfigCache $configCache, Adapter\IConfigAdapter $configAdapter) + public function __construct(Cache\ConfigCache $configCache, Model\Config\Config $configModel) { $this->configCache = $configCache; - $this->configAdapter = $configAdapter; - - $this->load(); + $this->configModel = $configModel; } /** @@ -51,16 +51,7 @@ class Configuration * * @return void */ - public function load($cat = 'config') - { - // If not connected, do nothing - if (!$this->configAdapter->isConnected()) { - return; - } - - // load the whole category out of the DB into the cache - $this->configCache->load($this->configAdapter->load($cat), true); - } + abstract public function load(string $cat = 'config'); /** * @brief Get a particular user's config variable given the category name @@ -77,26 +68,7 @@ class Configuration * * @return mixed Stored value or null if it does not exist */ - public function get($cat, $key, $default_value = null, $refresh = false) - { - // if the value isn't loaded or refresh is needed, load it to the cache - if ($this->configAdapter->isConnected() && - (!$this->configAdapter->isLoaded($cat, $key) || - $refresh)) { - - $dbvalue = $this->configAdapter->get($cat, $key); - - if (isset($dbvalue)) { - $this->configCache->set($cat, $key, $dbvalue); - unset($dbvalue); - } - } - - // use the config cache for return - $result = $this->configCache->get($cat, $key); - - return (isset($result)) ? $result : $default_value; - } + abstract public function get(string $cat, string $key, $default_value = null, bool $refresh = false); /** * @brief Sets a configuration value for system config @@ -111,20 +83,7 @@ class Configuration * * @return bool Operation success */ - public function set($cat, $key, $value) - { - // set the cache first - $cached = $this->configCache->set($cat, $key, $value); - - // If there is no connected adapter, we're finished - if (!$this->configAdapter->isConnected()) { - return $cached; - } - - $stored = $this->configAdapter->set($cat, $key, $value); - - return $cached && $stored; - } + abstract public function set(string $cat, string $key, $value); /** * @brief Deletes the given key from the system configuration. @@ -137,16 +96,5 @@ class Configuration * * @return bool */ - public function delete($cat, $key) - { - $cacheRemoved = $this->configCache->delete($cat, $key); - - if (!$this->configAdapter->isConnected()) { - return $cacheRemoved; - } - - $storeRemoved = $this->configAdapter->delete($cat, $key); - - return $cacheRemoved || $storeRemoved; - } + abstract public function delete(string $cat, string $key); } diff --git a/src/Core/Config/JitConfiguration.php b/src/Core/Config/JitConfiguration.php new file mode 100644 index 0000000000..40aa5d6f6c --- /dev/null +++ b/src/Core/Config/JitConfiguration.php @@ -0,0 +1,128 @@ +in_db = []; + + // take the values of the given cache instead of loading them from the model again + $preSet = $configCache->getAll(); + if (!empty($preSet)) { + foreach ($preSet as $cat => $data) { + foreach ($data as $key => $value) { + $this->in_db[$cat][$key] = true; + } + } + } + + $this->load(); + } + + /** + * {@inheritDoc} + * + */ + public function load(string $cat = 'config') + { + // If not connected, do nothing + if (!$this->configModel->isConnected()) { + return; + } + + $config = $this->configModel->load($cat); + + if (!empty($config[$cat])) { + foreach ($config[$cat] as $key => $value) { + $this->in_db[$cat][$key] = true; + } + } + + // load the whole category out of the DB into the cache + $this->configCache->load($config, true); + } + + /** + * {@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->configModel->isConnected() && + (empty($this->in_db[$cat][$key]) || + $refresh)) { + + $dbvalue = $this->configModel->get($cat, $key); + + if (isset($dbvalue)) { + $this->configCache->set($cat, $key, $dbvalue); + unset($dbvalue); + $this->in_db[$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) + { + // set the cache first + $cached = $this->configCache->set($cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configModel->isConnected()) { + return $cached; + } + + $stored = $this->configModel->set($cat, $key, $value); + + $this->in_db[$cat][$key] = $stored; + + return $cached && $stored; + } + + /** + * {@inheritDoc} + */ + public function delete(string $cat, string $key) + { + $cacheRemoved = $this->configCache->delete($cat, $key); + + if (isset($this->in_db[$cat][$key])) { + unset($this->in_db[$cat][$key]); + } + + if (!$this->configModel->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configModel->delete($cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Core/Config/PConfiguration.php b/src/Core/Config/PConfiguration.php index 79ed1a61e6..e69981c08c 100644 --- a/src/Core/Config/PConfiguration.php +++ b/src/Core/Config/PConfiguration.php @@ -6,7 +6,7 @@ namespace Friendica\Core\Config; * This class is responsible for the user-specific configuration values in Friendica * The values are set through the Config-DB-Table (per Config-DB-adapter @see Adapter\IPConfigAdapter ) * - * The configuration cache (@see Cache\IPConfigCache ) is used for temporary caching of database calls. This will + * The configuration cache (@see Cache\PConfigCache ) is used for temporary caching of database calls. This will * increase the performance. */ class PConfiguration @@ -35,7 +35,7 @@ class PConfiguration * @brief Loads all configuration values of a user's config family into a cached storage. * * All configuration values of the given user are stored with the $uid in - * the cache ( @see IPConfigCache ) + * the cache ( @see PConfigCache ) * * @param string $uid The user_id * @param string $cat The category of the configuration value @@ -59,7 +59,7 @@ class PConfiguration * * Get a particular user's config value from the given category ($cat) * and the $key with the $uid from a cached storage either from the $this->configAdapter - * (@see IConfigAdapter ) or from the $this->configCache (@see IConfigCache ). + * (@see IConfigAdapter ) or from the $this->configCache (@see PConfigCache ). * * @param string $uid The user_id * @param string $cat The category of the configuration value diff --git a/src/Core/Config/PreloadConfiguration.php b/src/Core/Config/PreloadConfiguration.php new file mode 100644 index 0000000000..b5823c6144 --- /dev/null +++ b/src/Core/Config/PreloadConfiguration.php @@ -0,0 +1,116 @@ +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->configModel->isConnected()) { + return; + } + + $config = $this->configModel->load(); + $this->config_loaded = true; + + // load the whole category out of the DB into the cache + $this->configCache->load($config, true); + } + + /** + * {@inheritDoc} + */ + public function get(string $cat, string $key, $default_value = null, bool $refresh = false) + { + if ($refresh) { + if ($this->configModel->isConnected()) { + $config = $this->configModel->get($cat, $key); + if (isset($config)) { + $this->configCache->set($cat, $key, $config); + } + } + } + + // 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) + { + if (!$this->config_loaded) { + $this->load(); + } + + // set the cache first + $cached = $this->configCache->set($cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configModel->isConnected()) { + return $cached; + } + + $stored = $this->configModel->set($cat, $key, $value); + + return $cached && $stored; + } + + /** + * {@inheritDoc} + */ + public function delete(string $cat, string $key) + { + if ($this->config_loaded) { + $this->load(); + } + + $cacheRemoved = $this->configCache->delete($cat, $key); + + if (!$this->configModel->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configModel->delete($cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Database/Database.php b/src/Database/Database.php index 14381ae922..0d1540f7cd 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -161,6 +161,15 @@ class Database $this->logger = $logger; } + /** + * Sets the profiler for DBA + * + * @param Profiler $profiler + */ + public function setProfiler(Profiler $profiler) + { + $this->profiler = $profiler; + } /** * Disconnects the current database connection */ @@ -323,6 +332,11 @@ class Database } } + public function isConnected() + { + return $this->connected; + } + public function connected() { $connected = false; diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php index 7764d89900..5a24f86283 100644 --- a/src/Factory/ConfigFactory.php +++ b/src/Factory/ConfigFactory.php @@ -6,6 +6,7 @@ use Friendica\Core; use Friendica\Core\Config; use Friendica\Core\Config\Adapter; use Friendica\Core\Config\Cache; +use Friendica\Model\Config\Config as ConfigModel; use Friendica\Util\Config\ConfigFileLoader; class ConfigFactory @@ -24,19 +25,19 @@ class ConfigFactory } /** - * @param Cache\ConfigCache $configCache The config cache + * @param Cache\ConfigCache $configCache The config cache of this adapter + * @param ConfigModel $configModel The configuration model * * @return Config\Configuration */ - public static function createConfig(Cache\ConfigCache $configCache) + public static function createConfig(Cache\ConfigCache $configCache, ConfigModel $configModel) { if ($configCache->get('system', 'config_adapter') === 'preload') { - $configAdapter = new Adapter\PreloadConfigAdapter(); + $configuration = new Config\PreloadConfiguration($configCache, $configModel); } else { - $configAdapter = new Adapter\JITConfigAdapter(); + $configuration = new Config\JitConfiguration($configCache, $configModel); } - $configuration = new Config\Configuration($configCache, $configAdapter); // Set the config in the static container for legacy usage Core\Config::init($configuration); diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php index 656a255b2b..36ab20a013 100644 --- a/src/Factory/DependencyFactory.php +++ b/src/Factory/DependencyFactory.php @@ -31,7 +31,8 @@ class DependencyFactory $configCache = Factory\ConfigFactory::createCache($configLoader); $profiler = Factory\ProfilerFactory::create($configCache); $database = Factory\DBFactory::init($configCache, $profiler, $_SERVER); - $config = Factory\ConfigFactory::createConfig($configCache); + $configModel = new \Friendica\Model\Config\Config($database); + $config = Factory\ConfigFactory::createConfig($configCache, $configModel); // needed to call PConfig::init() Factory\ConfigFactory::createPConfig($configCache, new PConfigCache()); $logger = Factory\LoggerFactory::create($channel, $database, $config, $profiler); diff --git a/src/Model/Config/Config.php b/src/Model/Config/Config.php new file mode 100644 index 0000000000..aa81f1cae9 --- /dev/null +++ b/src/Model/Config/Config.php @@ -0,0 +1,131 @@ +dba->select('config', ['cat', 'v', 'k']); + } else { + $configs = $this->dba->select('config', ['cat', 'v', 'k'], ['cat' => $cat]); + } + + while ($config = $this->dba->fetch($configs)) { + + $key = $config['k']; + $value = $this->toConfigValue($config['v']); + + // just save it in case it is set + if (isset($value)) { + $return[$config['cat']][$key] = $value; + } + } + $this->dba->close($configs); + + return $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 \Exception In case DB calls are invalid + */ + public function get(string $cat, string $key) + { + if (!$this->isConnected()) { + return null; + } + + $config = $this->dba->selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); + if ($this->dba->isResult($config)) { + $value = $this->toConfigValue($config['v']); + + // just return it in case it is set + if (isset($value)) { + return $value; + } + } + + 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 \Exception In case DB calls are invalid + */ + public function set(string $cat, string $key, $value) + { + 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 = $this->toDbValue($value); + + $result = $this->dba->update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); + + return $result; + } + + /** + * 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 \Exception In case DB calls are invalid + */ + public function delete(string $cat, string $key) + { + if (!$this->isConnected()) { + return false; + } + + return $this->dba->delete('config', ['cat' => $cat, 'k' => $key]); + } +} diff --git a/src/Model/Config/DbaConfig.php b/src/Model/Config/DbaConfig.php new file mode 100644 index 0000000000..cd6b6da8c0 --- /dev/null +++ b/src/Model/Config/DbaConfig.php @@ -0,0 +1,86 @@ +dba = $dba; + } + + /** + * Checks if the model is currently connected + * + * @return bool + */ + public function isConnected() + { + return $this->dba->isConnected(); + } + + /** + * 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 null|string $value + * + * @return null|array|string + */ + protected function toConfigValue($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; + } + } + + /** + * Formats a config value to a DB value (string) + * + * @param mixed $value + * + * @return string + */ + protected function toDbValue($value) + { + // if not set, save an empty string + if (!isset($value)) { + return ''; + } + + switch (true) { + // manage arrays + case is_array($value): + return serialize($value); + + default: + return (string)$value; + } + } +} diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index b3418c20f4..999da871db 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -5,12 +5,15 @@ namespace Friendica\Test; -use Friendica\App; -use Friendica\Database\DBA; -use Friendica\Factory; +use Friendica\App\Mode; +use Friendica\App\Router; +use Friendica\Core\Config\Cache\ConfigCache; +use Friendica\Database\Database; +use Friendica\Factory\ConfigFactory; +use Friendica\Factory\DBFactory; +use Friendica\Factory\ProfilerFactory; use Friendica\Util\BasePath; use Friendica\Util\Config\ConfigFileLoader; -use Friendica\Util\Logger\VoidLogger; use Friendica\Util\Profiler; use PHPUnit\DbUnit\DataSet\YamlDataSet; use PHPUnit\DbUnit\TestCaseTrait; @@ -25,6 +28,33 @@ abstract class DatabaseTest extends MockedTest { use TestCaseTrait; + /** @var Database */ + protected static $dba; + + /** @var BasePath */ + protected static $basePath; + + /** @var Mode */ + protected static $mode; + + /** @var ConfigCache */ + protected static $configCache; + + /** @var Profiler */ + protected static $profiler; + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + + self::$basePath = BasePath::create(dirname(__DIR__)); + self::$mode = new Mode(self::$basePath); + $configLoader = new ConfigFileLoader(self::$basePath, self::$mode); + self::$configCache = ConfigFactory::createCache($configLoader); + self::$profiler = ProfilerFactory::create(self::$configCache); + self::$dba = DBFactory::init(self::$configCache, self::$profiler, $_SERVER); + } + /** * Get database connection. * @@ -42,27 +72,13 @@ abstract class DatabaseTest extends MockedTest $this->markTestSkipped('Please set the MYSQL_* environment variables to your test database credentials.'); } - $basePath = BasePath::create(dirname(__DIR__)); - $mode = new App\Mode($basePath); - $configLoader = new ConfigFileLoader($basePath, $mode); - $config = Factory\ConfigFactory::createCache($configLoader); - - $profiler = \Mockery::mock(Profiler::class); - - DBA::connect( - $config, - $profiler, - new VoidLogger(), - getenv('MYSQL_HOST'), - getenv('MYSQL_USERNAME'), - getenv('MYSQL_PASSWORD'), - getenv('MYSQL_DATABASE')); - - if (!DBA::connected()) { - $this->markTestSkipped('Could not connect to the database.'); + if (!self::$dba->isConnected()) { + if (!self::$dba->connect()) { + $this->markTestSkipped('Could not connect to the database.'); + } } - return $this->createDefaultDBConnection(DBA::getConnection(), getenv('MYSQL_DATABASE')); + return $this->createDefaultDBConnection(self::$dba->getConnection(), getenv('MYSQL_DATABASE')); } /** diff --git a/tests/Util/AppMockTrait.php b/tests/Util/AppMockTrait.php index 0ca625ee0e..52941d0d51 100644 --- a/tests/Util/AppMockTrait.php +++ b/tests/Util/AppMockTrait.php @@ -44,12 +44,13 @@ trait AppMockTrait public function mockApp(vfsStreamDirectory $root, $raw = false) { $this->configMock = \Mockery::mock(Config\Cache\ConfigCache::class); + $this->configMock->shouldReceive('getAll')->andReturn([])->once(); $this->mode = \Mockery::mock(App\Mode::class); - $configAdapterMock = \Mockery::mock(Config\Adapter\IConfigAdapter::class); + $configModel= \Mockery::mock(\Friendica\Model\Config\Config::class); // Disable the adapter - $configAdapterMock->shouldReceive('isConnected')->andReturn(false); + $configModel->shouldReceive('isConnected')->andReturn(false); - $config = new Config\Configuration($this->configMock, $configAdapterMock); + $config = new Config\JitConfiguration($this->configMock, $configModel); // Initialize empty Config Config::init($config); diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index 94557ac69c..5ae4bdff50 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -12,9 +12,7 @@ use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Factory; use Friendica\Network\HTTPException; -use Friendica\Util\BasePath; use Friendica\Util\BaseURL; -use Friendica\Util\Config\ConfigFileLoader; use Monolog\Handler\TestHandler; require_once __DIR__ . '/../../include/api.php'; @@ -49,18 +47,13 @@ class ApiTest extends DatabaseTest */ public function setUp() { - $basePath = BasePath::create(dirname(__DIR__) . '/../'); - $mode = new App\Mode($basePath); - $router = new App\Router(); - $configLoader = new ConfigFileLoader($basePath, $mode); - $configCache = Factory\ConfigFactory::createCache($configLoader); - $profiler = Factory\ProfilerFactory::create($configCache); - $database = Factory\DBFactory::init($configCache, $profiler, $_SERVER); - $config = Factory\ConfigFactory::createConfig($configCache); - Factory\ConfigFactory::createPConfig($configCache, new Config\Cache\PConfigCache()); - $logger = Factory\LoggerFactory::create('test', $database, $config, $profiler); + $configModel = new \Friendica\Model\Config\Config(self::$dba); + $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel); + Factory\ConfigFactory::createPConfig(self::$configCache, new Config\Cache\PConfigCache()); + $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler); $baseUrl = new BaseURL($config, $_SERVER); - $this->app = new App($database, $config, $mode, $router, $baseUrl, $logger, $profiler, false); + $router = new App\Router(); + $this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, false); parent::setUp(); diff --git a/tests/src/Core/Config/Cache/ConfigCacheTest.php b/tests/src/Core/Config/Cache/ConfigCacheTest.php index db92cc7436..d8ac3eaea4 100644 --- a/tests/src/Core/Config/Cache/ConfigCacheTest.php +++ b/tests/src/Core/Config/Cache/ConfigCacheTest.php @@ -157,6 +157,12 @@ class ConfigCacheTest extends MockedTest 'key1' => 'value1', 'key2' => 'value2', ], $configCache->get('system')); + + // explicit null as key + $this->assertEquals([ + 'key1' => 'value1', + 'key2' => 'value2', + ], $configCache->get('system', null)); } /** diff --git a/tests/src/Core/Config/Cache/PConfigCacheTest.php b/tests/src/Core/Config/Cache/PConfigCacheTest.php index b8feec4b19..22beccf601 100644 --- a/tests/src/Core/Config/Cache/PConfigCacheTest.php +++ b/tests/src/Core/Config/Cache/PConfigCacheTest.php @@ -79,6 +79,12 @@ class PConfigCacheTest extends MockedTest 'key1' => 'value1', 'key2' => 'value2', ], $configCache->get($uid, 'system')); + + // test explicit cat with null as key + $this->assertEquals([ + 'key1' => 'value1', + 'key2' => 'value2', + ], $configCache->get($uid, 'system', null)); } /** @@ -113,7 +119,7 @@ class PConfigCacheTest extends MockedTest */ public function testKeyDiffWithResult($data) { - $configCache = new PConfigCache($data); + $configCache = new PConfigCache(); $diffConfig = [ 'fakeCat' => [ diff --git a/tests/src/Core/Config/ConfigurationTest.php b/tests/src/Core/Config/ConfigurationTest.php index fda69896fd..2191d86f1c 100644 --- a/tests/src/Core/Config/ConfigurationTest.php +++ b/tests/src/Core/Config/ConfigurationTest.php @@ -2,13 +2,55 @@ namespace Friendica\Test\src\Core\Config; -use Friendica\Core\Config\Adapter\IConfigAdapter; use Friendica\Core\Config\Cache\ConfigCache; use Friendica\Core\Config\Configuration; +use Friendica\Core\Config\JitConfiguration; +use Friendica\Model\Config\Config as ConfigModel; use Friendica\Test\MockedTest; +use Mockery\MockInterface; +use Mockery; -class ConfigurationTest extends MockedTest +abstract class ConfigurationTest extends MockedTest { + /** @var ConfigModel|MockInterface */ + protected $configModel; + + /** @var ConfigCache */ + protected $configCache; + + /** @var Configuration */ + protected $testedConfig; + + /** + * Assert a config tree + * + * @param string $cat The category to assert + * @param array $data The result data array + */ + protected function assertConfig(string $cat, array $data) + { + $result = $this->testedConfig->getCache()->getAll(); + + $this->assertNotEmpty($result); + $this->assertArrayHasKey($cat, $result); + $this->assertArraySubset($data, $result[$cat]); + } + + + protected function setUp() + { + parent::setUp(); + + // Create the config model + $this->configModel = Mockery::mock(ConfigModel::class); + $this->configCache = new ConfigCache(); + } + + /** + * @return Configuration + */ + public abstract function getInstance(); + public function dataTests() { return [ @@ -23,107 +65,243 @@ class ConfigurationTest extends MockedTest ]; } + public function dataConfigLoad() + { + $data = [ + 'system' => [ + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => 'value3', + ], + 'config' => [ + 'key1' => 'value1a', + 'key4' => 'value4', + ], + 'other' => [ + 'key5' => 'value5', + 'key6' => 'value6', + ], + ]; + + return [ + 'system' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'system', + ], + ], + 'other' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'other', + ], + ], + 'config' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'config', + ], + ], + 'all' => [ + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'system', + 'config', + 'other' + ], + ], + ]; + } + /** * Test the configuration initialization */ - public function testSetUp() + public function testSetUp(array $data) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->once(); + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertInstanceOf(ConfigCache::class, $configuration->getCache()); + // assert config is loaded everytime + $this->assertConfig('config', $data['config']); } /** * Test the configuration load() method */ - public function testCacheLoad() + public function testLoad(array $data, array $possibleCats, array $load) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - // expected loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'it']])->once(); - $configAdapter->shouldReceive('isLoaded')->with('testing', 'test')->andReturn(true)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $configuration = new Configuration($configCache, $configAdapter); - $configuration->load('testing'); + foreach ($load as $loadedCats) { + $this->testedConfig->load($loadedCats); + } - $this->assertEquals('it', $configuration->get('testing', 'test')); - $this->assertEquals('it', $configuration->getCache()->get('testing', 'test')); + // Assert at least loaded cats are loaded + foreach ($load as $loadedCats) { + $this->assertConfig($loadedCats, $data[$loadedCats]); + } + } + + public function dataDoubleLoad() + { + return [ + 'config' => [ + 'data1' => [ + 'config' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ], + ], + 'data2' => [ + 'config' => [ + 'key1' => 'overwritten!', + 'key3' => 'value3', + ], + ], + 'expect' => [ + 'config' => [ + // load should overwrite values everytime! + 'key1' => 'overwritten!', + 'key2' => 'value2', + 'key3' => 'value3', + ], + ], + ], + 'other' => [ + 'data1' => [ + 'config' => [ + 'key12' => 'data4', + 'key45' => 7, + ], + 'other' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ], + ], + 'data2' => [ + 'other' => [ + 'key1' => 'overwritten!', + 'key3' => 'value3', + ], + 'config' => [ + 'key45' => 45, + 'key52' => true, + ] + ], + 'expect' => [ + 'other' => [ + // load should overwrite values everytime! + 'key1' => 'overwritten!', + 'key2' => 'value2', + 'key3' => 'value3', + ], + 'config' => [ + 'key12' => 'data4', + 'key45' => 45, + 'key52' => true, + ], + ], + ], + ]; } /** * Test the configuration load() method with overwrite */ - public function testCacheLoadDouble() + public function testCacheLoadDouble(array $data1, array $data2, array $expect) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(5); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - // expected loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'it']])->once(); - $configAdapter->shouldReceive('isLoaded')->with('testing', 'test')->andReturn(true)->twice(); - // expected next loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'again']])->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $configuration = new Configuration($configCache, $configAdapter); - $configuration->load('testing'); + foreach ($data1 as $cat => $data) { + $this->testedConfig->load($cat); + } - $this->assertEquals('it', $configuration->get('testing', 'test')); - $this->assertEquals('it', $configuration->getCache()->get('testing', 'test')); + // Assert at least loaded cats are loaded + foreach ($data1 as $cat => $data) { + $this->assertConfig($cat, $data); + } - $configuration->load('testing'); + foreach ($data2 as $cat => $data) { + $this->testedConfig->load($cat); + } + } - $this->assertEquals('again', $configuration->get('testing', 'test')); - $this->assertEquals('again', $configuration->getCache()->get('testing', 'test')); + /** + * Test the configuration load without result + */ + public function testLoadWrong() + { + $this->configModel->shouldReceive('isConnected')->andReturn(true)->once(); + $this->configModel->shouldReceive('load')->withAnyArgs()->andReturn([])->once(); + + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); + + $this->assertEmpty($this->testedConfig->getCache()->getAll()); } /** * Test the configuration get() and set() methods without adapter + * * @dataProvider dataTests */ public function testSetGetWithoutDB($data) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(3); + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(3); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertTrue($configuration->set('test', 'it', $data)); + $this->assertTrue($this->testedConfig->set('test', 'it', $data)); - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); } /** - * Test the configuration get() and set() methods with adapter + * Test the configuration get() and set() methods with a model/db + * * @dataProvider dataTests */ public function testSetGetWithDB($data) { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); - $configAdapter->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); + $this->configModel->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertTrue($configuration->set('test', 'it', $data)); + $this->assertTrue($this->testedConfig->set('test', 'it', $data)); - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); } /** @@ -131,145 +309,111 @@ class ConfigurationTest extends MockedTest */ public function testGetWrongWithoutDB() { - $configCache = new ConfigCache(); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); - - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); // without refresh - $this->assertNull($configuration->get('test', 'it')); + $this->assertNull($this->testedConfig->get('test', 'it')); /// beware that the cache returns '!!' and not null for a non existing value - $this->assertNull($configuration->getCache()->get('test', 'it')); + $this->assertNull($this->testedConfig->getCache()->get('test', 'it')); // with default value - $this->assertEquals('default', $configuration->get('test', 'it', 'default')); + $this->assertEquals('default', $this->testedConfig->get('test', 'it', 'default')); // with default value and refresh - $this->assertEquals('default', $configuration->get('test', 'it', 'default', true)); + $this->assertEquals('default', $this->testedConfig->get('test', 'it', 'default', true)); } /** * Test the configuration get() method with refresh + * * @dataProvider dataTests */ public function testGetWithRefresh($data) { - $configCache = new ConfigCache(['test' => ['it' => 'now']]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->twice(); - $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn($data)->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'not')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with('test', 'not')->andReturn(null)->once(); + $this->configCache->load(['test' => ['it' => 'now']]); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); // without refresh - $this->assertEquals('now', $configuration->get('test', 'it')); - $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); // with refresh - $this->assertEquals($data, $configuration->get('test', 'it', null, true)); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it', null, true)); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); // without refresh and wrong value and default - $this->assertEquals('default', $configuration->get('test', 'not', 'default')); - $this->assertNull($configuration->getCache()->get('test', 'not')); + $this->assertEquals('default', $this->testedConfig->get('test', 'not', 'default')); + $this->assertNull($this->testedConfig->getCache()->get('test', 'not')); } /** - * Test the configuration get() method with different isLoaded settings - * @dataProvider dataTests - */ - public function testGetWithoutLoaded($data) - { - $configCache = new ConfigCache(['test' => ['it' => 'now']]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn(null)->once(); - - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn($data)->once(); - - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); - - $configuration = new Configuration($configCache, $configAdapter); - - // first run is not loaded and no data is found in the DB - $this->assertEquals('now', $configuration->get('test', 'it')); - $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); - - // second run is not loaded, but now data is found in the db (overwrote cache) - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); - - // third run is loaded and therefore cache is used - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); - } - - /** - * Test the configuration delete() method without adapter + * Test the configuration delete() method without a model/db + * * @dataProvider dataTests */ public function testDeleteWithoutDB($data) { - $configCache = new ConfigCache(['test' => ['it' => $data]]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); + $this->configCache->load(['test' => ['it' => $data]]); - $configuration = new Configuration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $this->assertEquals($data, $configuration->get('test', 'it')); - $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->get('test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get('test', 'it')); - $this->assertTrue($configuration->delete('test', 'it')); - $this->assertNull($configuration->get('test', 'it')); - $this->assertNull($configuration->getCache()->get('test', 'it')); + $this->assertTrue($this->testedConfig->delete('test', 'it')); + $this->assertNull($this->testedConfig->get('test', 'it')); + $this->assertNull($this->testedConfig->getCache()->get('test', 'it')); - $this->assertEmpty($configuration->getCache()->getAll()); + $this->assertEmpty($this->testedConfig->getCache()->getAll()); } /** - * Test the configuration delete() method with adapter + * Test the configuration delete() method with a model/db */ public function testDeleteWithDB() { - $configCache = new ConfigCache(['test' => ['it' => 'now', 'quarter' => 'true']]); - $configAdapter = \Mockery::mock(IConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(6); - // constructor loading - $configAdapter->shouldReceive('load')->andReturn([])->once(); - $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); + $this->configCache->load(['test' => ['it' => 'now', 'quarter' => 'true']]); - $configAdapter->shouldReceive('delete')->with('test', 'it')->andReturn(false)->once(); + $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(); - $configAdapter->shouldReceive('delete')->with('test', 'second')->andReturn(true)->once(); - $configAdapter->shouldReceive('delete')->with('test', 'third')->andReturn(false)->once(); - $configAdapter->shouldReceive('delete')->with('test', 'quarter')->andReturn(true)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(ConfigCache::class, $this->testedConfig->getCache()); - $configuration = new Configuration($configCache, $configAdapter); + // directly set the value to the cache + $this->testedConfig->getCache()->set('test', 'it', 'now'); - $this->assertEquals('now', $configuration->get('test', 'it')); - $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->get('test', 'it')); + $this->assertEquals('now', $this->testedConfig->getCache()->get('test', 'it')); // delete from cache only - $this->assertTrue($configuration->delete('test', 'it')); + $this->assertTrue($this->testedConfig->delete('test', 'it')); // delete from db only - $this->assertTrue($configuration->delete('test', 'second')); + $this->assertTrue($this->testedConfig->delete('test', 'second')); // no delete - $this->assertFalse($configuration->delete('test', 'third')); + $this->assertFalse($this->testedConfig->delete('test', 'third')); // delete both - $this->assertTrue($configuration->delete('test', 'quarter')); + $this->assertTrue($this->testedConfig->delete('test', 'quarter')); - $this->assertEmpty($configuration->getCache()->getAll()); + $this->assertEmpty($this->testedConfig->getCache()->getAll()); } } diff --git a/tests/src/Core/Config/JitConfigurationTest.php b/tests/src/Core/Config/JitConfigurationTest.php new file mode 100644 index 0000000000..79aada48d6 --- /dev/null +++ b/tests/src/Core/Config/JitConfigurationTest.php @@ -0,0 +1,167 @@ +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 + */ + public function testLoad(array $data, array $possibleCats, 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, $possibleCats, $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, $expect); + + // Assert the expected categories + foreach ($data2 as $cat => $data) { + $this->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 + $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(); + + parent::testDeleteWithDB(); + } +} diff --git a/tests/src/Core/Config/PreloadConfigurationTest.php b/tests/src/Core/Config/PreloadConfigurationTest.php new file mode 100644 index 0000000000..88044395b1 --- /dev/null +++ b/tests/src/Core/Config/PreloadConfigurationTest.php @@ -0,0 +1,140 @@ +configCache, $this->configModel); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testSetUp(array $data) + { + $this->configModel->shouldReceive('load') + ->andReturn($data) + ->once(); + + parent::testSetUp($data); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testLoad(array $data, array $possibleCats, array $load) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); + + $this->configModel->shouldReceive('load') + ->andReturn($data) + ->once(); + + parent::testLoad($data, $possibleCats, $load); + + // Assert that every category is loaded everytime + foreach ($data as $cat => $values) { + $this->assertConfig($cat, $values); + } + } + + /** + * @dataProvider dataDoubleLoad + */ + 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, $expect); + + // Assert that every category is loaded everytime and is NOT overwritten + foreach ($data1 as $cat => $values) { + $this->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(); + } +} diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index 84d17dc899..852db4498f 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -6,26 +6,19 @@ use Friendica\Core\Config; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Test\DatabaseTest; -use Friendica\Util\BasePath; use Friendica\Util\BaseURL; -use Friendica\Util\Config\ConfigFileLoader; class DBATest extends DatabaseTest { public function setUp() { - $basePath = BasePath::create(dirname(__DIR__) . '/../../'); - $mode = new App\Mode($basePath); - $router = new App\Router(); - $configLoader = new ConfigFileLoader($basePath, $mode); - $configCache = Factory\ConfigFactory::createCache($configLoader); - $profiler = Factory\ProfilerFactory::create($configCache); - $database = Factory\DBFactory::init($configCache, $profiler, $_SERVER); - $config = Factory\ConfigFactory::createConfig($configCache); - Factory\ConfigFactory::createPConfig($configCache, new Config\Cache\PConfigCache()); - $logger = Factory\LoggerFactory::create('test', $database, $config, $profiler); + $configModel = new \Friendica\Model\Config\Config(self::$dba); + $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel); + Factory\ConfigFactory::createPConfig(self::$configCache, new Config\Cache\PConfigCache()); + $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler); $baseUrl = new BaseURL($config, $_SERVER); - $this->app = new App($database, $config, $mode, $router, $baseUrl, $logger, $profiler, false); + $router = new App\Router(); + $this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, false); parent::setUp(); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index b005d8d53d..d10f07493b 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -6,27 +6,24 @@ use Friendica\App; use Friendica\Core\Config\Cache\PConfigCache; use Friendica\Database\DBStructure; use Friendica\Factory; +use Friendica\Model\Config\Config; use Friendica\Test\DatabaseTest; -use Friendica\Util\BasePath; use Friendica\Util\BaseURL; -use Friendica\Util\Config\ConfigFileLoader; class DBStructureTest extends DatabaseTest { + /** + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ public function setUp() { - $basePath = BasePath::create(dirname(__DIR__) . '/../../'); - $mode = new App\Mode($basePath); - $router = new App\Router(); - $configLoader = new ConfigFileLoader($basePath, $mode); - $configCache = Factory\ConfigFactory::createCache($configLoader); - $profiler = Factory\ProfilerFactory::create($configCache); - $database = Factory\DBFactory::init($configCache, $profiler, $_SERVER); - $config = Factory\ConfigFactory::createConfig($configCache); - Factory\ConfigFactory::createPConfig($configCache, new PConfigCache()); - $logger = Factory\LoggerFactory::create('test', $database, $config, $profiler); + $configModel = new Config(self::$dba); + $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel); + Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache()); + $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler); $baseUrl = new BaseURL($config, $_SERVER); - $this->app = new App($database, $config, $mode, $router, $baseUrl, $logger, $profiler, false); + $router = new App\Router(); + $this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, false); parent::setUp(); }