mirror of
https://github.com/Lastorder-DC/rhymix.git
synced 2026-01-07 10:41:40 +09:00
300 lines
6 KiB
PHP
300 lines
6 KiB
PHP
<?php
|
|
|
|
namespace Rhymix\Framework\Drivers\Cache;
|
|
|
|
use Rhymix\Framework\Storage;
|
|
|
|
/**
|
|
* The SQLite cache driver.
|
|
*/
|
|
class SQLite implements \Rhymix\Framework\Drivers\CacheInterface
|
|
{
|
|
/**
|
|
* Set this flag to false to disable cache prefixes.
|
|
*/
|
|
public $prefix = false;
|
|
|
|
/**
|
|
* The singleton instance is stored here.
|
|
*/
|
|
protected static $_instance = null;
|
|
|
|
/**
|
|
* The database handle and prepared statements are stored here.
|
|
*/
|
|
protected $_dbh = null;
|
|
protected $_ps = array();
|
|
|
|
/**
|
|
* Direct invocation of the constructor is not permitted.
|
|
*/
|
|
protected function __construct()
|
|
{
|
|
$dir = \RX_BASEDIR . 'files/cache/store';
|
|
if (!Storage::isDirectory($dir))
|
|
{
|
|
Storage::createDirectory($dir);
|
|
}
|
|
|
|
$key = substr(hash_hmac('sha256', $dir, config('crypto.authentication_key')), 0, 32);
|
|
$filename = "$dir/$key.db";
|
|
if (Storage::exists($filename))
|
|
{
|
|
$this->_connect($filename);
|
|
}
|
|
else
|
|
{
|
|
$this->_connect($filename);
|
|
for ($i = 0; $i < 32; $i++)
|
|
{
|
|
$this->_dbh->exec('CREATE TABLE cache_' . $i . ' (k TEXT PRIMARY KEY, v TEXT, exp INT)');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Connect to an SQLite3 database.
|
|
*
|
|
* @param string $filename
|
|
* @return void
|
|
*/
|
|
protected function _connect($filename)
|
|
{
|
|
$this->_dbh = new \SQLite3($filename);
|
|
$this->_dbh->busyTimeout(250);
|
|
$this->_dbh->exec('PRAGMA journal_mode = MEMORY');
|
|
$this->_dbh->exec('PRAGMA synchronous = OFF');
|
|
}
|
|
|
|
/**
|
|
* Create a new instance of the current cache driver, using the given settings.
|
|
*
|
|
* @param array $config
|
|
* @return void
|
|
*/
|
|
public static function getInstance(array $config)
|
|
{
|
|
if (self::$_instance === null)
|
|
{
|
|
self::$_instance = new self();
|
|
}
|
|
return self::$_instance;
|
|
}
|
|
|
|
/**
|
|
* Check if the current cache driver is supported on this server.
|
|
*
|
|
* This method returns true on success and false on failure.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function isSupported()
|
|
{
|
|
return class_exists('\\SQLite3', false) && config('crypto.authentication_key') !== null && stripos(\PHP_SAPI, 'win') === false;
|
|
}
|
|
|
|
/**
|
|
* Validate cache settings.
|
|
*
|
|
* This method returns true on success and false on failure.
|
|
*
|
|
* @param mixed $config
|
|
* @return bool
|
|
*/
|
|
public static function validateSettings($config)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the value of a key.
|
|
*
|
|
* This method returns null if the key was not found.
|
|
*
|
|
* @param string $key
|
|
* @return mixed
|
|
*/
|
|
public function get($key)
|
|
{
|
|
$table = 'cache_' . (crc32($key) % 32);
|
|
$stmt = $this->_dbh->prepare('SELECT v, exp FROM ' . $table . ' WHERE k = :key');
|
|
if (!$stmt)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
|
$result = $stmt->execute();
|
|
if (!$result)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
$row = $result->fetchArray(\SQLITE3_NUM);
|
|
if ($row)
|
|
{
|
|
if ($row[1] == 0 || $row[1] >= time())
|
|
{
|
|
return unserialize($row[0]);
|
|
}
|
|
else
|
|
{
|
|
$this->delete($key);
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the value to a key.
|
|
*
|
|
* This method returns true on success and false on failure.
|
|
* $ttl is measured in seconds. If it is zero, the key should not expire.
|
|
*
|
|
* @param string $key
|
|
* @param mixed $value
|
|
* @param int $ttl
|
|
* @param bool $force
|
|
* @return bool
|
|
*/
|
|
public function set($key, $value, $ttl = 0, $force = false)
|
|
{
|
|
$table = 'cache_' . (crc32($key) % 32);
|
|
$stmt = $this->_dbh->prepare('INSERT OR REPLACE INTO ' . $table . ' (k, v, exp) VALUES (:key, :val, :exp)');
|
|
if (!$stmt)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
|
$stmt->bindValue(':val', serialize($value), \SQLITE3_TEXT);
|
|
$stmt->bindValue(':exp', $ttl ? (time() + $ttl) : 0, \SQLITE3_INTEGER);
|
|
return $stmt->execute() ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Delete a key.
|
|
*
|
|
* This method returns true on success and false on failure.
|
|
* If the key does not exist, it should return false.
|
|
*
|
|
* @param string $key
|
|
* @return bool
|
|
*/
|
|
public function delete($key)
|
|
{
|
|
$table = 'cache_' . (crc32($key) % 32);
|
|
$stmt = $this->_dbh->prepare('DELETE FROM ' . $table . ' WHERE k = :key');
|
|
if (!$stmt)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
|
return $stmt->execute() ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Check if a key exists.
|
|
*
|
|
* This method returns true on success and false on failure.
|
|
*
|
|
* @param string $key
|
|
* @return bool
|
|
*/
|
|
public function exists($key)
|
|
{
|
|
$table = 'cache_' . (crc32($key) % 32);
|
|
$stmt = $this->_dbh->prepare('SELECT 1 FROM ' . $table . ' WHERE k = :key AND (exp = 0 OR exp >= :exp)');
|
|
if (!$stmt)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$stmt->bindValue(':key', $key, \SQLITE3_TEXT);
|
|
$stmt->bindValue(':exp', time(), \SQLITE3_INTEGER);
|
|
$result = $stmt->execute();
|
|
if (!$result)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$row = $result->fetchArray(\SQLITE3_NUM);
|
|
if ($row)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Increase the value of a key by $amount.
|
|
*
|
|
* If the key does not exist, this method assumes that the current value is zero.
|
|
* This method returns the new value.
|
|
*
|
|
* @param string $key
|
|
* @param int $amount
|
|
* @return int
|
|
*/
|
|
public function incr($key, $amount)
|
|
{
|
|
$this->_dbh->exec('BEGIN');
|
|
$current_value = $this->get($key);
|
|
$new_value = intval($current_value) + $amount;
|
|
if ($this->set($key, $new_value))
|
|
{
|
|
$this->_dbh->exec('COMMIT');
|
|
return $new_value;
|
|
}
|
|
else
|
|
{
|
|
$this->_dbh->exec('ROLLBACK');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Decrease the value of a key by $amount.
|
|
*
|
|
* If the key does not exist, this method assumes that the current value is zero.
|
|
* This method returns the new value.
|
|
*
|
|
* @param string $key
|
|
* @param int $amount
|
|
* @return int
|
|
*/
|
|
public function decr($key, $amount)
|
|
{
|
|
return $this->incr($key, 0 - $amount);
|
|
}
|
|
|
|
/**
|
|
* Clear all keys from the cache.
|
|
*
|
|
* This method returns true on success and false on failure.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function clear()
|
|
{
|
|
for ($i = 0; $i < 32; $i++)
|
|
{
|
|
$this->_dbh->exec('DROP TABLE cache_' . $i);
|
|
}
|
|
|
|
for ($i = 0; $i < 32; $i++)
|
|
{
|
|
$this->_dbh->exec('CREATE TABLE cache_' . $i . ' (k TEXT PRIMARY KEY, v TEXT, exp INT)');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|