From: Philipp Date: Sat, 23 Oct 2021 08:49:27 +0000 (+0200) Subject: Restructure Cache to follow new paradigm X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=68046573a4171dcbd5db75a991487bafa4b4370f;p=friendica.git Restructure Cache to follow new paradigm --- diff --git a/mod/ping.php b/mod/ping.php index 2143f73af2..c12594902a 100644 --- a/mod/ping.php +++ b/mod/ping.php @@ -22,7 +22,7 @@ use Friendica\App; use Friendica\Content\ForumManager; use Friendica\Content\Text\BBCode; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Hook; use Friendica\Database\DBA; use Friendica\DI; diff --git a/src/App/Router.php b/src/App/Router.php index ad4b33d084..5076839530 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -26,7 +26,7 @@ use FastRoute\DataGenerator\GroupCountBased; use FastRoute\Dispatcher; use FastRoute\RouteCollector; use FastRoute\RouteParser\Std; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\ICache; use Friendica\Core\Hook; use Friendica\Core\L10n; diff --git a/src/Console/Cache.php b/src/Console/Cache.php index 049ffd0ffb..a9452435eb 100644 --- a/src/Console/Cache.php +++ b/src/Console/Cache.php @@ -23,7 +23,7 @@ namespace Friendica\Console; use Asika\SimpleConsole\CommandArgsException; use Friendica\App; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\ICache; use RuntimeException; diff --git a/src/Content/OEmbed.php b/src/Content/OEmbed.php index 313a2628db..14e910ebab 100644 --- a/src/Content/OEmbed.php +++ b/src/Content/OEmbed.php @@ -26,7 +26,7 @@ use DOMNode; use DOMText; use DOMXPath; use Exception; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Hook; use Friendica\Core\Renderer; use Friendica\Database\Database; diff --git a/src/Content/Widget.php b/src/Content/Widget.php index daa34a8b00..4a3e918674 100644 --- a/src/Content/Widget.php +++ b/src/Content/Widget.php @@ -22,7 +22,7 @@ namespace Friendica\Content; use Friendica\Core\Addon; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Protocol; use Friendica\Core\Renderer; use Friendica\Database\DBA; diff --git a/src/Core/BaseCache.php b/src/Core/BaseCache.php deleted file mode 100644 index c6c6b60a64..0000000000 --- a/src/Core/BaseCache.php +++ /dev/null @@ -1,108 +0,0 @@ -. - * - */ - -namespace Friendica\Core; - -use Friendica\Core\Cache\ICache; - -/** - * Abstract class for common used functions - */ -abstract class BaseCache implements ICache -{ - /** - * @var string The hostname - */ - private $hostName; - - public function __construct(string $hostName) - { - $this->hostName = $hostName; - } - - /** - * Returns the prefix (to avoid namespace conflicts) - * - * @return string - * @throws \Exception - */ - protected function getPrefix() - { - // We fetch with the hostname as key to avoid problems with other applications - return $this->hostName; - } - - /** - * @param string $key The original key - * @return string The cache key used for the cache - * @throws \Exception - */ - protected function getCacheKey($key) - { - return $this->getPrefix() . ":" . $key; - } - - /** - * @param array $keys A list of cached keys - * @return array A list of original keys - */ - protected function getOriginalKeys($keys) - { - if (empty($keys)) { - return []; - } else { - // Keys are prefixed with the node hostname, let's remove it - array_walk($keys, function (&$value) { - $value = preg_replace('/^' . $this->hostName . ':/', '', $value); - }); - - sort($keys); - - return $keys; - } - } - - /** - * Filters the keys of an array with a given prefix - * Returns the filtered keys as an new array - * - * @param array $keys The keys, which should get filtered - * @param string|null $prefix The prefix (if null, all keys will get returned) - * - * @return array The filtered array with just the keys - */ - protected function filterArrayKeysByPrefix(array $keys, string $prefix = null) - { - if (empty($prefix)) { - return $keys; - } else { - $result = []; - - foreach ($keys as $key) { - if (strpos($key, $prefix) === 0) { - array_push($result, $key); - } - } - - return $result; - } - } -} diff --git a/src/Core/Cache/APCuCache.php b/src/Core/Cache/APCuCache.php deleted file mode 100644 index 7d819b4e8d..0000000000 --- a/src/Core/Cache/APCuCache.php +++ /dev/null @@ -1,181 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Exception; -use Friendica\Core\BaseCache; - -/** - * APCu Cache. - */ -class APCuCache extends BaseCache implements IMemoryCache -{ - use TraitCompareSet; - use TraitCompareDelete; - - /** - * @throws Exception - */ - public function __construct(string $hostname) - { - if (!self::isAvailable()) { - throw new Exception('APCu is not available.'); - } - - parent::__construct($hostname); - } - - /** - * (@inheritdoc) - */ - public function getAllKeys($prefix = null) - { - $ns = $this->getCacheKey($prefix); - $ns = preg_quote($ns, '/'); - - if (class_exists('\APCIterator')) { - $iterator = new \APCIterator('user', '/^' . $ns. '/', APC_ITER_KEY); - } else { - $iterator = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY); - } - - $keys = []; - foreach ($iterator as $item) { - array_push($keys, $item['key']); - } - - return $this->getOriginalKeys($keys); - } - - /** - * (@inheritdoc) - */ - public function get($key) - { - $return = null; - $cachekey = $this->getCacheKey($key); - - $cached = apcu_fetch($cachekey, $success); - if (!$success) { - return null; - } - - $value = unserialize($cached); - - // Only return a value if the serialized value is valid. - // We also check if the db entry is a serialized - // boolean 'false' value (which we want to return). - if ($cached === serialize(false) || $value !== false) { - $return = $value; - } - - return $return; - } - - /** - * (@inheritdoc) - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - - $cached = serialize($value); - - if ($ttl > 0) { - return apcu_store( - $cachekey, - $cached, - $ttl - ); - } else { - return apcu_store( - $cachekey, - $cached - ); - } - } - - /** - * (@inheritdoc) - */ - public function delete($key) - { - $cachekey = $this->getCacheKey($key); - return apcu_delete($cachekey); - } - - /** - * (@inheritdoc) - */ - public function clear($outdated = true) - { - if ($outdated) { - return true; - } else { - $prefix = $this->getPrefix(); - $prefix = preg_quote($prefix, '/'); - - if (class_exists('\APCIterator')) { - $iterator = new \APCIterator('user', '/^' . $prefix . '/', APC_ITER_KEY); - } else { - $iterator = new \APCUIterator('/^' . $prefix . '/', APC_ITER_KEY); - } - - return apcu_delete($iterator); - } - } - - /** - * (@inheritdoc) - */ - public function add($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - $cached = serialize($value); - - return apcu_add($cachekey, $cached); - } - - public static function isAvailable() - { - if (!extension_loaded('apcu')) { - return false; - } elseif (!ini_get('apc.enabled') && !ini_get('apc.enable_cli')) { - return false; - } elseif ( - version_compare(phpversion('apc') ?: '0.0.0', '4.0.6') === -1 && - version_compare(phpversion('apcu') ?: '0.0.0', '5.1.0') === -1 - ) { - return false; - } - - return true; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Type::APCU; - } -} diff --git a/src/Core/Cache/ArrayCache.php b/src/Core/Cache/ArrayCache.php deleted file mode 100644 index 5970500896..0000000000 --- a/src/Core/Cache/ArrayCache.php +++ /dev/null @@ -1,118 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Friendica\Core\BaseCache; - -/** - * Implementation of the IMemoryCache mainly for testing purpose - */ -class ArrayCache extends BaseCache implements IMemoryCache -{ - use TraitCompareDelete; - - /** @var array Array with the cached data */ - protected $cachedData = array(); - - /** - * (@inheritdoc) - */ - public function getAllKeys($prefix = null) - { - return $this->filterArrayKeysByPrefix(array_keys($this->cachedData), $prefix); - } - - /** - * (@inheritdoc) - */ - public function get($key) - { - if (isset($this->cachedData[$key])) { - return $this->cachedData[$key]; - } - return null; - } - - /** - * (@inheritdoc) - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $this->cachedData[$key] = $value; - return true; - } - - /** - * (@inheritdoc) - */ - public function delete($key) - { - unset($this->cachedData[$key]); - return true; - } - - /** - * (@inheritdoc) - */ - public function clear($outdated = true) - { - // Array doesn't support TTL so just don't delete something - if ($outdated) { - return true; - } - - $this->cachedData = []; - return true; - } - - /** - * (@inheritdoc) - */ - public function add($key, $value, $ttl = Duration::FIVE_MINUTES) - { - if (isset($this->cachedData[$key])) { - return false; - } else { - return $this->set($key, $value, $ttl); - } - } - - /** - * (@inheritdoc) - */ - public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) - { - if ($this->get($key) === $oldValue) { - return $this->set($key, $newValue); - } else { - return false; - } - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Type::ARRAY; - } -} diff --git a/src/Core/Cache/DatabaseCache.php b/src/Core/Cache/DatabaseCache.php deleted file mode 100644 index 8dcfae8aff..0000000000 --- a/src/Core/Cache/DatabaseCache.php +++ /dev/null @@ -1,138 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Friendica\Database\Database; -use Friendica\Util\DateTimeFormat; -use Friendica\Core\BaseCache; - -/** - * Database Cache - */ -class DatabaseCache extends BaseCache implements ICache -{ - /** - * @var Database - */ - private $dba; - - public function __construct(string $hostname, Database $dba) - { - parent::__construct($hostname); - - $this->dba = $dba; - } - - /** - * (@inheritdoc) - */ - public function getAllKeys($prefix = null) - { - if (empty($prefix)) { - $where = ['`expires` >= ?', DateTimeFormat::utcNow()]; - } else { - $where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix]; - } - - $stmt = $this->dba->select('cache', ['k'], $where); - - $keys = []; - while ($key = $this->dba->fetch($stmt)) { - array_push($keys, $key['k']); - } - $this->dba->close($stmt); - - return $keys; - } - - /** - * (@inheritdoc) - */ - public function get($key) - { - $cache = $this->dba->selectFirst('cache', ['v'], ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, DateTimeFormat::utcNow()]); - - if ($this->dba->isResult($cache)) { - $cached = $cache['v']; - $value = @unserialize($cached); - - // Only return a value if the serialized value is valid. - // We also check if the db entry is a serialized - // boolean 'false' value (which we want to return). - if ($cached === serialize(false) || $value !== false) { - return $value; - } - } - - return null; - } - - /** - * (@inheritdoc) - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - if ($ttl > 0) { - $fields = [ - 'v' => serialize($value), - 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds'), - 'updated' => DateTimeFormat::utcNow() - ]; - } else { - $fields = [ - 'v' => serialize($value), - 'expires' => -1, - 'updated' => DateTimeFormat::utcNow() - ]; - } - - return $this->dba->update('cache', $fields, ['k' => $key], true); - } - - /** - * (@inheritdoc) - */ - public function delete($key) - { - return $this->dba->delete('cache', ['k' => $key]); - } - - /** - * (@inheritdoc) - */ - public function clear($outdated = true) - { - if ($outdated) { - return $this->dba->delete('cache', ['`expires` < NOW()']); - } else { - return $this->dba->delete('cache', ['`k` IS NOT NULL ']); - } - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Type::DATABASE; - } -} diff --git a/src/Core/Cache/Duration.php b/src/Core/Cache/Duration.php deleted file mode 100644 index fcbaadda7a..0000000000 --- a/src/Core/Cache/Duration.php +++ /dev/null @@ -1,38 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -/** - * Enumeration for cache durations - */ -abstract class Duration -{ - const MONTH = 2592000; - const HOUR = 3600; - const HALF_HOUR = 1800; - const QUARTER_HOUR = 900; - const MINUTE = 60; - const WEEK = 604800; - const INFINITE = 0; - const DAY = 86400; - const FIVE_MINUTES = 300; -} diff --git a/src/Core/Cache/Enum/Duration.php b/src/Core/Cache/Enum/Duration.php new file mode 100644 index 0000000000..c5771c7803 --- /dev/null +++ b/src/Core/Cache/Enum/Duration.php @@ -0,0 +1,38 @@ +. + * + */ + +namespace Friendica\Core\Cache\Enum; + +/** + * Enumeration for cache durations + */ +abstract class Duration +{ + const MONTH = 2592000; + const HOUR = 3600; + const HALF_HOUR = 1800; + const QUARTER_HOUR = 900; + const MINUTE = 60; + const WEEK = 604800; + const INFINITE = 0; + const DAY = 86400; + const FIVE_MINUTES = 300; +} diff --git a/src/Core/Cache/Enum/Type.php b/src/Core/Cache/Enum/Type.php new file mode 100644 index 0000000000..9bdfed5953 --- /dev/null +++ b/src/Core/Cache/Enum/Type.php @@ -0,0 +1,35 @@ +. + * + */ + +namespace Friendica\Core\Cache\Enum; + +/** + * Enumeration for cache types + */ +abstract class Type +{ + const APCU = 'apcu'; + const REDIS = 'redis'; + const ARRAY = 'array'; + const MEMCACHE = 'memcache'; + const DATABASE = 'database'; + const MEMCACHED = 'memcached'; +} diff --git a/src/Core/Cache/Factory/CacheFactory.php b/src/Core/Cache/Factory/CacheFactory.php new file mode 100644 index 0000000000..2e99180ecc --- /dev/null +++ b/src/Core/Cache/Factory/CacheFactory.php @@ -0,0 +1,120 @@ +. + * + */ + +namespace Friendica\Core\Cache\Factory; + +use Friendica\App\BaseURL; +use Friendica\Core\Cache; +use Friendica\Core\Cache\ICache; +use Friendica\Core\Config\IConfig; +use Friendica\Database\Database; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +/** + * Class CacheFactory + * + * @package Friendica\Core\Cache + * + * A basic class to generate a CacheDriver + */ +class CacheFactory +{ + /** + * @var string The default cache if nothing set + */ + const DEFAULT_TYPE = Cache\Enum\Type::DATABASE; + + /** + * @var IConfig The IConfiguration to read parameters out of the config + */ + private $config; + + /** + * @var Database The database connection in case that the cache is used the dba connection + */ + private $dba; + + /** + * @var string The hostname, used as Prefix for Caching + */ + private $hostname; + + /** + * @var Profiler The optional profiler if the cached should be profiled + */ + private $profiler; + + /** + * @var LoggerInterface The Friendica Logger + */ + private $logger; + + public function __construct(BaseURL $baseURL, IConfig $config, Database $dba, Profiler $profiler, LoggerInterface $logger) + { + $this->hostname = $baseURL->getHostname(); + $this->config = $config; + $this->dba = $dba; + $this->profiler = $profiler; + $this->logger = $logger; + } + + /** + * This method creates a CacheDriver for the given cache driver name + * + * @param string $type The cache type to create (default is per config) + * + * @return ICache The instance of the CacheDriver + * @throws \Exception The exception if something went wrong during the CacheDriver creation + */ + public function create(string $type = null) + { + if (empty($type)) { + $type = $this->config->get('system', 'cache_driver', self::DEFAULT_TYPE); + } + + switch ($type) { + case Cache\Enum\Type::MEMCACHE: + $cache = new Cache\Type\MemcacheCache($this->hostname, $this->config); + break; + case Cache\Enum\Type::MEMCACHED: + $cache = new Cache\Type\MemcachedCache($this->hostname, $this->config, $this->logger); + break; + case Cache\Enum\Type::REDIS: + $cache = new Cache\Type\RedisCache($this->hostname, $this->config); + break; + case Cache\Enum\Type::APCU: + $cache = new Cache\Type\APCuCache($this->hostname); + break; + default: + $cache = new Cache\Type\DatabaseCache($this->hostname, $this->dba); + } + + $profiling = $this->config->get('system', 'profiling', false); + + // In case profiling is enabled, wrap the ProfilerCache around the current cache + if (isset($profiling) && $profiling !== false) { + return new Cache\Type\ProfilerCache($cache, $this->profiler); + } else { + return $cache; + } + } +} diff --git a/src/Core/Cache/ICache.php b/src/Core/Cache/ICache.php index 0589f4506b..3918b00893 100644 --- a/src/Core/Cache/ICache.php +++ b/src/Core/Cache/ICache.php @@ -21,6 +21,8 @@ namespace Friendica\Core\Cache; +use Friendica\Core\Cache\Enum\Duration; + /** * Cache Interface */ diff --git a/src/Core/Cache/IMemoryCache.php b/src/Core/Cache/IMemoryCache.php index 248908d801..a46db0b011 100644 --- a/src/Core/Cache/IMemoryCache.php +++ b/src/Core/Cache/IMemoryCache.php @@ -21,6 +21,8 @@ namespace Friendica\Core\Cache; +use Friendica\Core\Cache\Enum\Duration; + /** * This interface defines methods for Memory-Caches only */ diff --git a/src/Core/Cache/MemcacheCache.php b/src/Core/Cache/MemcacheCache.php deleted file mode 100644 index ac826eaa26..0000000000 --- a/src/Core/Cache/MemcacheCache.php +++ /dev/null @@ -1,163 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Exception; -use Friendica\Core\BaseCache; -use Friendica\Core\Config\IConfig; -use Memcache; - -/** - * Memcache Cache - */ -class MemcacheCache extends BaseCache implements IMemoryCache -{ - use TraitCompareSet; - use TraitCompareDelete; - use TraitMemcacheCommand; - - /** - * @var Memcache - */ - private $memcache; - - /** - * @throws Exception - */ - public function __construct(string $hostname, IConfig $config) - { - if (!class_exists('Memcache', false)) { - throw new Exception('Memcache class isn\'t available'); - } - - parent::__construct($hostname); - - $this->memcache = new Memcache(); - - $this->server = $config->get('system', 'memcache_host');; - $this->port = $config->get('system', 'memcache_port'); - - if (!@$this->memcache->connect($this->server, $this->port)) { - throw new Exception('Expected Memcache server at ' . $this->server . ':' . $this->port . ' isn\'t available'); - } - } - - /** - * (@inheritdoc) - */ - public function getAllKeys($prefix = null) - { - $keys = $this->getOriginalKeys($this->getMemcacheKeys()); - - return $this->filterArrayKeysByPrefix($keys, $prefix); - } - - /** - * (@inheritdoc) - */ - public function get($key) - { - $return = null; - $cachekey = $this->getCacheKey($key); - - // We fetch with the hostname as key to avoid problems with other applications - $cached = $this->memcache->get($cachekey); - - // @see http://php.net/manual/en/memcache.get.php#84275 - if (is_bool($cached) || is_double($cached) || is_long($cached)) { - return $return; - } - - $value = @unserialize($cached); - - // Only return a value if the serialized value is valid. - // We also check if the db entry is a serialized - // boolean 'false' value (which we want to return). - if ($cached === serialize(false) || $value !== false) { - $return = $value; - } - - return $return; - } - - /** - * (@inheritdoc) - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - - // We store with the hostname as key to avoid problems with other applications - if ($ttl > 0) { - return $this->memcache->set( - $cachekey, - serialize($value), - MEMCACHE_COMPRESSED, - time() + $ttl - ); - } else { - return $this->memcache->set( - $cachekey, - serialize($value), - MEMCACHE_COMPRESSED - ); - } - } - - /** - * (@inheritdoc) - */ - public function delete($key) - { - $cachekey = $this->getCacheKey($key); - return $this->memcache->delete($cachekey); - } - - /** - * (@inheritdoc) - */ - public function clear($outdated = true) - { - if ($outdated) { - return true; - } else { - return $this->memcache->flush(); - } - } - - /** - * (@inheritdoc) - */ - public function add($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - return $this->memcache->add($cachekey, serialize($value), MEMCACHE_COMPRESSED, $ttl); - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Type::MEMCACHE; - } -} diff --git a/src/Core/Cache/MemcachedCache.php b/src/Core/Cache/MemcachedCache.php deleted file mode 100644 index 5ce64e9995..0000000000 --- a/src/Core/Cache/MemcachedCache.php +++ /dev/null @@ -1,179 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Exception; -use Friendica\Core\BaseCache; -use Friendica\Core\Config\IConfig; -use Memcached; -use Psr\Log\LoggerInterface; - -/** - * Memcached Cache - */ -class MemcachedCache extends BaseCache implements IMemoryCache -{ - use TraitCompareSet; - use TraitCompareDelete; - use TraitMemcacheCommand; - - /** - * @var \Memcached - */ - private $memcached; - - /** - * @var LoggerInterface - */ - private $logger; - - /** - * Due to limitations of the INI format, the expected configuration for Memcached servers is the following: - * array { - * 0 => "hostname, port(, weight)", - * 1 => ... - * } - * - * @param array $memcached_hosts - * - * @throws \Exception - */ - public function __construct(string $hostname, IConfig $config, LoggerInterface $logger) - { - if (!class_exists('Memcached', false)) { - throw new Exception('Memcached class isn\'t available'); - } - - parent::__construct($hostname); - - $this->logger = $logger; - - $this->memcached = new Memcached(); - - $memcached_hosts = $config->get('system', 'memcached_hosts'); - - array_walk($memcached_hosts, function (&$value) { - if (is_string($value)) { - $value = array_map('trim', explode(',', $value)); - } - }); - - $this->server = $memcached_hosts[0][0] ?? 'localhost'; - $this->port = $memcached_hosts[0][1] ?? 11211; - - $this->memcached->addServers($memcached_hosts); - - if (count($this->memcached->getServerList()) == 0) { - throw new Exception('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true)); - } - } - - /** - * (@inheritdoc) - */ - public function getAllKeys($prefix = null) - { - $keys = $this->getOriginalKeys($this->getMemcacheKeys()); - - return $this->filterArrayKeysByPrefix($keys, $prefix); - } - - /** - * (@inheritdoc) - */ - public function get($key) - { - $return = null; - $cachekey = $this->getCacheKey($key); - - // We fetch with the hostname as key to avoid problems with other applications - $value = $this->memcached->get($cachekey); - - if ($this->memcached->getResultCode() === Memcached::RES_SUCCESS) { - $return = $value; - } else { - $this->logger->debug('Memcached \'get\' failed', ['result' => $this->memcached->getResultMessage()]); - } - - return $return; - } - - /** - * (@inheritdoc) - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - - // We store with the hostname as key to avoid problems with other applications - if ($ttl > 0) { - return $this->memcached->set( - $cachekey, - $value, - $ttl - ); - } else { - return $this->memcached->set( - $cachekey, - $value - ); - } - } - - /** - * (@inheritdoc) - */ - public function delete($key) - { - $cachekey = $this->getCacheKey($key); - return $this->memcached->delete($cachekey); - } - - /** - * (@inheritdoc) - */ - public function clear($outdated = true) - { - if ($outdated) { - return true; - } else { - return $this->memcached->flush(); - } - } - - /** - * (@inheritdoc) - */ - public function add($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - return $this->memcached->add($cachekey, $value, $ttl); - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Type::MEMCACHED; - } -} diff --git a/src/Core/Cache/ProfilerCache.php b/src/Core/Cache/ProfilerCache.php deleted file mode 100644 index a8c9f15113..0000000000 --- a/src/Core/Cache/ProfilerCache.php +++ /dev/null @@ -1,181 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Friendica\Core\System; -use Friendica\Util\Profiler; - -/** - * This class wraps cache driver so they can get profiled - in case the profiler is enabled - * - * It is using the decorator pattern (@see - */ -class ProfilerCache implements ICache, IMemoryCache -{ - /** - * @var ICache The original cache driver - */ - private $cache; - - /** - * @var Profiler The profiler of Friendica - */ - private $profiler; - - public function __construct(ICache $cache, Profiler $profiler) - { - $this->cache = $cache; - $this->profiler = $profiler; - } - - /** - * {@inheritDoc} - */ - public function getAllKeys($prefix = null) - { - $this->profiler->startRecording('cache'); - - $return = $this->cache->getAllKeys($prefix); - - $this->profiler->stopRecording(); - - return $return; - } - - /** - * {@inheritDoc} - */ - public function get($key) - { - $this->profiler->startRecording('cache'); - - $return = $this->cache->get($key); - - $this->profiler->stopRecording(); - - return $return; - } - - /** - * {@inheritDoc} - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $this->profiler->startRecording('cache'); - - $return = $this->cache->set($key, $value, $ttl); - - $this->profiler->stopRecording(); - - return $return; - } - - /** - * {@inheritDoc} - */ - public function delete($key) - { - $this->profiler->startRecording('cache'); - - $return = $this->cache->delete($key); - - $this->profiler->stopRecording(); - - return $return; - } - - /** - * {@inheritDoc} - */ - public function clear($outdated = true) - { - $this->profiler->startRecording('cache'); - - $return = $this->cache->clear($outdated); - - $this->profiler->stopRecording(); - - return $return; - } - - /** - * {@inheritDoc} - */ - public function add($key, $value, $ttl = Duration::FIVE_MINUTES) - { - if ($this->cache instanceof IMemoryCache) { - $this->profiler->startRecording('cache'); - - $return = $this->cache->add($key, $value, $ttl); - - $this->profiler->stopRecording(); - - return $return; - } else { - return false; - } - } - - /** - * {@inheritDoc} - */ - public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) - { - if ($this->cache instanceof IMemoryCache) { - $this->profiler->startRecording('cache'); - - $return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl); - - $this->profiler->stopRecording(); - - return $return; - } else { - return false; - } - } - - /** - * {@inheritDoc} - */ - public function compareDelete($key, $value) - { - if ($this->cache instanceof IMemoryCache) { - $this->profiler->startRecording('cache'); - - $return = $this->cache->compareDelete($key, $value); - - $this->profiler->stopRecording(); - - return $return; - } else { - return false; - } - } - - /** - * {@inheritDoc} - */ - public function GetName() - { - return $this->cache->getName() . ' (with profiler)'; - } -} diff --git a/src/Core/Cache/RedisCache.php b/src/Core/Cache/RedisCache.php deleted file mode 100644 index ce081d7ef7..0000000000 --- a/src/Core/Cache/RedisCache.php +++ /dev/null @@ -1,223 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Exception; -use Friendica\Core\BaseCache; -use Friendica\Core\Config\IConfig; -use Redis; - -/** - * Redis Cache. This driver is based on Memcache driver - */ -class RedisCache extends BaseCache implements IMemoryCache -{ - /** - * @var Redis - */ - private $redis; - - /** - * @throws Exception - */ - public function __construct(string $hostname, IConfig $config) - { - if (!class_exists('Redis', false)) { - throw new Exception('Redis class isn\'t available'); - } - - parent::__construct($hostname); - - $this->redis = new Redis(); - - $redis_host = $config->get('system', 'redis_host'); - $redis_port = $config->get('system', 'redis_port'); - $redis_pw = $config->get('system', 'redis_password'); - $redis_db = $config->get('system', 'redis_db', 0); - - if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) { - throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available'); - } elseif (!@$this->redis->connect($redis_host)) { - throw new Exception('Expected Redis server at ' . $redis_host . ' isn\'t available'); - } - - if (isset($redis_pw) && !$this->redis->auth($redis_pw)) { - throw new Exception('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port); - } - - if ($redis_db !== 0 && !$this->redis->select($redis_db)) { - throw new Exception('Cannot switch to redis db ' . $redis_db . ' at ' . $redis_host . ':' . $redis_port); - } - } - - /** - * (@inheritdoc) - */ - public function getAllKeys($prefix = null) - { - if (empty($prefix)) { - $search = '*'; - } else { - $search = $prefix . '*'; - } - - $list = $this->redis->keys($this->getCacheKey($search)); - - return $this->getOriginalKeys($list); - } - - /** - * (@inheritdoc) - */ - public function get($key) - { - $return = null; - $cachekey = $this->getCacheKey($key); - - $cached = $this->redis->get($cachekey); - if ($cached === false && !$this->redis->exists($cachekey)) { - return null; - } - - $value = unserialize($cached); - - // Only return a value if the serialized value is valid. - // We also check if the db entry is a serialized - // boolean 'false' value (which we want to return). - if ($cached === serialize(false) || $value !== false) { - $return = $value; - } - - return $return; - } - - /** - * (@inheritdoc) - */ - public function set($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - - $cached = serialize($value); - - if ($ttl > 0) { - return $this->redis->setex( - $cachekey, - $ttl, - $cached - ); - } else { - return $this->redis->set( - $cachekey, - $cached - ); - } - } - - /** - * (@inheritdoc) - */ - public function delete($key) - { - $cachekey = $this->getCacheKey($key); - $this->redis->del($cachekey); - // Redis doesn't have an error state for del() - return true; - } - - /** - * (@inheritdoc) - */ - public function clear($outdated = true) - { - if ($outdated) { - return true; - } else { - return $this->redis->flushAll(); - } - } - - /** - * (@inheritdoc) - */ - public function add($key, $value, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - $cached = serialize($value); - - return $this->redis->setnx($cachekey, $cached); - } - - /** - * (@inheritdoc) - */ - public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) - { - $cachekey = $this->getCacheKey($key); - - $newCached = serialize($newValue); - - $this->redis->watch($cachekey); - // If the old value isn't what we expected, somebody else changed the key meanwhile - if ($this->get($key) === $oldValue) { - if ($ttl > 0) { - $result = $this->redis->multi() - ->setex($cachekey, $ttl, $newCached) - ->exec(); - } else { - $result = $this->redis->multi() - ->set($cachekey, $newCached) - ->exec(); - } - return $result !== false; - } - $this->redis->unwatch(); - return false; - } - - /** - * (@inheritdoc) - */ - public function compareDelete($key, $value) - { - $cachekey = $this->getCacheKey($key); - - $this->redis->watch($cachekey); - // If the old value isn't what we expected, somebody else changed the key meanwhile - if ($this->get($key) === $value) { - $result = $this->redis->multi() - ->del($cachekey) - ->exec(); - return $result !== false; - } - $this->redis->unwatch(); - return false; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return Type::REDIS; - } -} diff --git a/src/Core/Cache/TraitCompareDelete.php b/src/Core/Cache/TraitCompareDelete.php deleted file mode 100644 index 31c3e7cf08..0000000000 --- a/src/Core/Cache/TraitCompareDelete.php +++ /dev/null @@ -1,60 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -/** - * Trait TraitCompareSetDelete - * - * This Trait is to compensate non native "exclusive" sets/deletes in caches - */ -trait TraitCompareDelete -{ - abstract public function get($key); - - abstract public function set($key, $value, $ttl = Duration::FIVE_MINUTES); - - abstract public function delete($key); - - abstract public function add($key, $value, $ttl = Duration::FIVE_MINUTES); - - /** - * NonNative - Compares if the old value is set and removes it - * - * @param string $key The cache key - * @param mixed $value The old value we know and want to delete - * @return bool - */ - public function compareDelete($key, $value) { - if ($this->add($key . "_lock", true)) { - if ($this->get($key) === $value) { - $this->delete($key); - $this->delete($key . "_lock"); - return true; - } else { - $this->delete($key . "_lock"); - return false; - } - } else { - return false; - } - } -} diff --git a/src/Core/Cache/TraitCompareSet.php b/src/Core/Cache/TraitCompareSet.php deleted file mode 100644 index fa20bd9e58..0000000000 --- a/src/Core/Cache/TraitCompareSet.php +++ /dev/null @@ -1,63 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -/** - * Trait TraitCompareSetDelete - * - * This Trait is to compensate non native "exclusive" sets/deletes in caches - */ -trait TraitCompareSet -{ - abstract public function get($key); - - abstract public function set($key, $value, $ttl = Duration::FIVE_MINUTES); - - abstract public function delete($key); - - abstract public function add($key, $value, $ttl = Duration::FIVE_MINUTES); - - /** - * NonNative - Compares if the old value is set and sets the new value - * - * @param string $key The cache key - * @param mixed $oldValue The old value we know from the cache - * @param mixed $newValue The new value we want to set - * @param int $ttl The cache lifespan, must be one of the Cache constants - * - * @return bool - */ - public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) { - if ($this->add($key . "_lock", true)) { - if ($this->get($key) === $oldValue) { - $this->set($key, $newValue, $ttl); - $this->delete($key . "_lock"); - return true; - } else { - $this->delete($key . "_lock"); - return false; - } - } else { - return false; - } - } -} diff --git a/src/Core/Cache/TraitMemcacheCommand.php b/src/Core/Cache/TraitMemcacheCommand.php deleted file mode 100644 index abc41ceeae..0000000000 --- a/src/Core/Cache/TraitMemcacheCommand.php +++ /dev/null @@ -1,123 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -use Friendica\Network\HTTPException\InternalServerErrorException; - -/** - * Trait for Memcache to add a custom version of the - * method getAllKeys() since this isn't working anymore - * - * Adds the possibility to directly communicate with the memcache too - */ -trait TraitMemcacheCommand -{ - /** - * @var string server address - */ - protected $server; - - /** - * @var int server port - */ - protected $port; - - /** - * Retrieves the stored keys of the memcache instance - * Uses custom commands, which aren't bound to the used instance of the class - * - * @todo Due the fact that we use a custom command, there are race conditions possible: - * - $this->memcache(d) adds a key - * - $this->getMemcacheKeys is called directly "after" - * - But $this->memcache(d) isn't finished adding the key, so getMemcacheKeys doesn't find it - * - * @return array All keys of the memcache instance - * - * @throws InternalServerErrorException - */ - protected function getMemcacheKeys() - { - $string = $this->sendMemcacheCommand("stats items"); - $lines = explode("\r\n", $string); - $slabs = []; - $keys = []; - - foreach ($lines as $line) { - - if (preg_match("/STAT items:([\d]+):number ([\d]+)/", $line, $matches) && - isset($matches[1]) && - !in_array($matches[1], $keys)) { - - $slabs[] = $matches[1]; - $string = $this->sendMemcacheCommand("stats cachedump " . $matches[1] . " " . $matches[2]); - preg_match_all("/ITEM (.*?) /", $string, $matches); - $keys = array_merge($keys, $matches[1]); - } - } - - return $keys; - } - - /** - * Taken directly from memcache PECL source - * Sends a command to the memcache instance and returns the result - * as a string - * - * http://pecl.php.net/package/memcache - * - * @param string $command The command to send to the Memcache server - * - * @return string The returned buffer result - * - * @throws InternalServerErrorException In case the memcache server isn't available (anymore) - */ - protected function sendMemcacheCommand(string $command) - { - $s = @fsockopen($this->server, $this->port); - if (!$s) { - throw new InternalServerErrorException("Cant connect to:" . $this->server . ':' . $this->port); - } - - fwrite($s, $command . "\r\n"); - $buf = ''; - - while (!feof($s)) { - - $buf .= fgets($s, 256); - - if (strpos($buf, "END\r\n") !== false) { // stat says end - break; - } - - if (strpos($buf, "DELETED\r\n") !== false || strpos($buf, "NOT_FOUND\r\n") !== false) { // delete says these - break; - } - - if (strpos($buf, "OK\r\n") !== false) { // flush_all says ok - break; - } - } - - fclose($s); - return ($buf); - } -} diff --git a/src/Core/Cache/Type.php b/src/Core/Cache/Type.php deleted file mode 100644 index 5153acaf8d..0000000000 --- a/src/Core/Cache/Type.php +++ /dev/null @@ -1,35 +0,0 @@ -. - * - */ - -namespace Friendica\Core\Cache; - -/** - * Enumeration for cache types - */ -abstract class Type -{ - const APCU = 'apcu'; - const REDIS = 'redis'; - const ARRAY = 'array'; - const MEMCACHE = 'memcache'; - const DATABASE = 'database'; - const MEMCACHED = 'memcached'; -} diff --git a/src/Core/Cache/Type/APCuCache.php b/src/Core/Cache/Type/APCuCache.php new file mode 100644 index 0000000000..b473f68030 --- /dev/null +++ b/src/Core/Cache/Type/APCuCache.php @@ -0,0 +1,185 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Exception; +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\IMemoryCache; +use Friendica\Core\Cache\Type\TraitCompareDelete; +use Friendica\Core\Cache\Type\TraitCompareSet; +use Friendica\Core\Cache\Enum\Type; + +/** + * APCu Cache. + */ +class APCuCache extends BaseCache implements IMemoryCache +{ + use TraitCompareSet; + use TraitCompareDelete; + + /** + * @throws Exception + */ + public function __construct(string $hostname) + { + if (!self::isAvailable()) { + throw new Exception('APCu is not available.'); + } + + parent::__construct($hostname); + } + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + $ns = $this->getCacheKey($prefix); + $ns = preg_quote($ns, '/'); + + if (class_exists('\APCIterator')) { + $iterator = new \APCIterator('user', '/^' . $ns. '/', APC_ITER_KEY); + } else { + $iterator = new \APCUIterator('/^' . $ns . '/', APC_ITER_KEY); + } + + $keys = []; + foreach ($iterator as $item) { + array_push($keys, $item['key']); + } + + return $this->getOriginalKeys($keys); + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + $return = null; + $cachekey = $this->getCacheKey($key); + + $cached = apcu_fetch($cachekey, $success); + if (!$success) { + return null; + } + + $value = unserialize($cached); + + // Only return a value if the serialized value is valid. + // We also check if the db entry is a serialized + // boolean 'false' value (which we want to return). + if ($cached === serialize(false) || $value !== false) { + $return = $value; + } + + return $return; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + + $cached = serialize($value); + + if ($ttl > 0) { + return apcu_store( + $cachekey, + $cached, + $ttl + ); + } else { + return apcu_store( + $cachekey, + $cached + ); + } + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + $cachekey = $this->getCacheKey($key); + return apcu_delete($cachekey); + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + if ($outdated) { + return true; + } else { + $prefix = $this->getPrefix(); + $prefix = preg_quote($prefix, '/'); + + if (class_exists('\APCIterator')) { + $iterator = new \APCIterator('user', '/^' . $prefix . '/', APC_ITER_KEY); + } else { + $iterator = new \APCUIterator('/^' . $prefix . '/', APC_ITER_KEY); + } + + return apcu_delete($iterator); + } + } + + /** + * (@inheritdoc) + */ + public function add($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + $cached = serialize($value); + + return apcu_add($cachekey, $cached); + } + + public static function isAvailable() + { + if (!extension_loaded('apcu')) { + return false; + } elseif (!ini_get('apc.enabled') && !ini_get('apc.enable_cli')) { + return false; + } elseif ( + version_compare(phpversion('apc') ?: '0.0.0', '4.0.6') === -1 && + version_compare(phpversion('apcu') ?: '0.0.0', '5.1.0') === -1 + ) { + return false; + } + + return true; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return Type::APCU; + } +} diff --git a/src/Core/Cache/Type/ArrayCache.php b/src/Core/Cache/Type/ArrayCache.php new file mode 100644 index 0000000000..dd0985a978 --- /dev/null +++ b/src/Core/Cache/Type/ArrayCache.php @@ -0,0 +1,121 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\IMemoryCache; +use Friendica\Core\Cache\Type\TraitCompareDelete; +use Friendica\Core\Cache\Enum\Type; + +/** + * Implementation of the IMemoryCache mainly for testing purpose + */ +class ArrayCache extends BaseCache implements IMemoryCache +{ + use TraitCompareDelete; + + /** @var array Array with the cached data */ + protected $cachedData = array(); + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + return $this->filterArrayKeysByPrefix(array_keys($this->cachedData), $prefix); + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + if (isset($this->cachedData[$key])) { + return $this->cachedData[$key]; + } + return null; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $this->cachedData[$key] = $value; + return true; + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + unset($this->cachedData[$key]); + return true; + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + // Array doesn't support TTL so just don't delete something + if ($outdated) { + return true; + } + + $this->cachedData = []; + return true; + } + + /** + * (@inheritdoc) + */ + public function add($key, $value, $ttl = Duration::FIVE_MINUTES) + { + if (isset($this->cachedData[$key])) { + return false; + } else { + return $this->set($key, $value, $ttl); + } + } + + /** + * (@inheritdoc) + */ + public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) + { + if ($this->get($key) === $oldValue) { + return $this->set($key, $newValue); + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return Type::ARRAY; + } +} diff --git a/src/Core/Cache/Type/BaseCache.php b/src/Core/Cache/Type/BaseCache.php new file mode 100644 index 0000000000..a52f1e9cef --- /dev/null +++ b/src/Core/Cache/Type/BaseCache.php @@ -0,0 +1,108 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Core\Cache\ICache; + +/** + * Abstract class for common used functions + */ +abstract class BaseCache implements ICache +{ + /** + * @var string The hostname + */ + private $hostName; + + public function __construct(string $hostName) + { + $this->hostName = $hostName; + } + + /** + * Returns the prefix (to avoid namespace conflicts) + * + * @return string + * @throws \Exception + */ + protected function getPrefix() + { + // We fetch with the hostname as key to avoid problems with other applications + return $this->hostName; + } + + /** + * @param string $key The original key + * @return string The cache key used for the cache + * @throws \Exception + */ + protected function getCacheKey($key) + { + return $this->getPrefix() . ":" . $key; + } + + /** + * @param array $keys A list of cached keys + * @return array A list of original keys + */ + protected function getOriginalKeys($keys) + { + if (empty($keys)) { + return []; + } else { + // Keys are prefixed with the node hostname, let's remove it + array_walk($keys, function (&$value) { + $value = preg_replace('/^' . $this->hostName . ':/', '', $value); + }); + + sort($keys); + + return $keys; + } + } + + /** + * Filters the keys of an array with a given prefix + * Returns the filtered keys as an new array + * + * @param array $keys The keys, which should get filtered + * @param string|null $prefix The prefix (if null, all keys will get returned) + * + * @return array The filtered array with just the keys + */ + protected function filterArrayKeysByPrefix(array $keys, string $prefix = null) + { + if (empty($prefix)) { + return $keys; + } else { + $result = []; + + foreach ($keys as $key) { + if (strpos($key, $prefix) === 0) { + array_push($result, $key); + } + } + + return $result; + } + } +} diff --git a/src/Core/Cache/Type/DatabaseCache.php b/src/Core/Cache/Type/DatabaseCache.php new file mode 100644 index 0000000000..a3c83c8dfe --- /dev/null +++ b/src/Core/Cache/Type/DatabaseCache.php @@ -0,0 +1,140 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\ICache; +use Friendica\Core\Cache\Enum\Type; +use Friendica\Database\Database; +use Friendica\Util\DateTimeFormat; + +/** + * Database Cache + */ +class DatabaseCache extends BaseCache implements ICache +{ + /** + * @var Database + */ + private $dba; + + public function __construct(string $hostname, Database $dba) + { + parent::__construct($hostname); + + $this->dba = $dba; + } + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + if (empty($prefix)) { + $where = ['`expires` >= ?', DateTimeFormat::utcNow()]; + } else { + $where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix]; + } + + $stmt = $this->dba->select('cache', ['k'], $where); + + $keys = []; + while ($key = $this->dba->fetch($stmt)) { + array_push($keys, $key['k']); + } + $this->dba->close($stmt); + + return $keys; + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + $cache = $this->dba->selectFirst('cache', ['v'], ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, DateTimeFormat::utcNow()]); + + if ($this->dba->isResult($cache)) { + $cached = $cache['v']; + $value = @unserialize($cached); + + // Only return a value if the serialized value is valid. + // We also check if the db entry is a serialized + // boolean 'false' value (which we want to return). + if ($cached === serialize(false) || $value !== false) { + return $value; + } + } + + return null; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + if ($ttl > 0) { + $fields = [ + 'v' => serialize($value), + 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds'), + 'updated' => DateTimeFormat::utcNow() + ]; + } else { + $fields = [ + 'v' => serialize($value), + 'expires' => -1, + 'updated' => DateTimeFormat::utcNow() + ]; + } + + return $this->dba->update('cache', $fields, ['k' => $key], true); + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + return $this->dba->delete('cache', ['k' => $key]); + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + if ($outdated) { + return $this->dba->delete('cache', ['`expires` < NOW()']); + } else { + return $this->dba->delete('cache', ['`k` IS NOT NULL ']); + } + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return Type::DATABASE; + } +} diff --git a/src/Core/Cache/Type/MemcacheCache.php b/src/Core/Cache/Type/MemcacheCache.php new file mode 100644 index 0000000000..f991517fcc --- /dev/null +++ b/src/Core/Cache/Type/MemcacheCache.php @@ -0,0 +1,168 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Exception; +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\IMemoryCache; +use Friendica\Core\Cache\Type\TraitCompareDelete; +use Friendica\Core\Cache\Type\TraitCompareSet; +use Friendica\Core\Cache\Type\TraitMemcacheCommand; +use Friendica\Core\Cache\Enum\Type; +use Friendica\Core\Config\IConfig; +use Memcache; + +/** + * Memcache Cache + */ +class MemcacheCache extends BaseCache implements IMemoryCache +{ + use TraitCompareSet; + use TraitCompareDelete; + use TraitMemcacheCommand; + + /** + * @var Memcache + */ + private $memcache; + + /** + * @throws Exception + */ + public function __construct(string $hostname, IConfig $config) + { + if (!class_exists('Memcache', false)) { + throw new Exception('Memcache class isn\'t available'); + } + + parent::__construct($hostname); + + $this->memcache = new Memcache(); + + $this->server = $config->get('system', 'memcache_host');; + $this->port = $config->get('system', 'memcache_port'); + + if (!@$this->memcache->connect($this->server, $this->port)) { + throw new Exception('Expected Memcache server at ' . $this->server . ':' . $this->port . ' isn\'t available'); + } + } + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + $keys = $this->getOriginalKeys($this->getMemcacheKeys()); + + return $this->filterArrayKeysByPrefix($keys, $prefix); + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + $return = null; + $cachekey = $this->getCacheKey($key); + + // We fetch with the hostname as key to avoid problems with other applications + $cached = $this->memcache->get($cachekey); + + // @see http://php.net/manual/en/memcache.get.php#84275 + if (is_bool($cached) || is_double($cached) || is_long($cached)) { + return $return; + } + + $value = @unserialize($cached); + + // Only return a value if the serialized value is valid. + // We also check if the db entry is a serialized + // boolean 'false' value (which we want to return). + if ($cached === serialize(false) || $value !== false) { + $return = $value; + } + + return $return; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + + // We store with the hostname as key to avoid problems with other applications + if ($ttl > 0) { + return $this->memcache->set( + $cachekey, + serialize($value), + MEMCACHE_COMPRESSED, + time() + $ttl + ); + } else { + return $this->memcache->set( + $cachekey, + serialize($value), + MEMCACHE_COMPRESSED + ); + } + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + $cachekey = $this->getCacheKey($key); + return $this->memcache->delete($cachekey); + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + if ($outdated) { + return true; + } else { + return $this->memcache->flush(); + } + } + + /** + * (@inheritdoc) + */ + public function add($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + return $this->memcache->add($cachekey, serialize($value), MEMCACHE_COMPRESSED, $ttl); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return Type::MEMCACHE; + } +} diff --git a/src/Core/Cache/Type/MemcachedCache.php b/src/Core/Cache/Type/MemcachedCache.php new file mode 100644 index 0000000000..ded53ee977 --- /dev/null +++ b/src/Core/Cache/Type/MemcachedCache.php @@ -0,0 +1,184 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Exception; +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\IMemoryCache; +use Friendica\Core\Cache\Type\TraitCompareDelete; +use Friendica\Core\Cache\Type\TraitCompareSet; +use Friendica\Core\Cache\Type\TraitMemcacheCommand; +use Friendica\Core\Cache\Enum\Type; +use Friendica\Core\Config\IConfig; +use Memcached; +use Psr\Log\LoggerInterface; + +/** + * Memcached Cache + */ +class MemcachedCache extends BaseCache implements IMemoryCache +{ + use TraitCompareSet; + use TraitCompareDelete; + use TraitMemcacheCommand; + + /** + * @var \Memcached + */ + private $memcached; + + /** + * @var LoggerInterface + */ + private $logger; + + /** + * Due to limitations of the INI format, the expected configuration for Memcached servers is the following: + * array { + * 0 => "hostname, port(, weight)", + * 1 => ... + * } + * + * @param array $memcached_hosts + * + * @throws \Exception + */ + public function __construct(string $hostname, IConfig $config, LoggerInterface $logger) + { + if (!class_exists('Memcached', false)) { + throw new Exception('Memcached class isn\'t available'); + } + + parent::__construct($hostname); + + $this->logger = $logger; + + $this->memcached = new Memcached(); + + $memcached_hosts = $config->get('system', 'memcached_hosts'); + + array_walk($memcached_hosts, function (&$value) { + if (is_string($value)) { + $value = array_map('trim', explode(',', $value)); + } + }); + + $this->server = $memcached_hosts[0][0] ?? 'localhost'; + $this->port = $memcached_hosts[0][1] ?? 11211; + + $this->memcached->addServers($memcached_hosts); + + if (count($this->memcached->getServerList()) == 0) { + throw new Exception('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true)); + } + } + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + $keys = $this->getOriginalKeys($this->getMemcacheKeys()); + + return $this->filterArrayKeysByPrefix($keys, $prefix); + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + $return = null; + $cachekey = $this->getCacheKey($key); + + // We fetch with the hostname as key to avoid problems with other applications + $value = $this->memcached->get($cachekey); + + if ($this->memcached->getResultCode() === Memcached::RES_SUCCESS) { + $return = $value; + } else { + $this->logger->debug('Memcached \'get\' failed', ['result' => $this->memcached->getResultMessage()]); + } + + return $return; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + + // We store with the hostname as key to avoid problems with other applications + if ($ttl > 0) { + return $this->memcached->set( + $cachekey, + $value, + $ttl + ); + } else { + return $this->memcached->set( + $cachekey, + $value + ); + } + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + $cachekey = $this->getCacheKey($key); + return $this->memcached->delete($cachekey); + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + if ($outdated) { + return true; + } else { + return $this->memcached->flush(); + } + } + + /** + * (@inheritdoc) + */ + public function add($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + return $this->memcached->add($cachekey, $value, $ttl); + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return Type::MEMCACHED; + } +} diff --git a/src/Core/Cache/Type/ProfilerCache.php b/src/Core/Cache/Type/ProfilerCache.php new file mode 100644 index 0000000000..fd003857cd --- /dev/null +++ b/src/Core/Cache/Type/ProfilerCache.php @@ -0,0 +1,183 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\ICache; +use Friendica\Core\Cache\IMemoryCache; +use Friendica\Util\Profiler; + +/** + * This class wraps cache driver so they can get profiled - in case the profiler is enabled + * + * It is using the decorator pattern (@see + */ +class ProfilerCache implements ICache, IMemoryCache +{ + /** + * @var ICache The original cache driver + */ + private $cache; + + /** + * @var Profiler The profiler of Friendica + */ + private $profiler; + + public function __construct(ICache $cache, Profiler $profiler) + { + $this->cache = $cache; + $this->profiler = $profiler; + } + + /** + * {@inheritDoc} + */ + public function getAllKeys($prefix = null) + { + $this->profiler->startRecording('cache'); + + $return = $this->cache->getAllKeys($prefix); + + $this->profiler->stopRecording(); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function get($key) + { + $this->profiler->startRecording('cache'); + + $return = $this->cache->get($key); + + $this->profiler->stopRecording(); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $this->profiler->startRecording('cache'); + + $return = $this->cache->set($key, $value, $ttl); + + $this->profiler->stopRecording(); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function delete($key) + { + $this->profiler->startRecording('cache'); + + $return = $this->cache->delete($key); + + $this->profiler->stopRecording(); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function clear($outdated = true) + { + $this->profiler->startRecording('cache'); + + $return = $this->cache->clear($outdated); + + $this->profiler->stopRecording(); + + return $return; + } + + /** + * {@inheritDoc} + */ + public function add($key, $value, $ttl = Duration::FIVE_MINUTES) + { + if ($this->cache instanceof IMemoryCache) { + $this->profiler->startRecording('cache'); + + $return = $this->cache->add($key, $value, $ttl); + + $this->profiler->stopRecording(); + + return $return; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) + { + if ($this->cache instanceof IMemoryCache) { + $this->profiler->startRecording('cache'); + + $return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl); + + $this->profiler->stopRecording(); + + return $return; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function compareDelete($key, $value) + { + if ($this->cache instanceof IMemoryCache) { + $this->profiler->startRecording('cache'); + + $return = $this->cache->compareDelete($key, $value); + + $this->profiler->stopRecording(); + + return $return; + } else { + return false; + } + } + + /** + * {@inheritDoc} + */ + public function GetName() + { + return $this->cache->getName() . ' (with profiler)'; + } +} diff --git a/src/Core/Cache/Type/RedisCache.php b/src/Core/Cache/Type/RedisCache.php new file mode 100644 index 0000000000..a4cc419727 --- /dev/null +++ b/src/Core/Cache/Type/RedisCache.php @@ -0,0 +1,225 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Exception; +use Friendica\Core\Cache\Enum\Duration; +use Friendica\Core\Cache\IMemoryCache; +use Friendica\Core\Cache\Enum\Type; +use Friendica\Core\Config\IConfig; +use Redis; + +/** + * Redis Cache. This driver is based on Memcache driver + */ +class RedisCache extends BaseCache implements IMemoryCache +{ + /** + * @var Redis + */ + private $redis; + + /** + * @throws Exception + */ + public function __construct(string $hostname, IConfig $config) + { + if (!class_exists('Redis', false)) { + throw new Exception('Redis class isn\'t available'); + } + + parent::__construct($hostname); + + $this->redis = new Redis(); + + $redis_host = $config->get('system', 'redis_host'); + $redis_port = $config->get('system', 'redis_port'); + $redis_pw = $config->get('system', 'redis_password'); + $redis_db = $config->get('system', 'redis_db', 0); + + if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) { + throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available'); + } elseif (!@$this->redis->connect($redis_host)) { + throw new Exception('Expected Redis server at ' . $redis_host . ' isn\'t available'); + } + + if (isset($redis_pw) && !$this->redis->auth($redis_pw)) { + throw new Exception('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port); + } + + if ($redis_db !== 0 && !$this->redis->select($redis_db)) { + throw new Exception('Cannot switch to redis db ' . $redis_db . ' at ' . $redis_host . ':' . $redis_port); + } + } + + /** + * (@inheritdoc) + */ + public function getAllKeys($prefix = null) + { + if (empty($prefix)) { + $search = '*'; + } else { + $search = $prefix . '*'; + } + + $list = $this->redis->keys($this->getCacheKey($search)); + + return $this->getOriginalKeys($list); + } + + /** + * (@inheritdoc) + */ + public function get($key) + { + $return = null; + $cachekey = $this->getCacheKey($key); + + $cached = $this->redis->get($cachekey); + if ($cached === false && !$this->redis->exists($cachekey)) { + return null; + } + + $value = unserialize($cached); + + // Only return a value if the serialized value is valid. + // We also check if the db entry is a serialized + // boolean 'false' value (which we want to return). + if ($cached === serialize(false) || $value !== false) { + $return = $value; + } + + return $return; + } + + /** + * (@inheritdoc) + */ + public function set($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + + $cached = serialize($value); + + if ($ttl > 0) { + return $this->redis->setex( + $cachekey, + $ttl, + $cached + ); + } else { + return $this->redis->set( + $cachekey, + $cached + ); + } + } + + /** + * (@inheritdoc) + */ + public function delete($key) + { + $cachekey = $this->getCacheKey($key); + $this->redis->del($cachekey); + // Redis doesn't have an error state for del() + return true; + } + + /** + * (@inheritdoc) + */ + public function clear($outdated = true) + { + if ($outdated) { + return true; + } else { + return $this->redis->flushAll(); + } + } + + /** + * (@inheritdoc) + */ + public function add($key, $value, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + $cached = serialize($value); + + return $this->redis->setnx($cachekey, $cached); + } + + /** + * (@inheritdoc) + */ + public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) + { + $cachekey = $this->getCacheKey($key); + + $newCached = serialize($newValue); + + $this->redis->watch($cachekey); + // If the old value isn't what we expected, somebody else changed the key meanwhile + if ($this->get($key) === $oldValue) { + if ($ttl > 0) { + $result = $this->redis->multi() + ->setex($cachekey, $ttl, $newCached) + ->exec(); + } else { + $result = $this->redis->multi() + ->set($cachekey, $newCached) + ->exec(); + } + return $result !== false; + } + $this->redis->unwatch(); + return false; + } + + /** + * (@inheritdoc) + */ + public function compareDelete($key, $value) + { + $cachekey = $this->getCacheKey($key); + + $this->redis->watch($cachekey); + // If the old value isn't what we expected, somebody else changed the key meanwhile + if ($this->get($key) === $value) { + $result = $this->redis->multi() + ->del($cachekey) + ->exec(); + return $result !== false; + } + $this->redis->unwatch(); + return false; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + return Type::REDIS; + } +} diff --git a/src/Core/Cache/Type/TraitCompareDelete.php b/src/Core/Cache/Type/TraitCompareDelete.php new file mode 100644 index 0000000000..4873638927 --- /dev/null +++ b/src/Core/Cache/Type/TraitCompareDelete.php @@ -0,0 +1,62 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Core\Cache\Enum\Duration; + +/** + * Trait TraitCompareSetDelete + * + * This Trait is to compensate non native "exclusive" sets/deletes in caches + */ +trait TraitCompareDelete +{ + abstract public function get($key); + + abstract public function set($key, $value, $ttl = Duration::FIVE_MINUTES); + + abstract public function delete($key); + + abstract public function add($key, $value, $ttl = Duration::FIVE_MINUTES); + + /** + * NonNative - Compares if the old value is set and removes it + * + * @param string $key The cache key + * @param mixed $value The old value we know and want to delete + * @return bool + */ + public function compareDelete($key, $value) { + if ($this->add($key . "_lock", true)) { + if ($this->get($key) === $value) { + $this->delete($key); + $this->delete($key . "_lock"); + return true; + } else { + $this->delete($key . "_lock"); + return false; + } + } else { + return false; + } + } +} diff --git a/src/Core/Cache/Type/TraitCompareSet.php b/src/Core/Cache/Type/TraitCompareSet.php new file mode 100644 index 0000000000..86aef92903 --- /dev/null +++ b/src/Core/Cache/Type/TraitCompareSet.php @@ -0,0 +1,65 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Core\Cache\Enum\Duration; + +/** + * Trait TraitCompareSetDelete + * + * This Trait is to compensate non native "exclusive" sets/deletes in caches + */ +trait TraitCompareSet +{ + abstract public function get($key); + + abstract public function set($key, $value, $ttl = Duration::FIVE_MINUTES); + + abstract public function delete($key); + + abstract public function add($key, $value, $ttl = Duration::FIVE_MINUTES); + + /** + * NonNative - Compares if the old value is set and sets the new value + * + * @param string $key The cache key + * @param mixed $oldValue The old value we know from the cache + * @param mixed $newValue The new value we want to set + * @param int $ttl The cache lifespan, must be one of the Cache constants + * + * @return bool + */ + public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) { + if ($this->add($key . "_lock", true)) { + if ($this->get($key) === $oldValue) { + $this->set($key, $newValue, $ttl); + $this->delete($key . "_lock"); + return true; + } else { + $this->delete($key . "_lock"); + return false; + } + } else { + return false; + } + } +} diff --git a/src/Core/Cache/Type/TraitMemcacheCommand.php b/src/Core/Cache/Type/TraitMemcacheCommand.php new file mode 100644 index 0000000000..73495e2de6 --- /dev/null +++ b/src/Core/Cache/Type/TraitMemcacheCommand.php @@ -0,0 +1,123 @@ +. + * + */ + +namespace Friendica\Core\Cache\Type; + +use Friendica\Network\HTTPException\InternalServerErrorException; + +/** + * Trait for Memcache to add a custom version of the + * method getAllKeys() since this isn't working anymore + * + * Adds the possibility to directly communicate with the memcache too + */ +trait TraitMemcacheCommand +{ + /** + * @var string server address + */ + protected $server; + + /** + * @var int server port + */ + protected $port; + + /** + * Retrieves the stored keys of the memcache instance + * Uses custom commands, which aren't bound to the used instance of the class + * + * @todo Due the fact that we use a custom command, there are race conditions possible: + * - $this->memcache(d) adds a key + * - $this->getMemcacheKeys is called directly "after" + * - But $this->memcache(d) isn't finished adding the key, so getMemcacheKeys doesn't find it + * + * @return array All keys of the memcache instance + * + * @throws InternalServerErrorException + */ + protected function getMemcacheKeys() + { + $string = $this->sendMemcacheCommand("stats items"); + $lines = explode("\r\n", $string); + $slabs = []; + $keys = []; + + foreach ($lines as $line) { + + if (preg_match("/STAT items:([\d]+):number ([\d]+)/", $line, $matches) && + isset($matches[1]) && + !in_array($matches[1], $keys)) { + + $slabs[] = $matches[1]; + $string = $this->sendMemcacheCommand("stats cachedump " . $matches[1] . " " . $matches[2]); + preg_match_all("/ITEM (.*?) /", $string, $matches); + $keys = array_merge($keys, $matches[1]); + } + } + + return $keys; + } + + /** + * Taken directly from memcache PECL source + * Sends a command to the memcache instance and returns the result + * as a string + * + * http://pecl.php.net/package/memcache + * + * @param string $command The command to send to the Memcache server + * + * @return string The returned buffer result + * + * @throws InternalServerErrorException In case the memcache server isn't available (anymore) + */ + protected function sendMemcacheCommand(string $command) + { + $s = @fsockopen($this->server, $this->port); + if (!$s) { + throw new InternalServerErrorException("Cant connect to:" . $this->server . ':' . $this->port); + } + + fwrite($s, $command . "\r\n"); + $buf = ''; + + while (!feof($s)) { + + $buf .= fgets($s, 256); + + if (strpos($buf, "END\r\n") !== false) { // stat says end + break; + } + + if (strpos($buf, "DELETED\r\n") !== false || strpos($buf, "NOT_FOUND\r\n") !== false) { // delete says these + break; + } + + if (strpos($buf, "OK\r\n") !== false) { // flush_all says ok + break; + } + } + + fclose($s); + return ($buf); + } +} diff --git a/src/Core/Lock/CacheLock.php b/src/Core/Lock/CacheLock.php index 31c7d1ee12..20705e06e9 100644 --- a/src/Core/Lock/CacheLock.php +++ b/src/Core/Lock/CacheLock.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Lock; use Friendica\Core\BaseLock; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Cache\IMemoryCache; class CacheLock extends BaseLock diff --git a/src/Core/Lock/DatabaseLock.php b/src/Core/Lock/DatabaseLock.php index 688a00dac7..14e78f625a 100644 --- a/src/Core/Lock/DatabaseLock.php +++ b/src/Core/Lock/DatabaseLock.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Lock; use Friendica\Core\BaseLock; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Database\Database; use Friendica\Util\DateTimeFormat; diff --git a/src/Core/Lock/ILock.php b/src/Core/Lock/ILock.php index 7369e5b5aa..35e21305ee 100644 --- a/src/Core/Lock/ILock.php +++ b/src/Core/Lock/ILock.php @@ -21,7 +21,7 @@ namespace Friendica\Core\Lock; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; /** * Lock Interface diff --git a/src/Core/Lock/SemaphoreLock.php b/src/Core/Lock/SemaphoreLock.php index 219a4d738b..fa5cf5e876 100644 --- a/src/Core/Lock/SemaphoreLock.php +++ b/src/Core/Lock/SemaphoreLock.php @@ -22,7 +22,7 @@ namespace Friendica\Core\Lock; use Friendica\Core\BaseLock; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; class SemaphoreLock extends BaseLock { diff --git a/src/Core/Lock/Type.php b/src/Core/Lock/Type.php index 259b7d0ce2..769bad12eb 100644 --- a/src/Core/Lock/Type.php +++ b/src/Core/Lock/Type.php @@ -21,7 +21,7 @@ namespace Friendica\Core\Lock; -use Friendica\Core\Cache\Type as CacheType; +use Friendica\Core\Cache\Enum\Type as CacheType; /** * Enumeration for lock types diff --git a/src/Core/Update.php b/src/Core/Update.php index fa3c6b62d0..f3ea743117 100644 --- a/src/Core/Update.php +++ b/src/Core/Update.php @@ -132,7 +132,7 @@ class Update // Compare the current structure with the defined structure // If the Lock is acquired, never release it automatically to avoid double updates - if (DI::lock()->acquire('dbupdate', 0, Cache\Duration::INFINITE)) { + if (DI::lock()->acquire('dbupdate', 0, Cache\Enum\Duration::INFINITE)) { Logger::notice('Update starting.', ['from' => $stored, 'to' => $current]); @@ -246,7 +246,7 @@ class Update // If the update fails or times-out completely you may need to // delete the config entry to try again. - if (DI::lock()->acquire('dbupdate_function', 120, Cache\Duration::INFINITE)) { + if (DI::lock()->acquire('dbupdate_function', 120, Cache\Enum\Duration::INFINITE)) { // call the specific update Logger::notice('Pre update function start.', ['function' => $funcname]); diff --git a/src/Factory/CacheFactory.php b/src/Factory/CacheFactory.php deleted file mode 100644 index 1e93b5ee07..0000000000 --- a/src/Factory/CacheFactory.php +++ /dev/null @@ -1,120 +0,0 @@ -. - * - */ - -namespace Friendica\Factory; - -use Friendica\App\BaseURL; -use Friendica\Core\Cache; -use Friendica\Core\Cache\ICache; -use Friendica\Core\Config\IConfig; -use Friendica\Database\Database; -use Friendica\Util\Profiler; -use Psr\Log\LoggerInterface; - -/** - * Class CacheFactory - * - * @package Friendica\Core\Cache - * - * A basic class to generate a CacheDriver - */ -class CacheFactory -{ - /** - * @var string The default cache if nothing set - */ - const DEFAULT_TYPE = Cache\Type::DATABASE; - - /** - * @var IConfig The IConfiguration to read parameters out of the config - */ - private $config; - - /** - * @var Database The database connection in case that the cache is used the dba connection - */ - private $dba; - - /** - * @var string The hostname, used as Prefix for Caching - */ - private $hostname; - - /** - * @var Profiler The optional profiler if the cached should be profiled - */ - private $profiler; - - /** - * @var LoggerInterface The Friendica Logger - */ - private $logger; - - public function __construct(BaseURL $baseURL, IConfig $config, Database $dba, Profiler $profiler, LoggerInterface $logger) - { - $this->hostname = $baseURL->getHostname(); - $this->config = $config; - $this->dba = $dba; - $this->profiler = $profiler; - $this->logger = $logger; - } - - /** - * This method creates a CacheDriver for the given cache driver name - * - * @param string $type The cache type to create (default is per config) - * - * @return ICache The instance of the CacheDriver - * @throws \Exception The exception if something went wrong during the CacheDriver creation - */ - public function create(string $type = null) - { - if (empty($type)) { - $type = $this->config->get('system', 'cache_driver', self::DEFAULT_TYPE); - } - - switch ($type) { - case Cache\Type::MEMCACHE: - $cache = new Cache\MemcacheCache($this->hostname, $this->config); - break; - case Cache\Type::MEMCACHED: - $cache = new Cache\MemcachedCache($this->hostname, $this->config, $this->logger); - break; - case Cache\Type::REDIS: - $cache = new Cache\RedisCache($this->hostname, $this->config); - break; - case Cache\Type::APCU: - $cache = new Cache\APCuCache($this->hostname); - break; - default: - $cache = new Cache\DatabaseCache($this->hostname, $this->dba); - } - - $profiling = $this->config->get('system', 'profiling', false); - - // In case profiling is enabled, wrap the ProfilerCache around the current cache - if (isset($profiling) && $profiling !== false) { - return new Cache\ProfilerCache($cache, $this->profiler); - } else { - return $cache; - } - } -} diff --git a/src/Factory/LockFactory.php b/src/Factory/LockFactory.php index 0ac7525035..2a54910d45 100644 --- a/src/Factory/LockFactory.php +++ b/src/Factory/LockFactory.php @@ -21,8 +21,9 @@ namespace Friendica\Factory; +use Friendica\Core\Cache\Factory\CacheFactory; use Friendica\Core\Cache\IMemoryCache; -use Friendica\Core\Cache\Type; +use Friendica\Core\Cache\Enum\Type; use Friendica\Core\Config\IConfig; use Friendica\Core\Lock; use Friendica\Database\Database; diff --git a/src/Factory/SessionFactory.php b/src/Factory/SessionFactory.php index 491573033b..82161fb554 100644 --- a/src/Factory/SessionFactory.php +++ b/src/Factory/SessionFactory.php @@ -23,7 +23,7 @@ namespace Friendica\Factory; use Friendica\App; use Friendica\Core\Cache\ICache; -use Friendica\Core\Cache\Type; +use Friendica\Core\Cache\Enum\Type; use Friendica\Core\Config\IConfig; use Friendica\Core\Session; use Friendica\Core\System; diff --git a/src/Model/APContact.php b/src/Model/APContact.php index a919a67f30..5e992075e8 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -22,7 +22,7 @@ namespace Friendica\Model; use Friendica\Content\Text\HTML; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Database\DBA; diff --git a/src/Model/Photo.php b/src/Model/Photo.php index e72004029f..ebd278753b 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -21,7 +21,7 @@ namespace Friendica\Model; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Database\DBA; diff --git a/src/Model/Profile.php b/src/Model/Profile.php index d089ada3cd..bcd7d54973 100644 --- a/src/Model/Profile.php +++ b/src/Model/Profile.php @@ -24,7 +24,7 @@ namespace Friendica\Model; use Friendica\App; use Friendica\Content\Text\BBCode; use Friendica\Content\Widget\ContactBlock; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\Protocol; diff --git a/src/Model/Tag.php b/src/Model/Tag.php index ab7845c2c6..fb301d1436 100644 --- a/src/Model/Tag.php +++ b/src/Model/Tag.php @@ -22,7 +22,7 @@ namespace Friendica\Model; use Friendica\Content\Text\BBCode; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Core\System; diff --git a/src/Module/Search/Index.php b/src/Module/Search/Index.php index 7be5058c2b..39bf940972 100644 --- a/src/Module/Search/Index.php +++ b/src/Module/Search/Index.php @@ -25,7 +25,7 @@ use Friendica\Content\Nav; use Friendica\Content\Pager; use Friendica\Content\Text\HTML; use Friendica\Content\Widget; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Renderer; use Friendica\Core\Search; diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 5e6f772472..238aa3c16f 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -23,7 +23,7 @@ namespace Friendica\Protocol\ActivityPub; use Friendica\Content\Feature; use Friendica\Content\Text\BBCode; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Core\System; diff --git a/src/Protocol/Diaspora.php b/src/Protocol/Diaspora.php index 84f0c6de82..02d6d270a6 100644 --- a/src/Protocol/Diaspora.php +++ b/src/Protocol/Diaspora.php @@ -24,7 +24,7 @@ namespace Friendica\Protocol; use Friendica\Content\Feature; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\Markdown; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Core\System; diff --git a/src/Protocol/Feed.php b/src/Protocol/Feed.php index cdecbcf1b0..a9e50d532c 100644 --- a/src/Protocol/Feed.php +++ b/src/Protocol/Feed.php @@ -26,7 +26,7 @@ use DOMXPath; use Friendica\Content\PageInfo; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Database\DBA; diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index c40193e1b0..46f68086b1 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -25,7 +25,7 @@ use DOMDocument; use DOMXPath; use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Database\DBA; diff --git a/src/Util/JsonLD.php b/src/Util/JsonLD.php index 6ffe9a0a0f..e8c4751c43 100644 --- a/src/Util/JsonLD.php +++ b/src/Util/JsonLD.php @@ -21,7 +21,7 @@ namespace Friendica\Util; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Exception; use Friendica\DI; diff --git a/src/Worker/SearchDirectory.php b/src/Worker/SearchDirectory.php index bb69969b9e..5e1b1590fd 100644 --- a/src/Worker/SearchDirectory.php +++ b/src/Worker/SearchDirectory.php @@ -21,7 +21,7 @@ namespace Friendica\Worker; -use Friendica\Core\Cache\Duration; +use Friendica\Core\Cache\Enum\Duration; use Friendica\Core\Logger; use Friendica\Core\Search; use Friendica\DI; diff --git a/static/dependencies.config.php b/static/dependencies.config.php index bf7b412c2f..39aae97fd1 100644 --- a/static/dependencies.config.php +++ b/static/dependencies.config.php @@ -158,13 +158,13 @@ return [ ] ], Cache\ICache::class => [ - 'instanceOf' => Factory\CacheFactory::class, + 'instanceOf' => Cache\Factory\CacheFactory::class, 'call' => [ ['create', [], Dice::CHAIN_CALL], ], ], Cache\IMemoryCache::class => [ - 'instanceOf' => Factory\CacheFactory::class, + 'instanceOf' => Cache\Factory\CacheFactory::class, 'call' => [ ['create', [], Dice::CHAIN_CALL], ], diff --git a/tests/src/Core/Cache/APCuCacheTest.php b/tests/src/Core/Cache/APCuCacheTest.php index d8fe59da03..2f957a5d5b 100644 --- a/tests/src/Core/Cache/APCuCacheTest.php +++ b/tests/src/Core/Cache/APCuCacheTest.php @@ -21,7 +21,7 @@ namespace Friendica\Test\src\Core\Cache; -use Friendica\Core\Cache\APCuCache; +use Friendica\Core\Cache\Type\APCuCache; /** * @group APCU diff --git a/tests/src/Core/Cache/ArrayCacheTest.php b/tests/src/Core/Cache/ArrayCacheTest.php index c2e92806ce..3623fe0c18 100644 --- a/tests/src/Core/Cache/ArrayCacheTest.php +++ b/tests/src/Core/Cache/ArrayCacheTest.php @@ -21,7 +21,7 @@ namespace Friendica\Test\src\Core\Cache; -use Friendica\Core\Cache\ArrayCache; +use Friendica\Core\Cache\Type\ArrayCache; class ArrayCacheTest extends MemoryCacheTest { diff --git a/tests/src/Core/Cache/DatabaseCacheTest.php b/tests/src/Core/Cache/DatabaseCacheTest.php index 37859ef785..8d404b3552 100644 --- a/tests/src/Core/Cache/DatabaseCacheTest.php +++ b/tests/src/Core/Cache/DatabaseCacheTest.php @@ -60,7 +60,7 @@ class DatabaseCacheTest extends CacheTest $dba = new StaticDatabase($configCache, $profiler, $logger); - $this->cache = new Cache\DatabaseCache('database', $dba); + $this->cache = new Cache\Type\DatabaseCache('database', $dba); return $this->cache; } diff --git a/tests/src/Core/Cache/MemcacheCacheTest.php b/tests/src/Core/Cache/MemcacheCacheTest.php index 4947186ccb..6916f51695 100644 --- a/tests/src/Core/Cache/MemcacheCacheTest.php +++ b/tests/src/Core/Cache/MemcacheCacheTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Cache; use Exception; -use Friendica\Core\Cache\MemcacheCache; +use Friendica\Core\Cache\Type\MemcacheCache; use Friendica\Core\Config\IConfig; use Mockery; diff --git a/tests/src/Core/Cache/MemcachedCacheTest.php b/tests/src/Core/Cache/MemcachedCacheTest.php index 842e33d082..cc912e364d 100644 --- a/tests/src/Core/Cache/MemcachedCacheTest.php +++ b/tests/src/Core/Cache/MemcachedCacheTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Cache; use Exception; -use Friendica\Core\Cache\MemcachedCache; +use Friendica\Core\Cache\Type\MemcachedCache; use Friendica\Core\Config\IConfig; use Mockery; use Psr\Log\NullLogger; diff --git a/tests/src/Core/Cache/RedisCacheTest.php b/tests/src/Core/Cache/RedisCacheTest.php index 146dca6d9f..f5c540d32d 100644 --- a/tests/src/Core/Cache/RedisCacheTest.php +++ b/tests/src/Core/Cache/RedisCacheTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Cache; use Exception; -use Friendica\Core\Cache\RedisCache; +use Friendica\Core\Cache\Type\RedisCache; use Friendica\Core\Config\IConfig; use Mockery; @@ -58,7 +58,7 @@ class RedisCacheTest extends MemoryCacheTest ->andReturn(null); try { - $this->cache = new RedisCache($host, $configMock); + $this->cache = new \Friendica\Core\Cache\Type\RedisCache($host, $configMock); } catch (Exception $e) { static::markTestSkipped('Redis is not available. Failure: ' . $e->getMessage()); } diff --git a/tests/src/Core/Lock/APCuCacheLockTest.php b/tests/src/Core/Lock/APCuCacheLockTest.php index 9f893ab324..750748975a 100644 --- a/tests/src/Core/Lock/APCuCacheLockTest.php +++ b/tests/src/Core/Lock/APCuCacheLockTest.php @@ -21,7 +21,7 @@ namespace Friendica\Test\src\Core\Lock; -use Friendica\Core\Cache\APCuCache; +use Friendica\Core\Cache\Type\APCuCache; use Friendica\Core\Lock\CacheLock; /** diff --git a/tests/src/Core/Lock/ArrayCacheLockTest.php b/tests/src/Core/Lock/ArrayCacheLockTest.php index 11ea794c68..93c2c20526 100644 --- a/tests/src/Core/Lock/ArrayCacheLockTest.php +++ b/tests/src/Core/Lock/ArrayCacheLockTest.php @@ -21,7 +21,7 @@ namespace Friendica\Test\src\Core\Lock; -use Friendica\Core\Cache\ArrayCache; +use Friendica\Core\Cache\Type\ArrayCache; use Friendica\Core\Lock\CacheLock; class ArrayCacheLockTest extends LockTest diff --git a/tests/src/Core/Lock/MemcacheCacheLockTest.php b/tests/src/Core/Lock/MemcacheCacheLockTest.php index 98266852f9..efb27dcaba 100644 --- a/tests/src/Core/Lock/MemcacheCacheLockTest.php +++ b/tests/src/Core/Lock/MemcacheCacheLockTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Lock; use Exception; -use Friendica\Core\Cache\MemcacheCache; +use Friendica\Core\Cache\Type\MemcacheCache; use Friendica\Core\Config\IConfig; use Friendica\Core\Lock\CacheLock; use Mockery; diff --git a/tests/src/Core/Lock/MemcachedCacheLockTest.php b/tests/src/Core/Lock/MemcachedCacheLockTest.php index 67d3097fab..f729364847 100644 --- a/tests/src/Core/Lock/MemcachedCacheLockTest.php +++ b/tests/src/Core/Lock/MemcachedCacheLockTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Lock; use Exception; -use Friendica\Core\Cache\MemcachedCache; +use Friendica\Core\Cache\Type\MemcachedCache; use Friendica\Core\Config\IConfig; use Friendica\Core\Lock\CacheLock; use Mockery; diff --git a/tests/src/Core/Lock/RedisCacheLockTest.php b/tests/src/Core/Lock/RedisCacheLockTest.php index 360fa74fb2..6fdbd1eeb9 100644 --- a/tests/src/Core/Lock/RedisCacheLockTest.php +++ b/tests/src/Core/Lock/RedisCacheLockTest.php @@ -22,7 +22,7 @@ namespace Friendica\Test\src\Core\Lock; use Exception; -use Friendica\Core\Cache\RedisCache; +use Friendica\Core\Cache\Type\RedisCache; use Friendica\Core\Config\IConfig; use Friendica\Core\Lock\CacheLock; use Mockery;