diff --git a/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php b/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php deleted file mode 100644 index 38caf35cac..0000000000 --- a/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php +++ /dev/null @@ -1,83 +0,0 @@ -connected = DBA::connected(); - } - - /** - * Checks if the adapter is currently connected - * - * @return bool - */ - public function isConnected() - { - return $this->connected; - } - - /** - * 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/src/Core/Config/Adapter/IPConfigAdapter.php b/src/Core/Config/Adapter/IPConfigAdapter.php deleted file mode 100644 index c505532c59..0000000000 --- a/src/Core/Config/Adapter/IPConfigAdapter.php +++ /dev/null @@ -1,85 +0,0 @@ - - */ -class JITPConfigAdapter extends AbstractDbaConfigAdapter implements IPConfigAdapter -{ - private $in_db; - - /** - * {@inheritdoc} - */ - public function load($uid, $cat) - { - $return = []; - - if (!$this->isConnected()) { - return $return; - } - - $pconfigs = DBA::select('pconfig', ['v', 'k'], ['cat' => $cat, 'uid' => $uid]); - if (DBA::isResult($pconfigs)) { - while ($pconfig = DBA::fetch($pconfigs)) { - $key = $pconfig['k']; - $value = $this->toConfigValue($pconfig['v']); - - // The value was in the db, so don't check it again (unless you have to) - $this->in_db[$uid][$cat][$key] = true; - - if (isset($value)) { - $return[$key] = $value; - } - } - } else if ($cat != 'config') { - // Negative caching - $return = null; - } - DBA::close($pconfigs); - - return [$cat => $return]; - } - - /** - * {@inheritdoc} - * - * @param bool $mark if true, mark the selection of the current cat/key pair - */ - public function get($uid, $cat, $key, $mark = true) - { - if (!$this->isConnected()) { - return null; - } - - // The value was in the db, so don't check it again (unless you have to) - if ($mark) { - $this->in_db[$uid][$cat][$key] = true; - } - - $pconfig = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $key]); - if (DBA::isResult($pconfig)) { - $value = $this->toConfigValue($pconfig['v']); - - if (isset($value)) { - return $value; - } - } - - $this->in_db[$uid][$cat][$key] = false; - return null; - } - - /** - * {@inheritdoc} - */ - public function set($uid, $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($uid, $cat, $key, false); - - if (!isset($this->in_db[$uid])) { - $this->in_db[$uid] = []; - } - if (!isset($this->in_db[$uid][$cat])) { - $this->in_db[$uid][$cat] = []; - } - if (!isset($this->in_db[$uid][$cat][$key])) { - $this->in_db[$uid][$cat][$key] = false; - } - - if (isset($stored_value) && ($stored_value === $compare_value) && $this->in_db[$uid][$cat][$key]) { - return true; - } - - // manage array value - $dbvalue = (is_array($value) ? serialize($value) : $value); - - $result = DBA::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true); - - $this->in_db[$uid][$cat][$key] = $result; - - return $result; - } - - /** - * {@inheritdoc} - */ - public function delete($uid, $cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - if (isset($this->in_db[$uid][$cat][$key])) { - unset($this->in_db[$uid][$cat][$key]); - } - - return DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]); - } - - /** - * {@inheritdoc} - */ - public function isLoaded($uid, $cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return (isset($this->in_db[$uid][$cat][$key])) && $this->in_db[$uid][$cat][$key]; - } -} diff --git a/src/Core/Config/Adapter/PreloadPConfigAdapter.php b/src/Core/Config/Adapter/PreloadPConfigAdapter.php deleted file mode 100644 index 838f3763df..0000000000 --- a/src/Core/Config/Adapter/PreloadPConfigAdapter.php +++ /dev/null @@ -1,142 +0,0 @@ - - */ -class PreloadPConfigAdapter extends AbstractDbaConfigAdapter implements IPConfigAdapter -{ - /** - * @var array true if config for user is loaded - */ - private $config_loaded; - - /** - * @param int $uid The UID of the current user - */ - public function __construct($uid = null) - { - parent::__construct(); - - $this->config_loaded = []; - - if (isset($uid)) { - $this->load($uid, 'config'); - } - } - - /** - * {@inheritdoc} - */ - public function load($uid, $cat) - { - $return = []; - - if (empty($uid)) { - return $return; - } - - if (!$this->isLoaded($uid, $cat, null)) { - return $return; - } - - $pconfigs = DBA::select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]); - while ($pconfig = DBA::fetch($pconfigs)) { - $value = $this->toConfigValue($pconfig['v']); - if (isset($value)) { - $return[$pconfig['cat']][$pconfig['k']] = $value; - } - } - DBA::close($pconfigs); - - $this->config_loaded[$uid] = true; - - return $return; - } - - /** - * {@inheritdoc} - */ - public function get($uid, $cat, $key) - { - if (!$this->isConnected()) { - return null; - } - - if (!$this->isLoaded($uid, $cat, $key)) { - $this->load($uid, $cat); - } - - $config = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $key]); - if (DBA::isResult($config)) { - $value = $this->toConfigValue($config['v']); - - if (isset($value)) { - return $value; - } - } - return null; - } - - /** - * {@inheritdoc} - */ - public function set($uid, $cat, $key, $value) - { - if (!$this->isConnected()) { - return false; - } - - if (!$this->isLoaded($uid, $cat, $key)) { - $this->load($uid, $cat); - } - // 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($uid, $cat, $key); - - if (isset($stored_value) && $stored_value === $compare_value) { - return true; - } - - $dbvalue = $this->toDbValue($value); - - return DBA::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true); - } - - /** - * {@inheritdoc} - */ - public function delete($uid, $cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - if (!$this->isLoaded($uid, $cat, $key)) { - $this->load($uid, $cat); - } - - return DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]); - } - - /** - * {@inheritdoc} - */ - public function isLoaded($uid, $cat, $key) - { - if (!$this->isConnected()) { - return false; - } - - return isset($this->config_loaded[$uid]) && $this->config_loaded[$uid]; - } -} diff --git a/src/Core/Config/Cache/PConfigCache.php b/src/Core/Config/Cache/PConfigCache.php index b03d86f41e..b0dd209cbf 100644 --- a/src/Core/Config/Cache/PConfigCache.php +++ b/src/Core/Config/Cache/PConfigCache.php @@ -34,8 +34,12 @@ class PConfigCache * @param int $uid * @param array $config */ - public function load(int $uid, array $config) + public function load($uid, array $config) { + if (!is_int($uid)) { + return; + } + $categories = array_keys($config); foreach ($categories as $category) { @@ -62,8 +66,12 @@ class PConfigCache * * @return null|string The value of the config entry or null if not set */ - public function get(int $uid, string $cat, string $key = null) + public function get($uid, string $cat, string $key = null) { + if (!is_int($uid)) { + return null; + } + if (isset($this->config[$uid][$cat][$key])) { return $this->config[$uid][$cat][$key]; } elseif (!isset($key) && isset($this->config[$uid][$cat])) { @@ -85,8 +93,12 @@ class PConfigCache * * @return bool Set successful */ - public function set(int $uid, string $cat, string $key, $value) + public function set($uid, string $cat, string $key, $value) { + if (!is_int($uid)) { + return false; + } + if (!isset($this->config[$uid]) || !is_array($this->config[$uid])) { $this->config[$uid] = []; } @@ -116,8 +128,12 @@ class PConfigCache * * @return bool true, if deleted */ - public function delete(int $uid, string $cat, string $key) + public function delete($uid, string $cat, string $key) { + if (!is_int($uid)) { + return false; + } + if (isset($this->config[$uid][$cat][$key])) { unset($this->config[$uid][$cat][$key]); if (count($this->config[$uid][$cat]) == 0) { diff --git a/src/Core/Config/JitPConfiguration.php b/src/Core/Config/JitPConfiguration.php new file mode 100644 index 0000000000..82fcbe110c --- /dev/null +++ b/src/Core/Config/JitPConfiguration.php @@ -0,0 +1,131 @@ +db_loaded = []; + } + + /** + * {@inheritDoc} + * + */ + public function load($uid, string $cat = 'config') + { + // If not connected or no uid, do nothing + if (!is_int($uid) || !$this->configModel->isConnected()) { + return; + } + + $config = $this->configModel->load($uid, $cat); + + if (!empty($config[$cat])) { + foreach ($config[$cat] as $key => $value) { + $this->db_loaded[$uid][$cat][$key] = true; + } + } + + // load the whole category out of the DB into the cache + $this->configCache->load($uid, $config); + } + + /** + * {@inheritDoc} + */ + public function get($uid, string $cat, string $key, $default_value = null, bool $refresh = false) + { + if (!is_int($uid)) { + return $default_value; + } + + // if the value isn't loaded or refresh is needed, load it to the cache + if ($this->configModel->isConnected() && + (empty($this->db_loaded[$uid][$cat][$key]) || + $refresh)) { + + $dbvalue = $this->configModel->get($uid, $cat, $key); + + if (isset($dbvalue)) { + $this->configCache->set($uid, $cat, $key, $dbvalue); + unset($dbvalue); + } + + $this->db_loaded[$uid][$cat][$key] = true; + } + + // use the config cache for return + $result = $this->configCache->get($uid, $cat, $key); + + return (isset($result)) ? $result : $default_value; + } + + /** + * {@inheritDoc} + */ + public function set($uid, string $cat, string $key, $value) + { + if (!is_int($uid)) { + return false; + } + + // set the cache first + $cached = $this->configCache->set($uid, $cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configModel->isConnected()) { + return $cached; + } + + $stored = $this->configModel->set($uid, $cat, $key, $value); + + $this->db_loaded[$uid][$cat][$key] = $stored; + + return $cached && $stored; + } + + /** + * {@inheritDoc} + */ + public function delete($uid, string $cat, string $key) + { + if (!is_int($uid)) { + return false; + } + + $cacheRemoved = $this->configCache->delete($uid, $cat, $key); + + if (isset($this->db_loaded[$uid][$cat][$key])) { + unset($this->db_loaded[$uid][$cat][$key]); + } + + if (!$this->configModel->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configModel->delete($uid, $cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Core/Config/PConfiguration.php b/src/Core/Config/PConfiguration.php index e69981c08c..a00da819a4 100644 --- a/src/Core/Config/PConfiguration.php +++ b/src/Core/Config/PConfiguration.php @@ -2,66 +2,70 @@ namespace Friendica\Core\Config; +use Friendica\Model; + /** * 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 values are set through the Config-DB-Table (per Config-DB-model @see Model\Config\PConfig) * * The configuration cache (@see Cache\PConfigCache ) is used for temporary caching of database calls. This will * increase the performance. */ -class PConfiguration +abstract class PConfiguration { /** * @var Cache\PConfigCache */ - private $configCache; + protected $configCache; /** - * @var Adapter\IPConfigAdapter + * @var Model\Config\PConfig */ - private $configAdapter; + protected $configModel; /** - * @param Cache\PConfigCache $configCache The configuration cache - * @param Adapter\IPConfigAdapter $configAdapter The configuration DB-backend + * @param Cache\PConfigCache $configCache The configuration cache + * @param Model\Config\PConfig $configModel The configuration model */ - public function __construct(Cache\PConfigCache $configCache, Adapter\IPConfigAdapter $configAdapter) + public function __construct(Cache\PConfigCache $configCache, Model\Config\PConfig $configModel) { $this->configCache = $configCache; - $this->configAdapter = $configAdapter; + $this->configModel = $configModel; } /** - * @brief Loads all configuration values of a user's config family into a cached storage. + * Returns the Config Cache + * + * @return Cache\PConfigCache + */ + public function getCache() + { + return $this->configCache; + } + + /** + * 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 PConfigCache ) + * the cache ( @param int $uid The user_id * - * @param string $uid The user_id * @param string $cat The category of the configuration value * * @return void + * @see PConfigCache ) + * */ - public function load($uid, $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($uid, $this->configAdapter->load($uid, $cat)); - } + abstract public function load($uid, string $cat = 'config'); /** - * @brief Get a particular user's config variable given the category name + * Get a particular user's config variable given the category name * ($cat) and a key. * * 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 PConfigCache ). * - * @param string $uid The user_id + * @param int $uid The user_id * @param string $cat The category of the configuration value * @param string $key The configuration key to query * @param mixed $default_value optional, The value to return if key is not set (default: null) @@ -69,78 +73,37 @@ class PConfiguration * * @return mixed Stored value or null if it does not exist */ - public function get($uid, $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($uid, $cat, $key) || - $refresh)) { - $dbValue = $this->configAdapter->get($uid, $cat, $key); - - if (isset($dbValue)) { - $this->configCache->set($uid, $cat, $key, $dbValue); - return $dbValue; - } - } - - // use the config cache for return - $result = $this->configCache->get($uid, $cat, $key); - return (isset($result)) ? $result : $default_value; - } + abstract public function get($uid, string $cat, string $key, $default_value = null, bool $refresh = false); /** - * @brief Sets a configuration value for a user + * Sets a configuration value for a user * * Stores a config value ($value) in the category ($family) under the key ($key) * for the user_id $uid. * * @note Please do not store booleans - convert to 0/1 integer values! * - * @param string $uid The user_id - * @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 int $uid The user_id + * @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($uid, $cat, $key, $value) - { - // set the cache first - $cached = $this->configCache->set($uid, $cat, $key, $value); - - // If there is no connected adapter, we're finished - if (!$this->configAdapter->isConnected()) { - return $cached; - } - - $stored = $this->configAdapter->set($uid, $cat, $key, $value); - - return $cached && $stored; - } + abstract public function set($uid, string $cat, string $key, $value); /** - * @brief Deletes the given key from the users's configuration. + * Deletes the given key from the users's configuration. * * Removes the configured value from the stored cache in $this->configCache * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ) - * with the given $uid. + * with the given $uid. * - * @param string $uid The user_id + * @param int $uid The user_id * @param string $cat The category of the configuration value * @param string $key The configuration key to delete * * @return bool */ - public function delete($uid, $cat, $key) - { - $cacheRemoved = $this->configCache->delete($uid, $cat, $key); - - if (!$this->configAdapter->isConnected()) { - return $cacheRemoved; - } - - $storeRemoved = $this->configAdapter->delete($uid, $cat, $key); - - return $cacheRemoved || $storeRemoved; - } + abstract public function delete($uid, string $cat, string $key); } diff --git a/src/Core/Config/PreloadConfiguration.php b/src/Core/Config/PreloadConfiguration.php index b5823c6144..d54f1b0636 100644 --- a/src/Core/Config/PreloadConfiguration.php +++ b/src/Core/Config/PreloadConfiguration.php @@ -5,7 +5,7 @@ namespace Friendica\Core\Config; use Friendica\Model; /** - * This class implements the preload Time configuration, which will cache + * 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. diff --git a/src/Core/Config/PreloadPConfiguration.php b/src/Core/Config/PreloadPConfiguration.php new file mode 100644 index 0000000000..dd1a72bafd --- /dev/null +++ b/src/Core/Config/PreloadPConfiguration.php @@ -0,0 +1,128 @@ +config_loaded = []; + } + + /** + * {@inheritDoc} + * + * This loads all config values everytime load is called + * + */ + public function load($uid, string $cat = 'config') + { + // Don't load the whole configuration twice or with invalid uid + if (!is_int($uid) || !empty($this->config_loaded[$uid])) { + return; + } + + // If not connected, do nothing + if (!$this->configModel->isConnected()) { + return; + } + + $config = $this->configModel->load($uid); + $this->config_loaded[$uid] = true; + + // load the whole category out of the DB into the cache + $this->configCache->load($uid, $config); + } + + /** + * {@inheritDoc} + */ + public function get($uid, string $cat, string $key, $default_value = null, bool $refresh = false) + { + if (!is_int($uid)) { + return $default_value; + } + + if (empty($this->config_loaded[$uid])) { + $this->load($uid); + } elseif ($refresh) { + if ($this->configModel->isConnected()) { + $config = $this->configModel->get($uid, $cat, $key); + if (isset($config)) { + $this->configCache->set($uid, $cat, $key, $config); + } + } + } + + // use the config cache for return + $result = $this->configCache->get($uid, $cat, $key); + + return (isset($result)) ? $result : $default_value; + } + + /** + * {@inheritDoc} + */ + public function set($uid, string $cat, string $key, $value) + { + if (!is_int($uid)) { + return false; + } + + if (empty($this->config_loaded[$uid])) { + $this->load($uid); + } + + // set the cache first + $cached = $this->configCache->set($uid, $cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configModel->isConnected()) { + return $cached; + } + + $stored = $this->configModel->set($uid, $cat, $key, $value); + + return $cached && $stored; + } + + /** + * {@inheritDoc} + */ + public function delete($uid, string $cat, string $key) + { + if (!is_int($uid)) { + return false; + } + + if (empty($this->config_loaded[$uid])) { + $this->load($uid); + } + + $cacheRemoved = $this->configCache->delete($uid, $cat, $key); + + if (!$this->configModel->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configModel->delete($uid, $cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php index 5a24f86283..559411d623 100644 --- a/src/Factory/ConfigFactory.php +++ b/src/Factory/ConfigFactory.php @@ -4,9 +4,9 @@ namespace Friendica\Factory; 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\Model\Config\PConfig as PConfigModel; use Friendica\Util\Config\ConfigFileLoader; class ConfigFactory @@ -48,20 +48,18 @@ class ConfigFactory /** * @param Cache\ConfigCache $configCache The config cache * @param Cache\PConfigCache $pConfigCache The personal config cache - * @param int $uid The UID of the current user + * @param PConfigModel $configModel The configuration model * * @return Config\PConfiguration */ - public static function createPConfig(Cache\ConfigCache $configCache, Cache\PConfigCache $pConfigCache, $uid = null) + public static function createPConfig(Cache\ConfigCache $configCache, Cache\PConfigCache $pConfigCache, PConfigModel $configModel) { if ($configCache->get('system', 'config_adapter') === 'preload') { - $configAdapter = new Adapter\PreloadPConfigAdapter($uid); + $configuration = new Config\PreloadPConfiguration($pConfigCache, $configModel); } else { - $configAdapter = new Adapter\JITPConfigAdapter(); + $configuration = new Config\JitPConfiguration($pConfigCache, $configModel); } - $configuration = new Config\PConfiguration($pConfigCache, $configAdapter); - // Set the config in the static container for legacy usage Core\PConfig::init($configuration); diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php index 36ab20a013..d444f5d2f3 100644 --- a/src/Factory/DependencyFactory.php +++ b/src/Factory/DependencyFactory.php @@ -34,7 +34,8 @@ class DependencyFactory $configModel = new \Friendica\Model\Config\Config($database); $config = Factory\ConfigFactory::createConfig($configCache, $configModel); // needed to call PConfig::init() - Factory\ConfigFactory::createPConfig($configCache, new PConfigCache()); + $pconfigModel = new \Friendica\Model\Config\PConfig($database); + Factory\ConfigFactory::createPConfig($configCache, new PConfigCache(), $pconfigModel); $logger = Factory\LoggerFactory::create($channel, $database, $config, $profiler); Factory\LoggerFactory::createDev($channel, $config, $profiler); $baseURL = new BaseURL($config, $_SERVER); diff --git a/src/Model/Config/PConfig.php b/src/Model/Config/PConfig.php new file mode 100644 index 0000000000..2969272cc6 --- /dev/null +++ b/src/Model/Config/PConfig.php @@ -0,0 +1,135 @@ +dba->select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]); + } else { + $configs = $this->dba->select('üconfig', ['cat', 'v', 'k'], ['cat' => $cat, 'uid' => $uid]); + } + + 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 user 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 int $uid The id of the user to load + * @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(int $uid, string $cat, string $key) + { + if (!$this->isConnected()) { + return null; + } + + $config = $this->dba->selectFirst('pconfig', ['v'], ['uid' => $uid, '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) for a + * given user ($uid). + * + * Note: Please do not store booleans - convert to 0/1 integer values! + * + * @param int $uid The id of the user to load + * @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(int $uid, 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($uid, $cat, $key); + + if (isset($stored_value) && ($stored_value === $compare_value)) { + return true; + } + + $dbvalue = $this->toDbValue($value); + + $result = $this->dba->update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true); + + return $result; + } + + /** + * Removes the configured value of the given user. + * + * @param int $uid The id of the user to load + * @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(int $uid, string $cat, string $key) + { + if (!$this->isConnected()) { + return false; + } + + return $this->dba->delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]); + } +} diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index 5ae4bdff50..902445baa2 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -7,6 +7,7 @@ namespace Friendica\Test; use Friendica\App; use Friendica\Core\Config; +use Friendica\Core\Config\Cache\PConfigCache; use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\System; @@ -49,7 +50,8 @@ class ApiTest extends DatabaseTest { $configModel = new \Friendica\Model\Config\Config(self::$dba); $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel); - Factory\ConfigFactory::createPConfig(self::$configCache, new Config\Cache\PConfigCache()); + $pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba); + Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel); $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler); $baseUrl = new BaseURL($config, $_SERVER); $router = new App\Router(); diff --git a/tests/src/Core/Config/Cache/PConfigCacheTest.php b/tests/src/Core/Config/Cache/PConfigCacheTest.php index 22beccf601..283a5bc6ab 100644 --- a/tests/src/Core/Config/Cache/PConfigCacheTest.php +++ b/tests/src/Core/Config/Cache/PConfigCacheTest.php @@ -255,4 +255,21 @@ class PConfigCacheTest extends MockedTest $this->assertNull($configCache->get(1, 'cat2', 'key2')); $this->assertNull($configCache->get(2, 'cat1', 'key1')); } + + /** + * Test when using an invalid UID + * @todo check it the clean way before using the config class + */ + public function testInvalidUid() + { + // bad UID! + $uid = null; + + $configCache = new PConfigCache(); + + $this->assertNull($configCache->get($uid, 'cat1', 'cat2')); + + $this->assertFalse($configCache->set($uid, 'cat1', 'key1', 'doesn\'t matter!')); + $this->assertFalse($configCache->delete($uid, 'cat1', 'key1')); + } } diff --git a/tests/src/Core/Config/JitPConfigurationTest.php b/tests/src/Core/Config/JitPConfigurationTest.php new file mode 100644 index 0000000000..4eafb43b3d --- /dev/null +++ b/tests/src/Core/Config/JitPConfigurationTest.php @@ -0,0 +1,154 @@ +configCache, $this->configModel); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testLoad(int $uid, array $data, array $possibleCats, array $load) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(count($load)); + + foreach ($load as $loadCat) { + $this->configModel->shouldReceive('load') + ->with($uid, $loadCat) + ->andReturn([$loadCat => $data[$loadCat]]) + ->once(); + } + + parent::testLoad($uid, $data, $possibleCats, $load); + } + + /** + * @dataProvider dataDoubleLoad + */ + public function testCacheLoadDouble(int $uid, array $data1, array $data2, array $expect) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(count($data1) + count($data2)); + + foreach ($data1 as $cat => $data) { + $this->configModel->shouldReceive('load') + ->with($uid, $cat) + ->andReturn([$cat => $data]) + ->once(); + } + + + foreach ($data2 as $cat => $data) { + $this->configModel->shouldReceive('load') + ->with($uid, $cat) + ->andReturn([$cat => $data]) + ->once(); + } + + parent::testCacheLoadDouble($uid, $data1, $data2, $expect); + + // Assert the expected categories + foreach ($data2 as $cat => $data) { + $this->assertConfig($uid, $cat, $expect[$cat]); + } + } + + /** + * @dataProvider dataTests + */ + public function testSetGetWithoutDB(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(2); + + parent::testSetGetWithoutDB($uid, $data); + } + + /** + * @dataProvider dataTests + */ + public function testSetGetWithDB(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(2); + + parent::testSetGetWithDB($uid, $data); + } + + /** + * @dataProvider dataTests + */ + public function testGetWithRefresh(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(3); + + // mocking one get without result + $this->configModel->shouldReceive('get') + ->with($uid, 'test', 'it') + ->andReturn(null) + ->once(); + + // mocking the data get + $this->configModel->shouldReceive('get') + ->with($uid, 'test', 'it') + ->andReturn($data) + ->once(); + + // mocking second get + $this->configModel->shouldReceive('get') + ->with($uid, 'test', 'not') + ->andReturn(null) + ->once(); + + parent::testGetWithRefresh($uid, $data); + } + + public function testGetWrongWithoutDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(3); + + parent::testGetWrongWithoutDB(); + } + + /** + * @dataProvider dataTests + */ + public function testDeleteWithoutDB(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(3); + + parent::testDeleteWithoutDB($uid, $data); + } + + public function testDeleteWithDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(5); + + // mocking one get without result + $this->configModel->shouldReceive('get') + ->with(0, 'test', 'it') + ->andReturn(null) + ->once(); + + parent::testDeleteWithDB(); + } +} diff --git a/tests/src/Core/Config/PConfigurationTest.php b/tests/src/Core/Config/PConfigurationTest.php index f2f1857a69..1d5e9c0a17 100644 --- a/tests/src/Core/Config/PConfigurationTest.php +++ b/tests/src/Core/Config/PConfigurationTest.php @@ -2,110 +2,294 @@ namespace Friendica\Test\src\Core\Config; -use Friendica\Core\Config\Adapter\IPConfigAdapter; use Friendica\Core\Config\Cache\PConfigCache; use Friendica\Core\Config\PConfiguration; +use Friendica\Model\Config\PConfig as PConfigModel; use Friendica\Test\MockedTest; +use Mockery; +use Mockery\MockInterface; -class PConfigurationTest extends MockedTest +abstract class PConfigurationTest extends MockedTest { + /** @var PConfigModel|MockInterface */ + protected $configModel; + + /** @var PConfigCache */ + protected $configCache; + + /** @var PConfiguration */ + protected $testedConfig; + + /** + * Assert a config tree + * + * @param int $uid The uid to assert + * @param string $cat The category to assert + * @param array $data The result data array + */ + protected function assertConfig(int $uid, string $cat, array $data) + { + $result = $this->testedConfig->getCache()->getAll(); + + $this->assertNotEmpty($result); + $this->assertArrayHasKey($uid, $result); + $this->assertArrayHasKey($cat, $result[$uid]); + $this->assertArraySubset($data, $result[$uid][$cat]); + } + + + protected function setUp() + { + parent::setUp(); + + // Create the config model + $this->configModel = Mockery::mock(PConfigModel::class); + $this->configCache = new PConfigCache(); + } + + /** + * @return PConfiguration + */ + public abstract function getInstance(); + public function dataTests() { return [ - 'string' => ['data' => 'it'], - 'boolTrue' => ['data' => true], - 'boolFalse' => ['data' => false], - 'integer' => ['data' => 235], - 'decimal' => ['data' => 2.456], - 'array' => ['data' => ['1', 2, '3', true, false]], - 'boolIntTrue' => ['data' => 1], - 'boolIntFalse' => ['Data' => 0], + 'string' => ['uid' => 1, 'data' => 'it'], + 'boolTrue' => ['uid' => 2, 'data' => true], + 'boolFalse' => ['uid' => 3, 'data' => false], + 'integer' => ['uid' => 4, 'data' => 235], + 'decimal' => ['uid' => 5, 'data' => 2.456], + 'array' => ['uid' => 6, 'data' => ['1', 2, '3', true, false]], + 'boolIntTrue' => ['uid' => 7, 'data' => 1], + 'boolIntFalse' => ['uid' => 8, 'data' => 0], ]; } + public function dataConfigLoad() + { + $data = [ + 'system' => [ + 'key1' => 'value1', + 'key2' => 'value2', + 'key3' => 'value3', + ], + 'config' => [ + 'key1' => 'value1a', + 'key4' => 'value4', + ], + 'other' => [ + 'key5' => 'value5', + 'key6' => 'value6', + ], + ]; + + return [ + 'system' => [ + 'uid' => 1, + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'system', + ], + ], + 'other' => [ + 'uid' => 2, + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'other', + ], + ], + 'config' => [ + 'uid' => 3, + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'config', + ], + ], + 'all' => [ + 'uid' => 4, + 'data' => $data, + 'possibleCats' => [ + 'system', + 'config', + 'other' + ], + 'load' => [ + 'system', + 'config', + 'other' + ], + ], + ]; + } + + /** + * Test the configuration initialization + * @dataProvider dataConfigLoad + */ + public function testSetUp(int $uid, array $data) + { + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); + + $this->assertEmpty($this->testedConfig->getCache()->getAll()); + } + /** * Test the configuration load() method */ - public function testCacheLoad() + public function testLoad(int $uid, array $data, array $possibleCats, array $load) { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->twice(); - // expected loading - $configAdapter->shouldReceive('load') - ->with($uid, 'testing') - ->andReturn(['testing' => ['test' => 'it']]) - ->once(); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'testing', 'test')->andReturn(true)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); - $configuration = new PConfiguration($configCache, $configAdapter); - $configuration->load($uid, 'testing'); + foreach ($load as $loadedCats) { + $this->testedConfig->load($uid, $loadedCats); + } - $this->assertEquals('it', $configuration->get($uid, 'testing', 'test')); + // Assert at least loaded cats are loaded + foreach ($load as $loadedCats) { + $this->assertConfig($uid, $loadedCats, $data[$loadedCats]); + } + } + + public function dataDoubleLoad() + { + return [ + 'config' => [ + 'uid' => 1, + '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' => [ + 'uid' => 1, + '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(int $uid, array $data1, array $data2, array $expect) { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); - // expected loading - $configAdapter->shouldReceive('load')->with($uid, 'testing')->andReturn(['testing' => ['test' => 'it']])->once(); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'testing', 'test')->andReturn(true)->twice(); - // expected next loading - $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'again']])->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); - $configuration = new PConfiguration($configCache, $configAdapter); - $configuration->load($uid, 'testing'); + foreach ($data1 as $cat => $data) { + $this->testedConfig->load($uid, $cat); + } - $this->assertEquals('it', $configuration->get($uid, 'testing', 'test')); + // Assert at least loaded cats are loaded + foreach ($data1 as $cat => $data) { + $this->assertConfig($uid, $cat, $data); + } - $configuration->load($uid, 'testing'); - - $this->assertEquals('again', $configuration->get($uid, 'testing', 'test')); + foreach ($data2 as $cat => $data) { + $this->testedConfig->load($uid, $cat); + } } /** * Test the configuration get() and set() methods without adapter + * * @dataProvider dataTests */ - public function testSetGetWithoutDB($data) + public function testSetGetWithoutDB(int $uid, $data) { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(2); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); - $configuration = new PConfiguration($configCache, $configAdapter); + $this->assertTrue($this->testedConfig->set($uid, 'test', 'it', $data)); - $this->assertTrue($configuration->set($uid, 'test', 'it', $data)); - - $this->assertEquals($data, $configuration->get($uid, 'test', 'it')); + $this->assertEquals($data, $this->testedConfig->get($uid, 'test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get($uid, '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) + public function testSetGetWithDB(int $uid, $data) { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(2); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once(); - $configAdapter->shouldReceive('set')->with($uid, 'test', 'it', $data)->andReturn(true)->once(); + $this->configModel->shouldReceive('set') + ->with($uid, 'test', 'it', $data) + ->andReturn(true) + ->once(); - $configuration = new PConfiguration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); - $this->assertTrue($configuration->set($uid, 'test', 'it', $data)); + $this->assertTrue($this->testedConfig->set($uid, 'test', 'it', $data)); - $this->assertEquals($data, $configuration->get($uid, 'test', 'it')); + $this->assertEquals($data, $this->testedConfig->get($uid, 'test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get($uid, 'test', 'it')); } /** @@ -113,135 +297,177 @@ class PConfigurationTest extends MockedTest */ public function testGetWrongWithoutDB() { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(3); - - $configuration = new PConfiguration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); // without refresh - $this->assertNull($configuration->get($uid, 'test', 'it')); + $this->assertNull($this->testedConfig->get(0, 'test', 'it')); + + /// beware that the cache returns '!!' and not null for a non existing value + $this->assertNull($this->testedConfig->getCache()->get(0, 'test', 'it')); // with default value - $this->assertEquals('default', $configuration->get($uid, 'test', 'it', 'default')); + $this->assertEquals('default', $this->testedConfig->get(0, 'test', 'it', 'default')); // with default value and refresh - $this->assertEquals('default', $configuration->get($uid, 'test', 'it', 'default', true)); + $this->assertEquals('default', $this->testedConfig->get(0, 'test', 'it', 'default', true)); } /** * Test the configuration get() method with refresh + * * @dataProvider dataTests */ - public function testGetWithRefresh($data) + public function testGetWithRefresh(int $uid, $data) { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn('now')->once(); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->twice(); - $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn($data)->once(); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'not')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with($uid, 'test', 'not')->andReturn(null)->once(); + $this->configCache->load($uid, ['test' => ['it' => 'now']]); - $configuration = new PConfiguration($configCache, $configAdapter); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); // without refresh - $this->assertEquals('now', $configuration->get($uid, 'test', 'it')); - // use the cache again - $this->assertEquals('now', $configuration->get($uid, 'test', 'it')); + $this->assertEquals('now', $this->testedConfig->get($uid, 'test', 'it')); + $this->assertEquals('now', $this->testedConfig->getCache()->get($uid, 'test', 'it')); - // with refresh (and load the second value out of the db) - $this->assertEquals($data, $configuration->get($uid, 'test', 'it', null, true)); + // with refresh + $this->assertEquals($data, $this->testedConfig->get($uid, 'test', 'it', null, true)); + $this->assertEquals($data, $this->testedConfig->getCache()->get($uid, 'test', 'it')); // without refresh and wrong value and default - $this->assertEquals('default', $configuration->get($uid, 'test', 'not', 'default')); + $this->assertEquals('default', $this->testedConfig->get($uid, 'test', 'not', 'default')); + $this->assertNull($this->testedConfig->getCache()->get($uid, 'test', 'not')); } /** - * Test the configuration get() method with different isLoaded settings + * Test the configuration delete() method without a model/db + * * @dataProvider dataTests */ - public function testGetWithoutLoaded($data) + public function testDeleteWithoutDB(int $uid, $data) { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); + $this->configCache->load($uid, ['test' => ['it' => $data]]); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn(null)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once(); - $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn($data)->once(); + $this->assertEquals($data, $this->testedConfig->get($uid, 'test', 'it')); + $this->assertEquals($data, $this->testedConfig->getCache()->get($uid, 'test', 'it')); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once(); + $this->assertTrue($this->testedConfig->delete($uid, 'test', 'it')); + $this->assertNull($this->testedConfig->get($uid, 'test', 'it')); + $this->assertNull($this->testedConfig->getCache()->get($uid, 'test', 'it')); - $configuration = new PConfiguration($configCache, $configAdapter); - - // first run is not loaded and no data is found in the DB - $this->assertNull($configuration->get($uid, 'test', 'it')); - - // second run is not loaded, but now data is found in the db (overwrote cache) - $this->assertEquals($data, $configuration->get($uid,'test', 'it')); - - // third run is loaded and therefore cache is used - $this->assertEquals($data, $configuration->get($uid,'test', 'it')); + $this->assertEmpty($this->testedConfig->getCache()->getAll()); } /** - * Test the configuration delete() method without adapter - * @dataProvider dataTests - */ - public function testDeleteWithoutDB($data) - { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); - - $configuration = new PConfiguration($configCache, $configAdapter); - - $this->assertTrue($configuration->set($uid, 'test', 'it', $data)); - $this->assertEquals($data, $configuration->get($uid, 'test', 'it')); - - $this->assertTrue($configuration->delete($uid, 'test', 'it')); - $this->assertNull($configuration->get($uid, 'test', 'it')); - } - - /** - * Test the configuration delete() method with adapter + * Test the configuration delete() method with a model/db */ public function testDeleteWithDB() { - $uid = 234; - $configCache = new PConfigCache(); - $configAdapter = \Mockery::mock(IPConfigAdapter::class); - $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(6); - $configAdapter->shouldReceive('set')->with($uid, 'test', 'it', 'now')->andReturn(false)->once(); - $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once(); + $this->configCache->load(0, ['test' => ['it' => 'now', 'quarter' => 'true']]); - $configAdapter->shouldReceive('delete')->with($uid, 'test', 'it')->andReturn(false)->once(); + $this->configModel->shouldReceive('delete') + ->with(0, 'test', 'it') + ->andReturn(false) + ->once(); + $this->configModel->shouldReceive('delete') + ->with(0, 'test', 'second') + ->andReturn(true) + ->once(); + $this->configModel->shouldReceive('delete') + ->with(0, 'test', 'third') + ->andReturn(false) + ->once(); + $this->configModel->shouldReceive('delete') + ->with(0, 'test', 'quarter') + ->andReturn(true) + ->once(); - $configAdapter->shouldReceive('delete')->with($uid, 'test', 'second')->andReturn(true)->once(); - $configAdapter->shouldReceive('delete')->with($uid, 'test', 'third')->andReturn(false)->once(); - $configAdapter->shouldReceive('delete')->with($uid, 'test', 'quarter')->andReturn(true)->once(); + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); - $configuration = new PConfiguration($configCache, $configAdapter); + // directly set the value to the cache + $this->testedConfig->getCache()->set(0, 'test', 'it', 'now'); - $this->assertFalse($configuration->set($uid, 'test', 'it', 'now')); - $this->assertEquals('now', $configuration->get($uid, 'test', 'it')); + $this->assertEquals('now', $this->testedConfig->get(0, 'test', 'it')); + $this->assertEquals('now', $this->testedConfig->getCache()->get(0, 'test', 'it')); - // delete from set - $this->assertTrue($configuration->delete($uid, 'test', 'it')); + // delete from cache only + $this->assertTrue($this->testedConfig->delete(0, 'test', 'it')); // delete from db only - $this->assertTrue($configuration->delete($uid, 'test', 'second')); + $this->assertTrue($this->testedConfig->delete(0, 'test', 'second')); // no delete - $this->assertFalse($configuration->delete($uid, 'test', 'third')); + $this->assertFalse($this->testedConfig->delete(0, 'test', 'third')); // delete both - $this->assertTrue($configuration->delete($uid, 'test', 'quarter')); + $this->assertTrue($this->testedConfig->delete(0, 'test', 'quarter')); + + $this->assertEmpty($this->testedConfig->getCache()->getAll()); + } + + public function dataMultiUid() + { + return [ + 'normal' => [ + 'data1' => [ + 'uid' => 1, + 'data' => [ + 'cat1' => [ + 'key1' => 'value1', + ], + 'cat2' => [ + 'key2' => 'value2', + ] + ], + ], + 'data2' => [ + 'uid' => 2, + 'data' => [ + 'cat1' => [ + 'key1' => 'value1a', + ], + 'cat2' => [ + 'key2' => 'value2', + ], + ], + ], + ], + ]; + } + + /** + * Test if multiple uids for caching are usable without errors + * @dataProvider dataMultiUid + */ + public function testMultipleUidsWithCache(array $data1, array $data2) + { + $this->configCache->load($data1['uid'], $data1['data']); + $this->configCache->load($data2['uid'], $data2['data']); + + $this->testedConfig = $this->getInstance(); + $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache()); + + $this->assertConfig($data1['uid'], 'cat1', $data1['data']['cat1']); + $this->assertConfig($data1['uid'], 'cat2', $data1['data']['cat2']); + $this->assertConfig($data2['uid'], 'cat1', $data2['data']['cat1']); + $this->assertConfig($data2['uid'], 'cat2', $data2['data']['cat2']); + } + + /** + * Test when using an invalid UID + * @todo check it the clean way before using the config class + */ + public function testInvalidUid() + { + // bad UID! + $uid = null; + + $this->testedConfig = $this->getInstance(); + + $this->assertNull($this->testedConfig->get($uid, 'cat1', 'cat2')); + $this->assertEquals('fallback!', $this->testedConfig->get($uid, 'cat1', 'cat2', 'fallback!')); + + $this->assertFalse($this->testedConfig->set($uid, 'cat1', 'key1', 'doesn\'t matter!')); + $this->assertFalse($this->testedConfig->delete($uid, 'cat1', 'key1')); } } diff --git a/tests/src/Core/Config/PreloadPConfigurationTest.php b/tests/src/Core/Config/PreloadPConfigurationTest.php new file mode 100644 index 0000000000..ca916d4404 --- /dev/null +++ b/tests/src/Core/Config/PreloadPConfigurationTest.php @@ -0,0 +1,147 @@ +configCache, $this->configModel); + } + + /** + * @dataProvider dataConfigLoad + */ + public function testLoad(int $uid, array $data, array $possibleCats, array $load) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); + + $this->configModel->shouldReceive('load') + ->with($uid) + ->andReturn($data) + ->once(); + + parent::testLoad($uid, $data, $possibleCats, $load); + + // Assert that every category is loaded everytime + foreach ($data as $cat => $values) { + $this->assertConfig($uid, $cat, $values); + } + } + + /** + * @dataProvider dataDoubleLoad + */ + public function testCacheLoadDouble(int $uid, array $data1, array $data2, array $expect) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->once(); + + $this->configModel->shouldReceive('load') + ->with($uid) + ->andReturn($data1) + ->once(); + + parent::testCacheLoadDouble($uid, $data1, $data2, $expect); + + // Assert that every category is loaded everytime and is NOT overwritten + foreach ($data1 as $cat => $values) { + $this->assertConfig($uid, $cat, $values); + } + } + + /** + * @dataProvider dataTests + */ + public function testSetGetWithoutDB(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(3); + + parent::testSetGetWithoutDB($uid, $data); + } + + /** + * @dataProvider dataTests + */ + public function testSetGetWithDB(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->twice(); + + $this->configModel->shouldReceive('load') + ->with($uid) + ->andReturn(['config' => []]) + ->once(); + + parent::testSetGetWithDB($uid, $data); + } + + /** + * @dataProvider dataTests + */ + public function testGetWithRefresh(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(2); + + // constructor loading + $this->configModel->shouldReceive('load') + ->with($uid) + ->andReturn(['config' => []]) + ->once(); + + // mocking one get + $this->configModel->shouldReceive('get') + ->with($uid, 'test', 'it') + ->andReturn($data) + ->once(); + + parent::testGetWithRefresh($uid, $data); + } + + + public function testGetWrongWithoutDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(3); + + parent::testGetWrongWithoutDB(); + } + + /** + * @dataProvider dataTests + */ + public function testDeleteWithoutDB(int $uid, $data) + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(false) + ->times(4); + + parent::testDeleteWithoutDB($uid, $data); + } + + public function testDeleteWithDB() + { + $this->configModel->shouldReceive('isConnected') + ->andReturn(true) + ->times(5); + + // constructor loading + $this->configModel->shouldReceive('load') + ->with(0) + ->andReturn(['config' => []]) + ->once(); + + parent::testDeleteWithDB(); + } +} diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index 852db4498f..530430feb1 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -3,6 +3,7 @@ namespace Friendica\Test\src\Database; use Friendica\App; use Friendica\Core\Config; +use Friendica\Core\Config\Cache\PConfigCache; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Test\DatabaseTest; @@ -14,7 +15,8 @@ class DBATest extends DatabaseTest { $configModel = new \Friendica\Model\Config\Config(self::$dba); $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel); - Factory\ConfigFactory::createPConfig(self::$configCache, new Config\Cache\PConfigCache()); + $pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba); + Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel); $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler); $baseUrl = new BaseURL($config, $_SERVER); $router = new App\Router(); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index d10f07493b..64d249f4c0 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -19,7 +19,8 @@ class DBStructureTest extends DatabaseTest { $configModel = new Config(self::$dba); $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel); - Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache()); + $pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba); + Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel); $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler); $baseUrl = new BaseURL($config, $_SERVER); $router = new App\Router();