Commit 274bb3c7 by Scott

Split cache manager and file cache driver

Set up CacheManager as a Singleton.
parent 7b2262f8
...@@ -25,32 +25,73 @@ ...@@ -25,32 +25,73 @@
*/ */
class Q2A_Storage_CacheManager class Q2A_Storage_CacheManager
{ {
private static $instance;
private $enabled = false; private $enabled = false;
private $error = ''; private $cacheDriver;
private $dir;
/** /**
* Creates a new CacheManager instance and checks it's set up properly. * Creates a new CacheManager instance and sets up the cache driver.
*/ */
public function __construct() private function __construct()
{ {
$optEnabled = qa_opt('caching_enabled') == 1; $optEnabled = qa_opt('caching_enabled') == 1;
if (defined('QA_CACHE_DIRECTORY')) { $config = array(
// expand symlinks so we compare true paths 'dir' => defined('QA_CACHE_DIRECTORY') ? QA_CACHE_DIRECTORY : null,
$this->dir = realpath(QA_CACHE_DIRECTORY); );
$baseDir = realpath(QA_BASE_DIR);
if (!is_writable($this->dir)) { $this->cacheDriver = new Q2A_Storage_FileCache($config);
$this->error = qa_lang_html_sub('admin/caching_dir_error', QA_CACHE_DIRECTORY); $this->enabled = $optEnabled && $this->cacheDriver->isEnabled();
} elseif (strpos($this->dir, $baseDir) === 0) { }
$this->error = qa_lang_html_sub('admin/caching_dir_public', QA_CACHE_DIRECTORY);
/**
* 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;
}
$this->enabled = empty($this->error) && $optEnabled; /**
} elseif ($optEnabled) { * Serialize some data and store it in the cache.
$this->error = qa_lang_html('admin/caching_dir_missing'); * @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;
} }
/** /**
...@@ -68,6 +109,6 @@ class Q2A_Storage_CacheManager ...@@ -68,6 +109,6 @@ class Q2A_Storage_CacheManager
*/ */
public function getError() public function getError()
{ {
return $this->error; return isset($this->cacheDriver) ? $this->cacheDriver->getError() : '';
} }
} }
<?php
/*
Question2Answer by Gideon Greenspan and contributors
http://www.question2answer.org/
File: qa-include/Q2A/Storage/FileCache.php
Description: File-based driver 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_FileCache
{
private $enabled = false;
private $error;
private $cacheDir;
/**
* Creates a new FileCache instance and checks we can write to the cache directory.
* @param array $config Configuration data, including cache storage directory.
*/
public function __construct($config)
{
if (!isset($config['dir']))
return;
$this->cacheDir = realpath($config['dir']);
if (!is_writable($this->cacheDir)) {
$this->error = qa_lang_html_sub('admin/caching_dir_error', $this->cacheDir);
} elseif (strpos($this->cacheDir, realpath($_SERVER['DOCUMENT_ROOT'])) === 0 || strpos($this->cacheDir, realpath(QA_BASE_DIR)) === 0) {
// check the folder is outside the public root - checks against server root and Q2A root, in order to handle symbolic links
$this->error = qa_lang_html_sub('admin/caching_dir_public', $this->cacheDir);
}
$this->enabled = empty($this->error);
}
/**
* Get the cached data for the supplied key.
* @param string $key The unique cache identifier.
* @return string The cached data, or null otherwise.
*/
public function get($key)
{
$file = $this->getFilename($key);
if (is_readable($file)) {
$lines = file($file, FILE_IGNORE_NEW_LINES);
$actualKey = array_shift($lines);
// double check this is the correct data
if ($key === $actualKey) {
$expiry = array_shift($lines);
if (is_numeric($expiry) && time() < $expiry) {
return implode("\n", $lines);
}
}
}
return null;
}
/**
* Store a string (usually serialized data) in the cache along with the key and expiry time.
* @param string $key The unique cache identifier.
* @param string $str The data to cache (usually a serialized string).
* @param int $ttl Number of minutes for which to cache the data.
* @return bool Whether the file was successfully cached.
*/
public function set($key, $str, $ttl)
{
$success = false;
if ($this->enabled) {
$file = $this->getFilename($key);
$dir = dirname($file);
$expiry = time() + ($ttl * 60);
$cache = $key . "\n" . $expiry . "\n" . $str;
if (is_dir($dir) || mkdir($dir, 0777, true)) {
$success = file_put_contents($file, $cache) !== false;
}
}
return $success;
}
/**
* Whether caching is available.
* @return bool
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* Get the last error.
* @return string
*/
public function getError()
{
return $this->error;
}
/**
* Generates filename for cache key, of the form `1/23/123abc`
* @param string $key The unique cache key.
* @return string
*/
private function getFilename($key)
{
$filename = sha1($key);
return $this->cacheDir . '/' . substr($filename, 0, 1) . '/' . substr($filename, 1, 2) . '/' . $filename;
}
}
...@@ -1781,12 +1781,9 @@ ...@@ -1781,12 +1781,9 @@
break; break;
case 'caching': case 'caching':
$cacheManager = new Q2A_Storage_CacheManager; $cacheManager = Q2A_Storage_CacheManager::getInstance();
if (!$cacheManager->isEnabled()) { $qa_content['error'] = $cacheManager->getError();
$cacheError = $cacheManager->getError();
if ($cacheError)
$qa_content['error'] = $cacheError;
}
break; break;
} }
......
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