Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caches: Refactored API #1060

Merged
merged 8 commits into from
Apr 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions actions/DisplayAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ public function execute() {

// Initialize cache
$cache = Cache::create(Configuration::getConfig('cache', 'type'));
$cache->setPath(PATH_CACHE);
$cache->setScope('');
$cache->purgeCache(86400); // 24 hours
$cache->setParameters($cache_params);
$cache->setKey($cache_params);

$items = array();
$infos = array();
Expand Down
4 changes: 2 additions & 2 deletions bridges/ElloBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,8 @@ private function getUsername($post, $postData) {

private function getAPIKey() {
$cache = Cache::create(Configuration::getConfig('cache', 'type'));
$cache->setPath(PATH_CACHE);
$cache->setParameters(['key']);
$cache->setScope(get_called_class());
$cache->setKey(['key']);
$key = $cache->loadData();

if($key == null) {
Expand Down
12 changes: 0 additions & 12 deletions bridges/WordPressPluginUpdateBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,4 @@ public function getName(){

return parent::getName();
}

private function getCachedDate($url){
Debug::log('getting pubdate from url ' . $url . '');
// Initialize cache
$cache = Cache::create(Configuration::getConfig('cache', 'type'));
$cache->setPath(PATH_CACHE . 'pages/');
$params = [$url];
$cache->setParameters($params);
// Get cachefile timestamp
$time = $cache->getTime();
return ($time !== false ? $time : time());
}
}
72 changes: 39 additions & 33 deletions caches/FileCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,21 @@
* Cache with file system
*/
class FileCache implements CacheInterface {

protected $path;
protected $param;
protected $key;

public function loadData(){
if(file_exists($this->getCacheFile())) {
return unserialize(file_get_contents($this->getCacheFile()));
}

return null;
}

public function saveData($datas){
public function saveData($data){
// Notice: We use plain serialize() here to reduce memory footprint on
// large input data.
$writeStream = file_put_contents($this->getCacheFile(), serialize($datas));
$writeStream = file_put_contents($this->getCacheFile(), serialize($data));

if($writeStream === false) {
throw new \Exception('Cannot write the cache... Do you have the right permissions ?');
Expand All @@ -29,13 +30,14 @@ public function getTime(){
$cacheFile = $this->getCacheFile();
clearstatcache(false, $cacheFile);
if(file_exists($cacheFile)) {
return filemtime($cacheFile);
$time = filemtime($cacheFile);
return ($time !== false) ? $time : null;
}

return false;
return null;
}

public function purgeCache($duration){
public function purgeCache($seconds){
$cachePath = $this->getPath();
if(file_exists($cachePath)) {
$cacheIterator = new RecursiveIteratorIterator(
Expand All @@ -47,52 +49,58 @@ public function purgeCache($duration){
if(in_array($cacheFile->getBasename(), array('.', '..', '.gitkeep')))
continue;
elseif($cacheFile->isFile()) {
if(filemtime($cacheFile->getPathname()) < time() - $duration)
if(filemtime($cacheFile->getPathname()) < time() - $seconds)
unlink($cacheFile->getPathname());
}
}
}
}

/**
* Set cache path
* Set scope
* @return self
*/
public function setPath($path){
if(is_null($path) || !is_string($path)) {
throw new \Exception('The given path is invalid!');
public function setScope($scope){
if(is_null($scope) || !is_string($scope)) {
throw new \Exception('The given scope is invalid!');
}

$this->path = $path;

// Make sure path ends with '/' or '\'
$lastchar = substr($this->path, -1, 1);
if($lastchar !== '/' && $lastchar !== '\\')
$this->path .= '/';

if(!is_dir($this->path))
mkdir($this->path, 0755, true);
$this->path = PATH_CACHE . trim($scope, " \t\n\r\0\x0B\\\/") . '/';

return $this;
}

/**
* Set HTTP GET parameters
* Set key
* @return self
*/
public function setParameters(array $param){
$this->param = array_map('strtolower', $param);
public function setKey($key){
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;
}

/**
* Return cache path (and create if not exist)
* @return string Cache path
*/
protected function getPath(){
private function getPath(){
if(is_null($this->path)) {
throw new \Exception('Call "setPath" first!');
throw new \Exception('Call "setScope" first!');
}

if(!is_dir($this->path)) {
if (mkdir($this->path, 0755, true) !== true) {
fulmeek marked this conversation as resolved.
Show resolved Hide resolved
throw new \Exception('Unable to create ' . $this->path);
}
}

return $this->path;
Expand All @@ -102,21 +110,19 @@ protected function getPath(){
* Get the file name use for cache store
* @return string Path to the file cache
*/
protected function getCacheFile(){
private function getCacheFile(){
return $this->getPath() . $this->getCacheName();
}

/**
* Determines file name for store the cache
* return string
*/
protected function getCacheName(){
if(is_null($this->param)) {
throw new \Exception('Call "setParameters" first!');
private function getCacheName(){
if(is_null($this->key)) {
throw new \Exception('Call "setKey" first!');
}

// Change character when making incompatible changes to prevent loading
// errors due to incompatible file contents \|/
return hash('md5', http_build_query($this->param) . 'A') . '.cache';
return hash('md5', $this->key) . '.cache';
}
}
60 changes: 41 additions & 19 deletions caches/SQLiteCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,25 @@
* Cache based on SQLite 3 <https://www.sqlite.org>
*/
class SQLiteCache implements CacheInterface {
protected $path;
protected $param;
protected $scope;
protected $key;

private $db = null;

public function __construct() {
if (!extension_loaded('sqlite3'))
if (!extension_loaded('sqlite3')) {
die('"sqlite3" extension not loaded. Please check "php.ini"');
}

$file = PATH_CACHE . 'cache.sqlite';
$file = Configuration::getConfig(get_called_class(), 'file');
if (empty($file)) {
die('Configuration for ' . get_called_class() . ' missing. Please check your config.ini.php');
}
if (dirname($file) == '.') {
$file = PATH_CACHE . $file;
} elseif (!is_dir(dirname($file))) {
die('Invalid configuration for ' . get_called_class() . '. Please check your config.ini.php');
}

if (!is_file($file)) {
$this->db = new SQLite3($file);
Expand All @@ -39,10 +48,10 @@ public function loadData(){
return null;
}

public function saveData($datas){
public function saveData($data){
$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($datas));
$Qupdate->bindValue(':value', serialize($data));
$Qupdate->bindValue(':updated', time());
$Qupdate->execute();

Expand All @@ -60,40 +69,53 @@ public function getTime(){
}
}

return false;
return null;
}

public function purgeCache($duration){
public function purgeCache($seconds){
$Qdelete = $this->db->prepare('DELETE FROM storage WHERE updated < :expired');
$Qdelete->bindValue(':expired', time() - $duration);
$Qdelete->bindValue(':expired', time() - $seconds);
$Qdelete->execute();
}

/**
* Set cache path
* Set scope
* @return self
*/
public function setPath($path){
$this->path = $path;
public function setScope($scope){
if(is_null($scope) || !is_string($scope)) {
throw new \Exception('The given scope is invalid!');
}

$this->scope = $scope;
return $this;
}

/**
* Set HTTP GET parameters
* Set key
* @return self
*/
public function setParameters(array $param){
$this->param = array_map('strtolower', $param);
public function setKey($key){
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;
}

////////////////////////////////////////////////////////////////////////////

protected function getCacheKey(){
if(is_null($this->param)) {
throw new \Exception('Call "setParameters" first!');
private function getCacheKey(){
if(is_null($this->key)) {
throw new \Exception('Call "setKey" first!');
}

return hash('sha1', $this->path . http_build_query($this->param), true);
return hash('sha1', $this->scope . $this->key, true);
}
}
5 changes: 5 additions & 0 deletions config.default.ini.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,8 @@
; The password for authentication. Insert this password when prompted for login.
; Use a strong password to prevent others from guessing your login!
password = ""

; --- Cache specific configuration ---------------------------------------------

[SQLiteCache]
file = "cache.sqlite"
40 changes: 28 additions & 12 deletions lib/CacheInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,38 +13,54 @@

/**
* The cache interface
*
* @todo Add missing function to the interface
* @todo Explain parameters and return values in more detail
* @todo Return self more often (to allow call chaining)
*/
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);

/**
* 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);

/**
* Loads data from cache
*
* @return mixed The cache data
* @return mixed The cached data or null
*/
public function loadData();

/**
* Stores data to the cache
*
* @param mixed $datas The data to store
* @param mixed $data The data to store
* @return self The cache object
*/
public function saveData($datas);
public function saveData($data);

/**
* Returns the timestamp for the curent cache file
* Returns the timestamp for the curent cache data
*
* @return int Timestamp
* @return int Timestamp or null
*/
public function getTime();

/**
* Removes any data that is older than the specified duration from cache
* Removes any data that is older than the specified age from cache
*
* @param int $duration The cache duration in seconds
* @param int $seconds The cache age in seconds
*/
public function purgeCache($duration);
public function purgeCache($seconds);
}
Loading