Skip to content

Instantly share code, notes, and snippets.

@bmcminn
Created January 31, 2018 19:11
Show Gist options
  • Select an option

  • Save bmcminn/a63a72a4c1877cc5faa221302d833d52 to your computer and use it in GitHub Desktop.

Select an option

Save bmcminn/a63a72a4c1877cc5faa221302d833d52 to your computer and use it in GitHub Desktop.
A memoizer written in PHP that uses SQLite as it's persistence layer.
<?php
/**
* API Description
*
* # Memoize() utility
*
* ## Public Methods:
*
* getMemo($key)
* setMemo($key, $data)
*
*
* ## Basic workflow
*
* ```php
* 1. Initialize a new Memo DB instance
* $memo = new Memoizer([
* 'dbPath' => './cache', // default = './cache'
* 'cacheBuffer' => 5, // default = 5 (in minutes)
* ]');
*
* // NOTE: dbPath defines a path relative to the current working directory
*
* $router->get('/route/{user}/{name}'), function($user, $name) {
*
* // 2. Inside your function, splice a string together using your API route components
* $routeKey = "route_{$user}_{$name}";
*
* // 3. Use $memo->getMemo($key) to get the record in question
* $memo = $memo->getMemo($routeKey);
*
* if ($memo) {
* return $memo;
* }
*
* // 4. $memo->memoize() does an implicit check if the cache
* })
* ```
*
*/
class Memoizer {
private $pdo;
private $dbPath;
private $tableName;
private $cacheBuffer;
/**
* [__construct description]
* @param array $config Associative array for configuring each instance of Memoizer
* @propertu integer cacheBuffer Time in minutes for the cache to be valid
* @property string dbPath Location for where the SQLite DB should be kept
*/
function __construct(array $config = []) {
$config = array_replace_recursive([
'dbPath' => '../storage/framework/cache',
'cacheBuffer' => 5, // defaults to 5 minutes
], $config);
// ensure dbPath is rotated daily to prevent SQLite DB overloads
$this->dbPath = getcwd() . '/' . $config['dbPath'] . '/' . date('Y-m-d') . '.db';
$this->cacheBuffer = 60 * $config['cacheBuffer'];
$this->tableName = 'memos';
$this->_initDB();
$this->_createTables();
return $this;
}
/**
* [_initDB description]
* @return [type] [description]
*/
private function _initDB() {
if ($this->pdo == null) {
// try {
$this->pdo = new \PDO('sqlite:' . $this->dbPath);
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// } catch (\PDOException $e) {
// // TODO: handle error
// }
}
}
/**
* [_createTables description]
* @return [type] [description]
*/
private function _createTables() {
$cmds = [
"
CREATE TABLE IF NOT EXISTS $this->tableName (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key TEXT NOT NULL UNIQUE,
data TEXT NOT NULL,
cache INTEGER NOT NULL
)
"
];
foreach ($cmds as $cmd) {
$this->pdo->exec($cmd);
}
}
private function _bustCache() {
$params = $this->_getQueryParams();
// determine if we're busting the cache
return isset($params['resync']) ? true : false;
}
private function _getQueryParams() {
// @sauce: https://stackoverflow.com/a/10298907/3708807
$https = isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : 'off';
$protocol = ($https === 'on') ? 's://' : '://';
$url = 'http' . $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$parts = parse_url($url);
$params = [];
if (isset($parts['query'])) {
parse_str($parts['query'], $params);
}
return $params;
}
/**
* [_getCurrentTime description]
* @return [type] [description]
*/
private function _getCurrentTime() {
return floor(microtime(true));
}
/**
* [getMemo description]
* @param string $key [description]
* @return [type] [description]
*/
public function getMemo(string $key) {
$sql = "
SELECT *
FROM memos
WHERE key = :key
LIMIT 1
";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
':key' => $key
]);
// fetch the memo record from the database
$memo = $stmt->fetch(\PDO::FETCH_ASSOC);
// if we're busting the cache
if ($this->_bustCache()) {
return false;
}
// if cache is stale
if ($memo['cache'] + $this->cacheBuffer < $this->_getCurrentTime()) {
return false;
}
return $memo;
}
/**
* [setMemo description]
* @param string $key [description]
* @param [type] $data [description]
*/
public function setMemo(string $key, $data) {
$data = json_encode($data, true);
$sql = "
INSERT OR REPLACE INTO {$this->tableName} (id, key, cache, data)
VALUES (
coalesce((SELECT id FROM {$this->tableName} WHERE key=:key), null)
, :key
, round(:cache)
, :data
)
";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([
':key' => $key
, ':cache' => $this->_getCurrentTime()
, ':data' => $data
]);
return $this->getMemo($key);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment