refacor: improve cache interface (#3492)

* fix: proper typehint on setScope

* refactor: type hint setKey()

* typehint
This commit is contained in:
Dag 2023-07-06 15:10:30 +02:00 committed by GitHub
parent f8801d8cb3
commit caac7f572c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 56 additions and 220 deletions

View File

@ -91,7 +91,7 @@ class DisplayAction implements ActionInterface
$cache = $cacheFactory->create();
$cache->setScope('');
$cache->purgeCache(86400); // 24 hours
$cache->purgeCache(86400);
$cache->setKey($cache_params);
$items = [];
@ -215,7 +215,7 @@ class DisplayAction implements ActionInterface
$cache = $cacheFactory->create();
$cache->setScope('error_reporting');
$cache->setkey([$bridgeName . '_' . $code]);
$cache->purgeCache(86400); // 24 hours
$cache->purgeCache(86400);
if ($report = $cache->loadData()) {
$report = Json::decode($report);
$report['time'] = time();

View File

@ -39,6 +39,10 @@ class SetBridgeCacheAction implements ActionInterface
$cache = $cacheFactory->create();
$cache->setScope(get_class($bridge));
if (!is_array($key)) {
// not sure if $key is an array when it comes in from request
$key = [$key];
}
$cache->setKey($key);
$cache->saveData($value);

View File

@ -196,7 +196,7 @@ class SpotifyBridge extends BridgeAbstract
$cache->setScope('SpotifyBridge');
$cacheKey = sprintf('%s:%s', $this->getInput('clientid'), $this->getInput('clientsecret'));
$cache->setKey($cacheKey);
$cache->setKey([$cacheKey]);
$time = null;
if ($cache->getTime()) {

View File

@ -228,7 +228,7 @@ EOD
$cache->setScope('twitter');
$cache->setKey(['cache']);
$cache->purgeCache(60 * 60 * 3); // 3h
$cache->purgeCache(60 * 60 * 3);
$api = new TwitterClient($cache);
$screenName = $this->getInput('u');

View File

@ -3,8 +3,8 @@
class FileCache implements CacheInterface
{
private array $config;
protected $scope;
protected $key;
protected string $scope;
protected string $key;
public function __construct(array $config = [])
{
@ -39,13 +39,12 @@ class FileCache implements CacheInterface
return $data;
}
public function saveData($data)
public function saveData($data): void
{
$writeStream = file_put_contents($this->getCacheFile(), serialize($data));
if ($writeStream === false) {
throw new \Exception('The cache path is not writeable. You probably want: chown www-data:www-data cache');
}
return $this;
}
public function getTime(): ?int
@ -63,7 +62,7 @@ class FileCache implements CacheInterface
return null;
}
public function purgeCache($seconds)
public function purgeCache(int $seconds): void
{
if (! $this->config['enable_purge']) {
return;
@ -90,27 +89,14 @@ class FileCache implements CacheInterface
}
}
public function setScope($scope)
public function setScope(string $scope): void
{
if (!is_string($scope)) {
throw new \Exception('The given scope is invalid!');
}
$this->scope = $this->config['path'] . trim($scope, " \t\n\r\0\x0B\\\/") . '/';
return $this;
}
public function setKey($key)
public function setKey(array $key): void
{
$key = json_encode($key);
if (!is_string($key)) {
throw new \Exception('The given key is invalid!');
}
$this->key = $key;
return $this;
$this->key = json_encode($key);
}
private function getScope()

View File

@ -2,8 +2,8 @@
class MemcachedCache implements CacheInterface
{
private $scope;
private $key;
private string $scope;
private string $key;
private $conn;
private $expiration = 0;
private $time = null;
@ -58,11 +58,11 @@ class MemcachedCache implements CacheInterface
return $result['data'];
}
public function saveData($datas)
public function saveData($data): void
{
$time = time();
$object_to_save = [
'data' => $datas,
'data' => $data,
'time' => $time,
];
$result = $this->conn->set($this->getCacheKey(), $object_to_save, $this->expiration);
@ -72,8 +72,6 @@ class MemcachedCache implements CacheInterface
}
$this->time = $time;
return $this;
}
public function getTime(): ?int
@ -84,32 +82,21 @@ class MemcachedCache implements CacheInterface
return $this->time;
}
public function purgeCache($duration)
public function purgeCache(int $seconds): void
{
// Note: does not purges cache right now
// Just sets cache expiration and leave cache purging for memcached itself
$this->expiration = $duration;
$this->expiration = $seconds;
}
public function setScope($scope)
public function setScope(string $scope): void
{
$this->scope = $scope;
return $this;
}
public function setKey($key)
public function setKey(array $key): void
{
if (!empty($key) && is_array($key)) {
$key = array_map('strtolower', $key);
}
$key = json_encode($key);
if (!is_string($key)) {
throw new \Exception('The given key is invalid!');
}
$this->key = $key;
return $this;
$this->key = json_encode($key);
}
private function getCacheKey()

View File

@ -4,11 +4,11 @@ declare(strict_types=1);
class NullCache implements CacheInterface
{
public function setScope($scope)
public function setScope(string $scope): void
{
}
public function setKey($key)
public function setKey(array $key): void
{
}
@ -16,7 +16,7 @@ class NullCache implements CacheInterface
{
}
public function saveData($data)
public function saveData($data): void
{
}
@ -25,7 +25,7 @@ class NullCache implements CacheInterface
return null;
}
public function purgeCache($seconds)
public function purgeCache(int $seconds): void
{
}
}

View File

@ -5,8 +5,8 @@
*/
class SQLiteCache implements CacheInterface
{
protected $scope;
protected $key;
protected string $scope;
protected string $key;
private $db = null;
@ -59,15 +59,13 @@ class SQLiteCache implements CacheInterface
return null;
}
public function saveData($data)
public function saveData($data): void
{
$Qupdate = $this->db->prepare('INSERT OR REPLACE INTO storage (key, value, updated) VALUES (:key, :value, :updated)');
$Qupdate->bindValue(':key', $this->getCacheKey());
$Qupdate->bindValue(':value', serialize($data));
$Qupdate->bindValue(':updated', time());
$Qupdate->execute();
return $this;
}
public function getTime(): ?int
@ -85,36 +83,21 @@ class SQLiteCache implements CacheInterface
return null;
}
public function purgeCache($seconds)
public function purgeCache(int $seconds): void
{
$Qdelete = $this->db->prepare('DELETE FROM storage WHERE updated < :expired');
$Qdelete->bindValue(':expired', time() - $seconds);
$Qdelete->execute();
}
public function setScope($scope)
public function setScope(string $scope): void
{
if (is_null($scope) || !is_string($scope)) {
throw new \Exception('The given scope is invalid!');
}
$this->scope = $scope;
return $this;
}
public function setKey($key)
public function setKey(array $key): void
{
if (!empty($key) && is_array($key)) {
$key = array_map('strtolower', $key);
}
$key = json_encode($key);
if (!is_string($key)) {
throw new \Exception('The given key is invalid!');
}
$this->key = $key;
return $this;
$this->key = json_encode($key);
}
private function getCacheKey()

View File

@ -1,24 +1,3 @@
Create a new file in the `caches/` folder (see [Folder structure](../04_For_Developers/03_Folder_structure.md)).
The file must be named according to following specification:
* It starts with the type
* The file name must end with 'Cache'
* The file type must be PHP, written in small letters (seriously!) ".php"
**Examples:**
Type | Filename
-----|---------
File | FileCache.php
MySQL | MySQLCache.php
The file must start with the PHP tags and end with an empty line. The closing tag `?>` is [omitted](http://php.net/basic-syntax.instruction-separation).
Example:
```PHP
<?PHP
// PHP code here
// This line is empty (just imagine it!)
```
See `NullCache` and `SQLiteCache` for examples.

View File

@ -1,67 +1,18 @@
The `CacheInterface` interface defines functions that need to be implemented. To create a new cache that implements `CacheInterface` you must implement following functions:
See `CacheInterface`.
* [loadData](#the-loaddata-function)
* [saveData](#the-savedata-function)
* [getTime](#the-gettime-function)
* [purgeCache](#the-purgecache-function)
```php
interface CacheInterface
{
public function setScope(string $scope): void;
Find a [template](#template) at the end of this file.
public function setKey(array $key): void;
# Functions
public function loadData();
## The `loadData` function
public function saveData($data): void;
This function loads data from the cache and returns the data in the same format provided to the [saveData](#the-savedata-function) function.
public function getTime(): ?int;
```PHP
loadData(): mixed
```
## The `saveData` function
This function stores the given data into the cache and returns the object instance.
```PHP
saveData(mixed $data): self
```
## The `getTime` function
## The `purgeCache` function
This function removes any data from the cache that is not within the given duration. The duration is specified in seconds and defines the period between now and the oldest item to keep.
```PHP
purgeCache(int $duration): null
```
# Template
This is the bare minimum template for a new cache:
```PHP
<?php
class MyTypeCache implements CacheInterface {
public function loadData(){
// Implement your algorithm here!
return null;
}
public function saveData($data){
// Implement your algorithm here!
return $this;
}
public function getTime(){
// Implement your algorithm here!
return false;
}
public function purgeCache($duration){
// Implement your algorithm here!
}
}
// Imaginary empty line!
public function purgeCache(int $seconds): void;
}
```

View File

@ -410,11 +410,10 @@ abstract class BridgeAbstract implements BridgeInterface
/**
* Loads a cached value for the specified key
*
* @param string $key Key name
* @param int $duration Cache duration (optional)
* @return mixed Cached value or null if the key doesn't exist or has expired
*/
protected function loadCacheValue($key, $duration = null)
protected function loadCacheValue(string $key, $duration = null)
{
$cacheFactory = new CacheFactory();
@ -422,7 +421,7 @@ abstract class BridgeAbstract implements BridgeInterface
// Create class name without the namespace part
$scope = $this->getShortName();
$cache->setScope($scope);
$cache->setKey($key);
$cache->setKey([$key]);
$timestamp = $cache->getTime();
if (
@ -438,17 +437,16 @@ abstract class BridgeAbstract implements BridgeInterface
/**
* Stores a value to cache with the specified key
*
* @param string $key Key name
* @param mixed $value Value to cache
*/
protected function saveCacheValue($key, $value)
protected function saveCacheValue(string $key, $value)
{
$cacheFactory = new CacheFactory();
$cache = $cacheFactory->create();
$scope = $this->getShortName();
$cache->setScope($scope);
$cache->setKey($key);
$cache->setKey([$key]);
$cache->saveData($value);
}

View File

@ -1,68 +1,16 @@
<?php
/**
* This file is part of RSS-Bridge, a PHP project capable of generating RSS and
* Atom feeds for websites that don't have one.
*
* For the full license information, please view the UNLICENSE file distributed
* with this source code.
*
* @package Core
* @license http://unlicense.org/ UNLICENSE
* @link https://github.com/rss-bridge/rss-bridge
*/
/**
* The cache interface
*/
interface CacheInterface
{
/**
* Set scope of the current cache
*
* If $scope is an empty string, the cache is set to a global context.
*
* @param string $scope The scope the data is related to
*/
public function setScope($scope);
public function setScope(string $scope): void;
/**
* Set key to assign the current data
*
* Since $key can be anything, the cache implementation must ensure to
* assign the related data reliably; most commonly by serializing and
* hashing the key in an appropriate way.
*
* @param array $key The key the data is related to
*/
public function setKey($key);
public function setKey(array $key): void;
/**
* Loads data from cache
*
* @return mixed The cached data or null
*/
public function loadData();
/**
* Stores data to the cache
*
* @param mixed $data The data to store
* @return self The cache object
*/
public function saveData($data);
public function saveData($data): void;
/**
* Returns the modification time of the current cache item.
* In unix timestamp.
* Example: 1688570578
*/
public function getTime(): ?int;
/**
* Removes any data that is older than the specified age from cache
*
* @param int $seconds The cache age in seconds
*/
public function purgeCache($seconds);
public function purgeCache(int $seconds): void;
}

View File

@ -103,7 +103,7 @@ function getContents(
$cache = $cacheFactory->create();
$cache->setScope('server');
$cache->purgeCache(86400); // 24 hours (forced)
$cache->purgeCache(86400);
$cache->setKey([$url]);
// Snagged from https://github.com/lwthiker/curl-impersonate/blob/main/firefox/curl_ff102