2023-03-11 12:04:29 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
if (!defined('BB_ROOT')) die(basename(__FILE__));
|
|
|
|
|
|
|
|
class cache_sqlite extends cache_common
|
|
|
|
{
|
2023-10-21 07:16:17 +03:00
|
|
|
var $engine = 'SQLite';
|
2023-03-11 12:04:29 +03:00
|
|
|
var $used = true;
|
|
|
|
var $db = null;
|
|
|
|
var $prefix = null;
|
|
|
|
var $cfg = array(
|
|
|
|
'db_file_path' => '/path/to/cache.db.sqlite',
|
|
|
|
'table_name' => 'cache',
|
|
|
|
'table_schema' => 'CREATE TABLE cache (
|
|
|
|
cache_name VARCHAR(255),
|
|
|
|
cache_expire_time INT,
|
|
|
|
cache_value TEXT,
|
|
|
|
PRIMARY KEY (cache_name)
|
|
|
|
)',
|
|
|
|
'pconnect' => true,
|
|
|
|
'con_required' => true,
|
|
|
|
'log_name' => 'CACHE',
|
|
|
|
);
|
|
|
|
|
|
|
|
function cache_sqlite ($cfg, $prefix = null)
|
|
|
|
{
|
2023-08-10 09:35:32 +03:00
|
|
|
if (!$this->is_installed())
|
|
|
|
{
|
|
|
|
die("Error: {$this->db->engine} extension not installed");
|
|
|
|
}
|
2023-03-31 20:47:52 +03:00
|
|
|
|
2023-03-11 12:04:29 +03:00
|
|
|
$this->cfg = array_merge($this->cfg, $cfg);
|
|
|
|
$this->db = new sqlite_common($this->cfg);
|
|
|
|
$this->prefix = $prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
function get ($name, $get_miss_key_callback = '', $ttl = 604800)
|
|
|
|
{
|
|
|
|
if (empty($name))
|
|
|
|
{
|
|
|
|
return is_array($name) ? array() : false;
|
|
|
|
}
|
|
|
|
$this->db->shard($name);
|
|
|
|
$cached_items = array();
|
2023-10-23 14:49:27 +03:00
|
|
|
$this->prefix_len = strlen($this->prefix);
|
|
|
|
$this->prefix_sql = sqlite3_escape_string($this->prefix);
|
2023-03-11 12:04:29 +03:00
|
|
|
|
|
|
|
$name_ary = $name_sql = (array) $name;
|
2023-04-01 08:28:10 +03:00
|
|
|
array_deep($name_sql, 'sqlite3_escape_string');
|
2023-03-11 12:04:29 +03:00
|
|
|
|
|
|
|
// get available items
|
|
|
|
$rowset = $this->db->fetch_rowset("
|
|
|
|
SELECT cache_name, cache_value
|
|
|
|
FROM ". $this->cfg['table_name'] ."
|
2023-10-23 14:49:27 +03:00
|
|
|
WHERE cache_name IN('$this->prefix_sql". join("','$this->prefix_sql", $name_sql) ."') AND cache_expire_time > ". TIMENOW ."
|
2023-03-11 12:04:29 +03:00
|
|
|
LIMIT ". count($name) ."
|
|
|
|
");
|
|
|
|
|
|
|
|
$this->db->debug('start', 'unserialize()');
|
|
|
|
foreach ($rowset as $row)
|
|
|
|
{
|
2023-10-23 14:49:27 +03:00
|
|
|
$cached_items[substr($row['cache_name'], $this->prefix_len)] = unserialize($row['cache_value']);
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
$this->db->debug('stop');
|
|
|
|
|
|
|
|
// get miss items
|
|
|
|
if ($get_miss_key_callback AND $miss_key = array_diff($name_ary, array_keys($cached_items)))
|
|
|
|
{
|
|
|
|
foreach ($get_miss_key_callback($miss_key) as $k => $v)
|
|
|
|
{
|
|
|
|
$this->set($this->prefix . $k, $v, $ttl);
|
|
|
|
$cached_items[$k] = $v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// return
|
|
|
|
if (is_array($this->prefix . $name))
|
|
|
|
{
|
|
|
|
return $cached_items;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return isset($cached_items[$name]) ? $cached_items[$name] : false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function set ($name, $value, $ttl = 604800)
|
|
|
|
{
|
|
|
|
$this->db->shard($this->prefix . $name);
|
2023-04-01 08:28:10 +03:00
|
|
|
$name_sql = sqlite3_escape_string($this->prefix . $name);
|
2023-03-11 12:04:29 +03:00
|
|
|
$expire = TIMENOW + $ttl;
|
2023-04-01 08:28:10 +03:00
|
|
|
$value_sql = sqlite3_escape_string(serialize($value));
|
2023-03-11 12:04:29 +03:00
|
|
|
|
|
|
|
$result = $this->db->query("REPLACE INTO ". $this->cfg['table_name'] ." (cache_name, cache_expire_time, cache_value) VALUES ('$name_sql', $expire, '$value_sql')");
|
|
|
|
return (bool) $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function rm ($name = '')
|
|
|
|
{
|
|
|
|
if ($name)
|
|
|
|
{
|
|
|
|
$this->db->shard($this->prefix . $name);
|
2023-04-01 08:28:10 +03:00
|
|
|
$result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_name = '". sqlite3_escape_string($this->prefix . $name) ."'");
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$result = $this->db->query("DELETE FROM ". $this->cfg['table_name']);
|
|
|
|
}
|
|
|
|
return (bool) $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function gc ($expire_time = TIMENOW)
|
|
|
|
{
|
|
|
|
$result = $this->db->query("DELETE FROM ". $this->cfg['table_name'] ." WHERE cache_expire_time < $expire_time");
|
|
|
|
return ($result) ? $this->db->changes() : 0;
|
|
|
|
}
|
2023-03-31 20:47:52 +03:00
|
|
|
|
2023-08-10 09:35:32 +03:00
|
|
|
function is_installed ()
|
|
|
|
{
|
|
|
|
return class_exists('SQLite3');
|
|
|
|
}
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
class sqlite_common extends cache_common
|
|
|
|
{
|
|
|
|
var $cfg = array(
|
|
|
|
'db_file_path' => 'sqlite.db',
|
|
|
|
'table_name' => 'table_name',
|
|
|
|
'table_schema' => 'CREATE TABLE table_name (...)',
|
|
|
|
'pconnect' => true,
|
|
|
|
'con_required' => true,
|
|
|
|
'log_name' => 'SQLite',
|
|
|
|
'shard_type' => 'none', # none, string, int (тип перевичного ключа для шардинга)
|
|
|
|
'shard_val' => 0, # для string - кол. начальных символов, для int - делитель (будет использован остаток от деления)
|
|
|
|
);
|
|
|
|
var $engine = 'SQLite';
|
|
|
|
var $dbh = null;
|
|
|
|
var $connected = false;
|
|
|
|
var $shard_val = false;
|
|
|
|
|
|
|
|
var $table_create_attempts = 0;
|
|
|
|
|
|
|
|
function sqlite_common ($cfg)
|
|
|
|
{
|
|
|
|
$this->cfg = array_merge($this->cfg, $cfg);
|
|
|
|
$this->dbg_enabled = sql_dbg_enabled();
|
2023-09-17 13:34:27 +03:00
|
|
|
|
|
|
|
// Creates filecache dir
|
2023-09-27 16:24:27 +03:00
|
|
|
if ($db_dir = dirname($this->cfg['db_file_path']))
|
2023-09-17 13:34:27 +03:00
|
|
|
{
|
2023-09-27 16:24:27 +03:00
|
|
|
if (!is_dir($db_dir))
|
2023-09-17 13:34:27 +03:00
|
|
|
{
|
2023-09-27 16:24:27 +03:00
|
|
|
if (!bb_mkdir($db_dir))
|
2023-09-17 13:34:27 +03:00
|
|
|
{
|
2023-09-27 16:24:27 +03:00
|
|
|
$create_error = "Cannot create {$this->engine} cache dir: $db_dir";
|
2023-09-17 13:34:27 +03:00
|
|
|
|
2023-09-27 16:24:27 +03:00
|
|
|
if (DBG_LOG)
|
|
|
|
{
|
|
|
|
dbg_log($create_error, "{$this->engine}-CACHE-mkdir-FAIL_" . time());
|
|
|
|
}
|
|
|
|
|
|
|
|
die($create_error);
|
|
|
|
}
|
2023-09-17 13:34:27 +03:00
|
|
|
}
|
|
|
|
}
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function connect ()
|
|
|
|
{
|
|
|
|
$this->cur_query = ($this->dbg_enabled) ? 'connect to: '. $this->cfg['db_file_path'] : 'connect';
|
|
|
|
$this->debug('start');
|
|
|
|
|
|
|
|
if (@$this->dbh = new SQLite3($this->cfg['db_file_path']))
|
|
|
|
{
|
|
|
|
$this->connected = true;
|
|
|
|
}
|
|
|
|
|
2023-08-10 10:13:43 +03:00
|
|
|
if (!$this->connected && $this->cfg['con_required'])
|
|
|
|
{
|
2023-10-21 04:51:15 +03:00
|
|
|
header("HTTP/1.0 503 Service Unavailable");
|
2023-08-10 10:13:43 +03:00
|
|
|
$con_error = "Could not connect to {$this->engine}";
|
2023-03-11 12:04:29 +03:00
|
|
|
|
2023-08-10 10:13:43 +03:00
|
|
|
if (DBG_LOG)
|
|
|
|
{
|
|
|
|
dbg_log($con_error, "{$this->engine}-CACHE-connect-FAIL_" . time());
|
|
|
|
}
|
2023-04-30 11:04:43 +03:00
|
|
|
|
2023-08-10 10:13:43 +03:00
|
|
|
die($con_error);
|
|
|
|
}
|
2023-03-11 12:04:29 +03:00
|
|
|
|
|
|
|
$this->debug('stop');
|
|
|
|
$this->cur_query = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
function create_table ()
|
|
|
|
{
|
|
|
|
$this->table_create_attempts++;
|
|
|
|
return $this->dbh->query($this->cfg['table_schema']);
|
|
|
|
}
|
|
|
|
|
|
|
|
function shard ($name)
|
|
|
|
{
|
|
|
|
$type = $this->cfg['shard_type'];
|
|
|
|
|
|
|
|
if ($type == 'none') return;
|
|
|
|
if (is_array($name)) trigger_error('cannot shard: $name is array', E_USER_ERROR);
|
|
|
|
|
|
|
|
// define shard_val
|
|
|
|
if ($type == 'string')
|
|
|
|
{
|
|
|
|
$shard_val = substr($name, 0, $this->cfg['shard_val']);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
$shard_val = $name % $this->cfg['shard_val'];
|
|
|
|
}
|
|
|
|
// все запросы должны быть к одному и тому же шарду
|
|
|
|
if ($this->shard_val !== false)
|
|
|
|
{
|
|
|
|
if ($shard_val != $this->shard_val)
|
|
|
|
{
|
|
|
|
trigger_error("shard cannot be reassigned. [{$this->shard_val}, $shard_val, $name]", E_USER_ERROR);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->shard_val = $shard_val;
|
|
|
|
$this->cfg['db_file_path'] = str_replace('*', $shard_val, $this->cfg['db_file_path']);
|
|
|
|
}
|
|
|
|
|
|
|
|
function query ($query)
|
|
|
|
{
|
|
|
|
if (!$this->connected) $this->connect();
|
|
|
|
|
|
|
|
$this->cur_query = $query;
|
|
|
|
$this->debug('start');
|
|
|
|
|
|
|
|
if (!$result = @$this->dbh->query($query))
|
|
|
|
{
|
|
|
|
$rowsresult = $this->dbh->query("PRAGMA table_info({$this->cfg['table_name']})");
|
|
|
|
$rowscount = 0;
|
|
|
|
while ($row = $rowsresult->fetchArray(SQLITE3_ASSOC))
|
|
|
|
{
|
|
|
|
$rowscount++;
|
|
|
|
}
|
|
|
|
if (!$this->table_create_attempts && !$rowscount)
|
|
|
|
{
|
|
|
|
if ($this->create_table())
|
|
|
|
{
|
|
|
|
$result = $this->dbh->query($query);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!$result)
|
|
|
|
{
|
|
|
|
$this->trigger_error($this->get_error_msg());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->debug('stop');
|
|
|
|
$this->cur_query = null;
|
|
|
|
|
|
|
|
$this->num_queries++;
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2023-10-21 05:35:26 +03:00
|
|
|
function insert_id()
|
|
|
|
{
|
|
|
|
return $this->dbh->lastInsertRowID();
|
|
|
|
}
|
|
|
|
|
2023-03-11 12:04:29 +03:00
|
|
|
function fetch_row ($query)
|
|
|
|
{
|
|
|
|
$result = $this->query($query);
|
2023-10-21 05:48:02 +03:00
|
|
|
$row = $result->fetchArray(SQLITE3_ASSOC);
|
|
|
|
return is_resource($result) ? $row : false;
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function fetch_rowset ($query)
|
|
|
|
{
|
|
|
|
$result = $this->query($query);
|
|
|
|
$rowset = array();
|
|
|
|
while ($row = $result->fetchArray(SQLITE3_ASSOC))
|
|
|
|
{
|
|
|
|
$rowset[] = $row;
|
|
|
|
}
|
|
|
|
return $rowset;
|
|
|
|
}
|
|
|
|
|
|
|
|
function changes ()
|
|
|
|
{
|
2023-10-21 05:48:10 +03:00
|
|
|
$changes = $this->dbh->changes();
|
|
|
|
return is_resource($this->dbh) ? $changes : 0;
|
2023-03-11 12:04:29 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
function escape ($str)
|
|
|
|
{
|
|
|
|
return SQLite3::escapeString($str);
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_error_msg ()
|
|
|
|
{
|
|
|
|
return 'SQLite error #'. ($err_code = $this->dbh->lastErrorCode()) .': '. $this->dbh->lastErrorMsg();
|
|
|
|
}
|
|
|
|
|
|
|
|
function trigger_error ($msg = 'DB Error')
|
|
|
|
{
|
|
|
|
if (error_reporting()) trigger_error($msg, E_USER_ERROR);
|
|
|
|
}
|
2023-09-17 13:34:27 +03:00
|
|
|
}
|