Last active
September 21, 2019 20:17
-
-
Save sbrl/c1e53d19e500d9bbfc602cd056d4f7c6 to your computer and use it in GitHub Desktop.
[JsonStorageBox.php]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
███████ ████████ ██████ ██████ █████ ██████ ███████ ██████ ██████ ██ ██ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
███████ ██ ██ ██ ██████ ███████ ██ ███ █████ ██████ ██ ██ ███ | |
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ | |
███████ ██ ██████ ██ ██ ██ ██ ██████ ███████ ██████ ██████ ██ ██ | |
*/ | |
/** | |
* Represents a key-value data store. | |
* @license Apache 2.0 | |
*/ | |
class JsonStorageBox { | |
/** | |
* The SQLite database connection. | |
* @var \PDO | |
*/ | |
private $db; | |
/** | |
* A cache of values. | |
* @var object[] | |
*/ | |
private $cache = []; | |
/** | |
* A cache of prepared SQL statements. | |
* @var \PDOStatement[] | |
*/ | |
private $query_cache = []; | |
/** | |
* Initialises a new store connection. | |
* @param string $filename The filename that the store is located in. | |
*/ | |
function __construct(string $filename) { | |
$firstrun = !file_exists($filename); | |
$this->db = new \PDO("sqlite:" . path_resolve($filename, __DIR__)); // HACK: This might not work on some systems, because it depends on the current working directory | |
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |
if($firstrun) { | |
$this->query("CREATE TABLE store (key TEXT UNIQUE NOT NULL, value TEXT)"); | |
} | |
} | |
/** | |
* Makes a query against the database. | |
* @param string $sql The (potentially parametised) query to make. | |
* @param array $variables Optional. The variables to substitute into the SQL query. | |
* @return \PDOStatement The result of the query, as a PDOStatement. | |
*/ | |
private function query(string $sql, array $variables = []) { | |
// Add to the query cache if it doesn't exist | |
if(!isset($this->query_cache[$sql])) | |
$this->query_cache[$sql] = $this->db->prepare($sql); | |
$this->query_cache[$sql]->execute($variables); | |
return $this->query_cache[$sql]; // fetchColumn(), fetchAll(), etc. are defined on the statement, not the return value of execute() | |
} | |
/** | |
* Determines if the given key exists in the store or not. | |
* @param string $key The key to test. | |
* @return bool Whether the key exists in the store or not. | |
*/ | |
public function has(string $key) : bool { | |
if(isset($this->cache[$key])) | |
return true; | |
return $this->query( | |
"SELECT COUNT(key) FROM store WHERE key = :key;", | |
[ "key" => $key ] | |
)->fetchColumn() > 0; | |
} | |
/** | |
* Gets a value from the store. | |
* @param string $key The key value is stored under. | |
* @return mixed The stored value. | |
*/ | |
public function get(string $key) { | |
// If it's not in the cache, insert it | |
if(!isset($this->cache[$key])) { | |
$this->cache[$key] = [ "modified" => false, "value" => json_decode($this->query( | |
"SELECT value FROM store WHERE key = :key;", | |
[ "key" => $key ] | |
)->fetchColumn()) ]; | |
} | |
return $this->cache[$key]["value"]; | |
} | |
/** | |
* Sets a value in the data store. | |
* Note that this does NOT save changes to disk until you close the connection! | |
* @param string $key The key to set the value of. | |
* @param mixed $value The value to store. | |
*/ | |
public function set(string $key, $value) : void { | |
if(!isset($this->cache[$key])) $this->cache[$key] = []; | |
$this->cache[$key]["value"] = $value; | |
$this->cache[$key]["modified"] = true; | |
} | |
/** | |
* Deletes an item from the data store. | |
* @param string $key The key of the item to delete. | |
* @return bool Whether it was really deleted or not. Note that if it doesn't exist, then it can't be deleted. | |
*/ | |
public function delete(string $key) : bool { | |
// Remove it from the cache | |
if(isset($this->cache[$key])) | |
unset($this->cache[$key]); | |
// Remove it from disk | |
return $this->query( | |
"DELETE FROM store WHERE key = :key;", | |
[ "key" => $key ] | |
)->rowCount() > 0; | |
} | |
/** | |
* Empties the store. | |
*/ | |
public function clear() : void { | |
// Empty the cache; | |
$this->cache = []; | |
// Empty the disk | |
$this->query("DELETE FROM store;"); | |
} | |
/** | |
* Syncs changes to disk and closes the PDO connection. | |
*/ | |
public function close() : void { | |
$this->db->beginTransaction(); | |
foreach($this->cache as $key => $value_data) { | |
// If it wasn't modified, there's no point in saving it, is there? | |
if(!$value_data["modified"]) | |
continue; | |
$this->query( | |
"INSERT OR REPLACE INTO store(key, value) VALUES(:key, :value)", | |
[ | |
"key" => $key, | |
"value" => json_encode($value_data["value"]) | |
] | |
); | |
} | |
$this->db->commit(); | |
$this->db = null; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment