]> git.mxchange.org Git - friendica.git/commitdiff
Refactor PConfiguration
authorPhilipp Holzer <admin+github@philipp.info>
Mon, 15 Jul 2019 18:13:53 +0000 (20:13 +0200)
committerPhilipp Holzer <admin+github@philipp.info>
Mon, 15 Jul 2019 18:13:53 +0000 (20:13 +0200)
16 files changed:
src/Core/Config/Adapter/AbstractDbaConfigAdapter.php [deleted file]
src/Core/Config/Adapter/IPConfigAdapter.php [deleted file]
src/Core/Config/Adapter/JITPConfigAdapter.php [deleted file]
src/Core/Config/Adapter/PreloadPConfigAdapter.php [deleted file]
src/Core/Config/JitPConfiguration.php [new file with mode: 0644]
src/Core/Config/PConfiguration.php
src/Core/Config/PreloadPConfiguration.php [new file with mode: 0644]
src/Factory/ConfigFactory.php
src/Factory/DependencyFactory.php
src/Model/Config/PConfig.php [new file with mode: 0644]
tests/include/ApiTest.php
tests/src/Core/Config/JitPConfigurationTest.php [new file with mode: 0644]
tests/src/Core/Config/PConfigurationTest.php
tests/src/Core/Config/PreloadPConfigurationTest.php [new file with mode: 0644]
tests/src/Database/DBATest.php
tests/src/Database/DBStructureTest.php

diff --git a/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php b/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php
deleted file mode 100644 (file)
index 38caf35..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-namespace Friendica\Core\Config\Adapter;
-
-use Friendica\Database\DBA;
-
-abstract class AbstractDbaConfigAdapter
-{
-       /**
-        * The connection state of the adapter
-        *
-        * @var bool
-        */
-       protected $connected = true;
-
-       public function __construct()
-       {
-               $this->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 (file)
index c505532..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-
-namespace Friendica\Core\Config\Adapter;
-
-/**
- *
- * @author benlo
- */
-interface IPConfigAdapter
-{
-       /**
-        * Loads all configuration values of a user's config family and returns the loaded category as an array.
-        *
-        * @param string $uid The user_id
-        * @param string $cat The category of the configuration value
-        *
-        * @return array
-        */
-       public function load($uid, $cat);
-
-       /**
-        * Get a particular user's config variable given the category name
-        * ($family) and a key.
-        *
-        * Note: Boolean variables are defined as 0/1 in the database
-        *
-        * @param string  $uid           The user_id
-        * @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($uid, $cat, $key);
-
-       /**
-        * 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 string $value The value to store
-        *
-        * @return bool Operation success
-        */
-       public function set($uid, $cat, $key, $value);
-
-       /**
-        * Removes the configured value from the stored cache
-        * and removes it from the database.
-        *
-        * @param string $uid The user_id
-        * @param string $cat The category of the configuration value
-        * @param string $key The configuration key to delete
-        *
-        * @return bool Operation success
-        */
-       public function delete($uid, $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 for the user_id $uid.
-        *
-        * @param string $uid The user_id
-        * @param string $cat The configuration category
-        * @param string $key The configuration key
-        *
-        * @return bool
-        */
-       public function isLoaded($uid, $cat, $key);
-}
diff --git a/src/Core/Config/Adapter/JITPConfigAdapter.php b/src/Core/Config/Adapter/JITPConfigAdapter.php
deleted file mode 100644 (file)
index a0c6a95..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-namespace Friendica\Core\Config\Adapter;
-
-use Friendica\Database\DBA;
-
-/**
- * JustInTime User Configuration Adapter
- *
- * Default PConfig Adapter. Provides the best performance for pages loading few configuration variables.
- *
- * @author Hypolite Petovan <hypolite@mrpetovan.com>
- */
-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 (file)
index 838f376..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-namespace Friendica\Core\Config\Adapter;
-
-use Friendica\Database\DBA;
-
-/**
- * Preload User Configuration Adapter
- *
- * Minimizes the number of database queries to retrieve configuration values at the cost of memory.
- *
- * @author Hypolite Petovan <hypolite@mrpetovan.com>
- */
-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/JitPConfiguration.php b/src/Core/Config/JitPConfiguration.php
new file mode 100644 (file)
index 0000000..8ad65ef
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+use Friendica\Model;
+
+/**
+ * This class implements the Just-In-Time configuration, which will cache
+ * user config values in a cache, once they are retrieved.
+ *
+ * Default Configuration type.
+ * Provides the best performance for pages loading few configuration variables.
+ */
+class JitPConfiguration extends PConfiguration
+{
+       /**
+        * @var array Array of already loaded db values (even if there was no value)
+        */
+       private $db_loaded;
+
+       /**
+        * @param Cache\PConfigCache   $configCache The configuration cache
+        * @param Model\Config\PConfig $configModel The configuration model
+        */
+       public function __construct(Cache\PConfigCache $configCache, Model\Config\PConfig $configModel)
+       {
+               parent::__construct($configCache, $configModel);
+               $this->db_loaded = [];
+       }
+
+       /**
+        * {@inheritDoc}
+        *
+        */
+       public function load(int $uid, string $cat = 'config')
+       {
+               // If not connected, do nothing
+               if (!$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(int $uid, 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->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(int $uid, string $cat, string $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->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(int $uid, string $cat, string $key)
+       {
+               $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;
+       }
+}
index e69981c08c182543079b96d93de0b30cc2a84dd5..0dd3a8d3674b842e4b20feb44b119a93a0965ace 100644 (file)
@@ -2,6 +2,8 @@
 
 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 )
@@ -9,138 +11,101 @@ namespace Friendica\Core\Config;
  * 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;
+       }
+
+       /**
+        * Returns the Config Cache
+        *
+        * @return Cache\PConfigCache
+        */
+       public function getCache()
+       {
+               return $this->configCache;
        }
 
        /**
-        * @brief Loads all configuration values of a user's config family into a cached storage.
+        * 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(int $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 int $uid The user_id
         *
-        * @param string  $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)
         * @param boolean $refresh       optional, If true the config is loaded from the db and not from the cache (default: false)
         *
         * @return mixed Stored value or null if it does not exist
+        * @see IConfigAdapter ) or from the $this->configCache (@see PConfigCache ).
+        *
         */
-       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(int $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(int $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.
+        * (@param int $uid The user_id
         *
-        * @param string $uid The user_id
         * @param string $cat The category of the configuration value
         * @param string $key The configuration key to delete
         *
         * @return bool
+        * @see ConfigCache ) and removes it from the database (@see IConfigAdapter )
+        *      with the given $uid.
+        *
         */
-       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(int $uid, string $cat, string $key);
 }
diff --git a/src/Core/Config/PreloadPConfiguration.php b/src/Core/Config/PreloadPConfiguration.php
new file mode 100644 (file)
index 0000000..1682b7e
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+use Friendica\Model;
+
+/**
+ * This class implements the preload Time configuration, which will cache
+ * all user config values per call in a cache.
+ *
+ * Minimizes the number of database queries to retrieve configuration values at the cost of memory.
+ */
+class PreloadPConfiguration extends PConfiguration
+{
+       /** @var array */
+       private $config_loaded;
+
+       /**
+        * @param Cache\PConfigCache   $configCache The configuration cache
+        * @param Model\Config\PConfig $configModel The configuration model
+        */
+       public function __construct(Cache\PConfigCache $configCache, Model\Config\PConfig $configModel)
+       {
+               parent::__construct($configCache, $configModel);
+               $this->config_loaded = [];
+       }
+
+       /**
+        * {@inheritDoc}
+        *
+        * This loads all config values everytime load is called
+        *
+        */
+       public function load(int $uid, string $cat = 'config')
+       {
+               // Don't load the whole configuration twice
+               if (!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(int $uid, string $cat, string $key, $default_value = null, bool $refresh = false)
+       {
+               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(int $uid, string $cat, string $key, $value)
+       {
+               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(int $uid, string $cat, string $key)
+       {
+               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;
+       }
+}
index 5a24f86283b4817d4b08e45457ee59220205a92f..559411d62370425b605410cfa9918bbe0188d3bb 100644 (file)
@@ -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);
 
index 36ab20a0134b2e9cb02ebcaae081ddde282bf6bb..d444f5d2f3837f00f345db3f76f5055d682dccd1 100644 (file)
@@ -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 (file)
index 0000000..c76e41c
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+namespace Friendica\Model\Config;
+
+
+/**
+ * The Config model backend for users, which is using the general DB-model backend for user-configs
+ */
+class PConfig extends DbaConfig
+{
+       /**
+        * Loads all configuration values and returns the loaded category as an array.
+        *
+        * @param int         $uid The id of the user to load
+        * @param string|null $cat The category of the configuration values to load
+        *
+        * @return array The config array
+        *
+        * @throws \Exception In case DB calls are invalid
+        */
+       public function load(int $uid, string $cat = null)
+       {
+               $return = [];
+
+               if (empty($cat)) {
+                       $configs = $this->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]);
+       }
+}
index 5ae4bdff5074eecd7e6c4e7bec09f62c7c17451a..902445baa2055e553db7420df3e164a71002cbc4 100644 (file)
@@ -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/JitPConfigurationTest.php b/tests/src/Core/Config/JitPConfigurationTest.php
new file mode 100644 (file)
index 0000000..4eafb43
--- /dev/null
@@ -0,0 +1,154 @@
+<?php
+
+namespace Friendica\Test\src\Core\Config;
+
+use Friendica\Core\Config\JitPConfiguration;
+
+class JitPConfigurationTest extends PConfigurationTest
+{
+       public function getInstance()
+       {
+               return new JitPConfiguration($this->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();
+       }
+}
index f2f1857a69b9667daed508d061f05b26bcd92a12..adc9f267a1da7404606279112dd669ad057c9166 100644 (file)
 
 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();
-
-               $configuration = new PConfiguration($configCache, $configAdapter);
-               $configuration->load($uid, 'testing');
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache());
 
-               $this->assertEquals('it', $configuration->get($uid, 'testing', 'test'));
+               foreach ($data1 as $cat => $data) {
+                       $this->testedConfig->load($uid, $cat);
+               }
 
-               $configuration->load($uid, 'testing');
+               // Assert at least loaded cats are loaded
+               foreach ($data1 as $cat => $data) {
+                       $this->assertConfig($uid, $cat, $data);
+               }
 
-               $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,159 @@ 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 '!<unset>!' 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);
-
-               $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once();
-               $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn(null)->once();
-
-               $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once();
-               $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn($data)->once();
-
-               $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once();
+               $this->configCache->load($uid, ['test' => ['it' => $data]]);
 
-               $configuration = new PConfiguration($configCache, $configAdapter);
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache());
 
-               // first run is not loaded and no data is found in the DB
-               $this->assertNull($configuration->get($uid, 'test', 'it'));
+               $this->assertEquals($data, $this->testedConfig->get($uid, 'test', 'it'));
+               $this->assertEquals($data, $this->testedConfig->getCache()->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'));
+               $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'));
 
-               // 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
+        * Test the configuration delete() method with a model/db
         */
-       public function testDeleteWithoutDB($data)
+       public function testDeleteWithDB()
        {
-               $uid = 234;
-               $configCache = new PConfigCache();
-               $configAdapter = \Mockery::mock(IPConfigAdapter::class);
-               $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4);
-
-               $configuration = new PConfiguration($configCache, $configAdapter);
+               $this->configCache->load(0, ['test' => ['it' => 'now', 'quarter' => 'true']]);
+
+               $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();
+
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache());
+
+               // directly set the value to the cache
+               $this->testedConfig->getCache()->set(0, 'test', 'it', 'now');
+
+               $this->assertEquals('now', $this->testedConfig->get(0, 'test', 'it'));
+               $this->assertEquals('now', $this->testedConfig->getCache()->get(0, 'test', 'it'));
+
+               // delete from cache only
+               $this->assertTrue($this->testedConfig->delete(0, 'test', 'it'));
+               // delete from db only
+               $this->assertTrue($this->testedConfig->delete(0, 'test', 'second'));
+               // no delete
+               $this->assertFalse($this->testedConfig->delete(0, 'test', 'third'));
+               // delete both
+               $this->assertTrue($this->testedConfig->delete(0, 'test', 'quarter'));
 
-               $this->assertTrue($configuration->set($uid, 'test', 'it', $data));
-               $this->assertEquals($data, $configuration->get($uid, 'test', 'it'));
+               $this->assertEmpty($this->testedConfig->getCache()->getAll());
+       }
 
-               $this->assertTrue($configuration->delete($uid, 'test', 'it'));
-               $this->assertNull($configuration->get($uid, 'test', 'it'));
+       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 the configuration delete() method with adapter
+        * Test if multiple uids for caching are usable without errors
+        * @dataProvider dataMultiUid
         */
-       public function testDeleteWithDB()
+       public function testMultipleUidsWithCache(array $data1, array $data2)
        {
-               $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();
-
-               $configAdapter->shouldReceive('delete')->with($uid, 'test', 'it')->andReturn(false)->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();
-
-               $configuration = new PConfiguration($configCache, $configAdapter);
+               $this->configCache->load($data1['uid'], $data1['data']);
+               $this->configCache->load($data2['uid'], $data2['data']);
 
-               $this->assertFalse($configuration->set($uid, 'test', 'it', 'now'));
-               $this->assertEquals('now', $configuration->get($uid, 'test', 'it'));
+               $this->testedConfig = $this->getInstance();
+               $this->assertInstanceOf(PConfigCache::class, $this->testedConfig->getCache());
 
-               // delete from set
-               $this->assertTrue($configuration->delete($uid, 'test', 'it'));
-               // delete from db only
-               $this->assertTrue($configuration->delete($uid, 'test', 'second'));
-               // no delete
-               $this->assertFalse($configuration->delete($uid, 'test', 'third'));
-               // delete both
-               $this->assertTrue($configuration->delete($uid, 'test', 'quarter'));
+               $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']);
        }
 }
diff --git a/tests/src/Core/Config/PreloadPConfigurationTest.php b/tests/src/Core/Config/PreloadPConfigurationTest.php
new file mode 100644 (file)
index 0000000..ca916d4
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+
+namespace Friendica\Test\src\Core\Config;
+
+use Friendica\Core\Config\PreloadPConfiguration;
+
+class PreloadPConfigurationTest extends PConfigurationTest
+{
+       public function getInstance()
+       {
+               return new PreloadPConfiguration($this->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();
+       }
+}
index 852db4498fc1c0d78a72f1b3a8f3e89525dc1faa..530430feb1e9f827441a4bae7405edd5036821cb 100644 (file)
@@ -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();
index d10f07493bfaa7636ba7e9eec4d7582cad487de5..64d249f4c046523425c6d54f790aa5d51218299e 100644 (file)
@@ -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();