This will avoid the awkward recursive query for each post by fetching cached post settings if available, and caching them nicely.
278 lines
6.6 KiB
PHP
278 lines
6.6 KiB
PHP
<?php
|
|
|
|
class MySQLAdapter {
|
|
public $raw;
|
|
|
|
function __construct() {
|
|
$db_params = json_decode(file_get_contents('secrets/db.json'), true);
|
|
|
|
try {
|
|
if(false !== getenv('MYSQL_HOST')) {
|
|
$this->raw = mysqli_connect(getenv('MYSQL_HOST'),
|
|
getenv('MYSQL_USER'), getenv('MYSQL_PASSWORD'),
|
|
getenv('MYSQL_DATABASE'),
|
|
getenv('MYSQL_PORT'));
|
|
}
|
|
else {
|
|
$this->raw = mysqli_connect($db_params['MYSQL_HOST'],
|
|
$db_params['MYSQL_USER'], $db_params['MYSQL_PASSWORD'],
|
|
$db_params['MYSQL_DATABASE'],
|
|
$db_params['MYSQL_PORT']);
|
|
}
|
|
} catch (\Throwable $th) {
|
|
echo 'Connection failed<br>';
|
|
echo 'Error number: ' . mysqli_connect_errno() . '<br>';
|
|
echo 'Error message: ' . mysqli_connect_error() . '<br>';
|
|
die();
|
|
|
|
//throw $th;
|
|
}
|
|
}
|
|
|
|
function _sanitize_path($post_path) {
|
|
$post_path = chop($post_path, '/');
|
|
|
|
if($post_path == "") {
|
|
return "";
|
|
}
|
|
|
|
if(!preg_match('/^(?:\/[\w-]+)+(?:\.[\w-]+)*$/', $post_path)) {
|
|
echo "Post path match against " . $post_path . " failed!";
|
|
die();
|
|
}
|
|
|
|
return $post_path;
|
|
}
|
|
|
|
function _exec($qery, $argtypes, ...$args) {
|
|
$stmt = $this->raw->prepare($qery);
|
|
$stmt->bind_param($argtypes, ...$args);
|
|
$stmt->execute();
|
|
|
|
return $stmt->get_result();
|
|
}
|
|
|
|
function _normalize_post_data($post_data) {
|
|
if($post_data == null) {
|
|
return [
|
|
"found" => false
|
|
];
|
|
}
|
|
|
|
$post_data["found"] = true;
|
|
|
|
$post_data['post_metadata'] = json_decode($post_data["post_metadata"], true) ?? [];
|
|
$post_data["post_content"] ??= '';
|
|
|
|
return $post_data;
|
|
}
|
|
|
|
function bump_post($post_path, $post_metadata = [], $create_dirs = true) {
|
|
$post_path = $this->_sanitize_path($post_path);
|
|
$path_depth = substr_count($post_path, "/");
|
|
|
|
if($create_dirs) {
|
|
$this->make_post_directory(dirname($post_path));
|
|
}
|
|
|
|
$qry = "
|
|
INSERT INTO posts
|
|
(post_path, post_path_depth, post_metadata, post_content)
|
|
VALUES
|
|
( ?, ?, ?, ?) AS new
|
|
ON DUPLICATE KEY UPDATE post_path=new.post_path;";
|
|
|
|
$this->_exec($qry, "siss",
|
|
$post_path,
|
|
$path_depth,
|
|
json_encode($post_metadata),
|
|
'');
|
|
}
|
|
|
|
function make_post_directory($directory) {
|
|
$json_metadata = ["type" => 'directory'];
|
|
|
|
while(strlen($directory) > 1) {
|
|
try {
|
|
$this->bump_post($directory, $json_metadata, false);
|
|
}
|
|
catch(Exception $e) {
|
|
}
|
|
|
|
$directory = dirname($directory);
|
|
}
|
|
}
|
|
|
|
function log_post_access($post_path, $agent, $time) {
|
|
$qry = "
|
|
INSERT INTO path_access_counts
|
|
(post_path, agent,
|
|
path_access_count,
|
|
path_processing_time)
|
|
VALUES ( ?, ?, 1, ? ) AS new
|
|
ON DUPLICATE KEY
|
|
UPDATE path_access_count=path_access_counts.path_access_count+1,
|
|
path_processing_time=path_access_counts.path_processing_time+new.path_processing_time;
|
|
";
|
|
|
|
$this->_exec($qry, "ssd", $post_path, $agent, $time);
|
|
}
|
|
|
|
function get_post_access_counters() {
|
|
$qry = "
|
|
SELECT post_path, agent, path_access_count, path_processing_time
|
|
FROM path_access_counts
|
|
WHERE path_last_access_time > ( CURRENT_TIMESTAMP - INTERVAL 10 MINUTE );
|
|
";
|
|
|
|
$data = $this->_exec($qry, "")->fetch_all(MYSQLI_ASSOC);
|
|
|
|
$out_data = [];
|
|
|
|
foreach($data AS $post_data) {
|
|
$path = $post_data['post_path'];
|
|
|
|
$agent_data = ($out_data[$path] ?? []);
|
|
|
|
$agent_data[$post_data['agent']] = [
|
|
'count' => $post_data['path_access_count'],
|
|
'time' => round($post_data['path_processing_time'], 6)
|
|
];
|
|
|
|
$out_data[$path] = $agent_data;
|
|
}
|
|
|
|
return $out_data;
|
|
}
|
|
|
|
function reset_post_settings_cache($post_path) {
|
|
$post_path = $this->_sanitize_path($post_path);
|
|
|
|
$this->_exec("
|
|
UPDATE posts
|
|
SET post_settings_cache=NULL
|
|
WHERE post_path LIKE ?;
|
|
", "s", $post_path . "%");
|
|
}
|
|
|
|
function update_or_create_post($post_path, $post_metadata, $post_content) {
|
|
$post_path = $this->_sanitize_path($post_path);
|
|
$path_depth = substr_count($post_path, "/");
|
|
|
|
$this->make_post_directory(dirname($post_path));
|
|
|
|
$this->reset_post_settings_cache($post_path);
|
|
|
|
$qry = "
|
|
INSERT INTO posts
|
|
(post_path, post_path_depth, post_metadata, post_content)
|
|
VALUES
|
|
( ?, ?, ?, ?) AS new
|
|
ON DUPLICATE KEY
|
|
UPDATE post_metadata=new.post_metadata,
|
|
post_content=new.post_content,
|
|
post_update_time=CURRENT_TIMESTAMP;";
|
|
|
|
$this->_exec($qry, "siss",
|
|
$post_path,
|
|
$path_depth,
|
|
json_encode($post_metadata),
|
|
$post_content);
|
|
}
|
|
|
|
function get_settings_for_path($post_path) {
|
|
$post_path = $this->_sanitize_path($post_path);
|
|
|
|
$post_settings = $this->_exec("
|
|
SELECT post_path, post_settings_cache
|
|
FROM posts
|
|
WHERE post_path = ?
|
|
", "s", $post_path)->fetch_assoc();
|
|
|
|
if(!isset($post_settings)) {
|
|
return [];
|
|
}
|
|
if(isset($post_settings['post_settings_cache'])) {
|
|
return json_decode($post_settings['post_settings_cache'], true);
|
|
}
|
|
|
|
$parent_settings = [];
|
|
if($post_path != "") {
|
|
$parent_settings = $this->get_settings_for_path(dirname($post_path));
|
|
}
|
|
|
|
$post_settings = [];
|
|
$post_metadata = $this->_exec("
|
|
SELECT post_path, post_metadata
|
|
FROM posts
|
|
WHERE post_path = ?
|
|
", "s", $post_path)->fetch_assoc();
|
|
|
|
if(isset($post_metadata['post_metadata'])) {
|
|
$post_metadata = json_decode($post_metadata['post_metadata'], true);
|
|
|
|
if(isset($post_metadata['settings'])) {
|
|
$post_settings = $post_metadata['settings'];
|
|
}
|
|
}
|
|
|
|
$post_settings = array_merge($parent_settings, $post_settings);
|
|
|
|
$this->_exec("UPDATE posts SET post_settings_cache=? WHERE post_path=?", "ss",
|
|
json_encode($post_settings), $post_path);
|
|
|
|
return $post_settings;
|
|
}
|
|
|
|
function get_post_by_path($post_path,
|
|
$with_subposts = false, $with_settings = true) {
|
|
|
|
$qry = "SELECT * FROM posts WHERE post_path = ?";
|
|
$post_path = $this->_sanitize_path($post_path);
|
|
|
|
$post_data = $this->_exec($qry, "s", $post_path)->fetch_assoc();
|
|
$post_data = $this->_normalize_post_data($post_data);
|
|
|
|
$post_data['post_path'] = $post_path;
|
|
|
|
if(!$post_data['found']) {
|
|
return $post_data;
|
|
}
|
|
|
|
if($with_subposts) {
|
|
$post_data['subposts'] = $this->get_subposts_by_path($post_path);
|
|
}
|
|
if($with_settings) {
|
|
$post_data['settings'] = $this->get_settings_for_path($post_path);
|
|
}
|
|
|
|
return $post_data;
|
|
}
|
|
|
|
function get_subposts_by_path($path) {
|
|
global $sql;
|
|
|
|
$path = $this->_sanitize_path($path);
|
|
|
|
$path_depth = substr_count($path, "/");
|
|
|
|
$qry = "SELECT post_path, post_metadata, post_update_time
|
|
FROM posts
|
|
WHERE (post_path LIKE CONCAT(?,'/%'))
|
|
AND post_path_depth = ?
|
|
ORDER BY post_create_time DESC
|
|
LIMIT 10";
|
|
|
|
$post_data = $this->_exec($qry, "si", $path, $path_depth+1)->fetch_all(MYSQLI_ASSOC);
|
|
|
|
$fn = function($data) {
|
|
return $this->_normalize_post_data($data);
|
|
};
|
|
|
|
$post_data = array_map($fn, $post_data);
|
|
|
|
return $post_data;
|
|
}
|
|
}
|
|
|
|
?>
|