Commit 15f0e033 by Scott

Refactor cache handler

Move functionality to FileCacheDriver, strip down manager class
parent 5c554165
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/CacheManager.php
Description: Handler for caching system.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/**
* Caches data (typically from database queries) to the filesystem.
*/
class Q2A_Storage_CacheFactory
{
/**
* Get the appropriate cache handler.
* @return Q2A_Storage_CacheInterface The cache handler.
*/
public static function getCacheDriver()
{
$config = array(
'enabled' => (int) qa_opt('caching_enabled') === 1,
'dir' => defined('QA_CACHE_DIRECTORY') ? QA_CACHE_DIRECTORY : null,
);
return new Q2A_Storage_FileCacheDriver($config);
}
}
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/CacheManager.php
Description: Handler for caching system.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
More about this license: http://www.question2answer.org/license.php
*/
/**
* Caches data (typically from database queries) to the filesystem.
*/
class Q2A_Storage_CacheManager
{
private static $instance;
private $enabled = false;
private $cacheDriver;
/**
* Creates a new CacheManager instance and sets up the cache driver.
*/
private function __construct()
{
if (qa_opt('caching_enabled') != 1)
return;
$config = array(
'dir' => defined('QA_CACHE_DIRECTORY') ? QA_CACHE_DIRECTORY : null,
);
$this->cacheDriver = new Q2A_Storage_FileCache($config);
$this->enabled = $this->cacheDriver->isEnabled();
}
/**
* Initializes the class and returns the singleton.
* @return Q2A_Storage_CacheManager
*/
public static function getInstance()
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Get the cached data for the supplied key.
* @param string $key The unique cache identifier
* @return mixed The cached data, or null otherwise.
*/
public function get($key)
{
if ($this->enabled) {
$encData = $this->cacheDriver->get($key);
// retrieve data, ignoring any notices
$data = @unserialize($encData);
if ($data !== false) {
return $data;
}
}
return null;
}
/**
* Serialize some data and store it in the cache.
* @param string $key The unique cache identifier
* @param mixed $data The data to cache - must be scalar values (i.e. string, int, array).
* @param int $ttl Number of minutes for which to cache the data.
* @return bool Whether the data was successfully cached.
*/
public function set($key, $data, $ttl)
{
if ($this->enabled) {
$encData = serialize($data);
return $this->cacheDriver->set($key, $encData, $ttl);
}
return false;
}
/**
* Whether caching is available.
* @return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Get the last error.
* @return string
*/
public function getError()
{
return isset($this->cacheDriver) ? $this->cacheDriver->getError() : '';
}
}
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
/** /**
* Caches data (typically from database queries) to the filesystem. * Caches data (typically from database queries) to the filesystem.
*/ */
class Q2A_Storage_FileCache class Q2A_Storage_FileCacheDriver
{ {
private $enabled = false; private $enabled = false;
private $error; private $error;
...@@ -35,6 +35,10 @@ class Q2A_Storage_FileCache ...@@ -35,6 +35,10 @@ class Q2A_Storage_FileCache
*/ */
public function __construct($config) public function __construct($config)
{ {
if (!$config['enabled']) {
return;
}
if (isset($config['dir'])) { if (isset($config['dir'])) {
$this->cacheDir = realpath($config['dir']); $this->cacheDir = realpath($config['dir']);
...@@ -58,6 +62,10 @@ class Q2A_Storage_FileCache ...@@ -58,6 +62,10 @@ class Q2A_Storage_FileCache
*/ */
public function get($key) public function get($key)
{ {
if (!$this->enabled) {
return null;
}
$file = $this->getFilename($key); $file = $this->getFilename($key);
if (is_readable($file)) { if (is_readable($file)) {
...@@ -69,7 +77,12 @@ class Q2A_Storage_FileCache ...@@ -69,7 +77,12 @@ class Q2A_Storage_FileCache
$expiry = array_shift($lines); $expiry = array_shift($lines);
if (is_numeric($expiry) && time() < $expiry) { if (is_numeric($expiry) && time() < $expiry) {
return implode("\n", $lines); $encData = implode("\n", $lines);
// decode data, ignoring any notices
$data = @unserialize($encData);
if ($data !== false) {
return $data;
}
} }
} }
} }
...@@ -78,23 +91,24 @@ class Q2A_Storage_FileCache ...@@ -78,23 +91,24 @@ class Q2A_Storage_FileCache
} }
/** /**
* Store a string (usually serialized data) in the cache along with the key and expiry time. * Store something in the cache along with the key and expiry time. Data gets 'serialized' to a string before storing.
* @param string $key The unique cache identifier. * @param string $key The unique cache identifier.
* @param string $str The data to cache (usually a serialized string). * @param mixed $data The data to cache (in core Q2A this is usually an array).
* @param int $ttl Number of minutes for which to cache the data. * @param int $ttl Number of minutes for which to cache the data.
* @return bool Whether the file was successfully cached. * @return bool Whether the file was successfully cached.
*/ */
public function set($key, $str, $ttl) public function set($key, $data, $ttl)
{ {
$success = false; $success = false;
$ttl = (int) $ttl; $ttl = (int) $ttl;
if ($this->enabled && $ttl > 0) { if ($this->enabled && $ttl > 0) {
$file = $this->getFilename($key); $encData = serialize($data);
$dir = dirname($file);
$expiry = time() + ($ttl * 60); $expiry = time() + ($ttl * 60);
$cache = $key . "\n" . $expiry . "\n" . $str; $cache = $key . "\n" . $expiry . "\n" . $encData;
$file = $this->getFilename($key);
$dir = dirname($file);
if (is_dir($dir) || mkdir($dir, 0777, true)) { if (is_dir($dir) || mkdir($dir, 0777, true)) {
$success = file_put_contents($file, $cache) !== false; $success = file_put_contents($file, $cache) !== false;
} }
......
...@@ -1790,8 +1790,8 @@ switch ($adminsection) { ...@@ -1790,8 +1790,8 @@ switch ($adminsection) {
break; break;
case 'caching': case 'caching':
$cacheManager = Q2A_Storage_CacheManager::getInstance(); $cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver();
$qa_content['error'] = $cacheManager->getError(); $qa_content['error'] = $cacheDriver->getError();
break; break;
} }
......
...@@ -42,13 +42,13 @@ $pagestate = qa_get_state(); ...@@ -42,13 +42,13 @@ $pagestate = qa_get_state();
// Get information about this question // Get information about this question
$cacheHandler = Q2A_Storage_CacheManager::getInstance(); $cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver();
$cacheKey = "page:question:$questionid"; $cacheKey = "page:question:$questionid";
$useCache = $userid === null && $cacheHandler->isEnabled() && !qa_is_http_post() && empty($pagestate); $useCache = $userid === null && $cacheDriver->isEnabled() && !qa_is_http_post() && empty($pagestate);
$saveCache = false; $saveCache = false;
if ($useCache) { if ($useCache) {
$questionData = $cacheHandler->get($cacheKey); $questionData = $cacheDriver->get($cacheKey);
} }
if (!isset($questionData)) { if (!isset($questionData)) {
...@@ -158,7 +158,7 @@ if ($permiterror && (qa_is_human_probably() || !qa_opt('allow_view_q_bots'))) { ...@@ -158,7 +158,7 @@ if ($permiterror && (qa_is_human_probably() || !qa_opt('allow_view_q_bots'))) {
if ($saveCache) { if ($saveCache) {
$questionAge = qa_opt('db_time') - $question['created']; $questionAge = qa_opt('db_time') - $question['created'];
if ($questionAge > 86400 * qa_opt('caching_q_start')) { if ($questionAge > 86400 * qa_opt('caching_q_start')) {
$cacheHandler->set($cacheKey, $questionData, qa_opt('caching_q_time')); $cacheDriver->set($cacheKey, $questionData, qa_opt('caching_q_time'));
} }
} }
......
...@@ -511,11 +511,11 @@ function qa_db_single_select($selectspec) ...@@ -511,11 +511,11 @@ function qa_db_single_select($selectspec)
{ {
// check for cached results // check for cached results
if (isset($selectspec['caching'])) { if (isset($selectspec['caching'])) {
$cacheHandler = Q2A_Storage_CacheManager::getInstance(); $cacheDriver = Q2A_Storage_CacheFactory::getCacheDriver();
$cacheKey = 'query:' . $selectspec['caching']['key']; $cacheKey = 'query:' . $selectspec['caching']['key'];
if ($cacheHandler->isEnabled()) { if ($cacheDriver->isEnabled()) {
$queryData = $cacheHandler->get($cacheKey); $queryData = $cacheDriver->get($cacheKey);
if ($queryData !== null) if ($queryData !== null)
return $queryData; return $queryData;
} }
...@@ -535,8 +535,8 @@ function qa_db_single_select($selectspec) ...@@ -535,8 +535,8 @@ function qa_db_single_select($selectspec)
// save cached results // save cached results
if (isset($selectspec['caching'])) { if (isset($selectspec['caching'])) {
if ($cacheHandler->isEnabled()) { if ($cacheDriver->isEnabled()) {
$cacheHandler->set($cacheKey, $results, $selectspec['caching']['ttl']); $cacheDriver->set($cacheKey, $results, $selectspec['caching']['ttl']);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment