<?php $config = array(); define('VERSION', '0.0.1'); define('GIT_REPO', 'https://git.aleteoryx.me/cgit/visitors_dot_php/'); /* CONFIG OPTIONS - COSMETIC */ // Custom CSS: ?string // // Will be injected after the builtin CSS. Respects `use_path_info` when being served. //$config['custom_css'] = 'body { background: red; }'; // Use builtin CSS: bool // // Toggles the builtin CSS $config['builtin_css'] = true; // Form mode: choice // // 0 - Only ask for name // 1 - Ask for name and website // 2 - Ask for name, website, and email // 3 - Ask for name and (website or email) // // Any entries in one mode will display fine in any other. $config['form_mode'] = 0; // E-Mail display mode: choice // // 0 - Show an icon with a `mailto:` link // 1 - Show an icon with a `mailto:` link, or use the username text as the link if no website is present // 2 - Show the text `[e-mail]` with a `mailto:` link // 3 - Show the text `[e-mail]` with a `mailto:` link, or use the username text for the link if no website is present // 4 - Show the e-mail as escaped text, e.g. 'alyx at aleteoryx dot me' $config['email_display'] = 0; // E-Mail icon: ?string // // Should be the link to an icon for email display modes 0 and 1. Supports base64. If null or empty, defaults to a builtin image. //$config['email_icon'] = '/static/my_cool_email_icon.gif'; /* CONFIG OPTIONS - BACKEND */ // Send assets via PATH_INFO: bool // // If true, assets like CSS, GIFs, etc, will be served from URLs like `/visitors.php/foo.css`. // Otherwise, inline them into a single HTML document. // Only enable this if your webserver supports PATH_INFO. $config['use_path_info'] = false; // Database path: string // // The path to the database file, in one of 3 formats. // // *.json - use a JSON array // *.jsonl - use newline-delimited JSON objects // *.csv - use CSV // *.db, *.sqlite, *.sqlite3 - use Sqlite3 $config['db'] = 'visitors.csv'; /* --- END OF CONFIG, CODE BELOW, PROBABLY DON'T EDIT PAST THIS POINT --- */ // db_row: ['id' => int (only for list_rows), 'name' => string, 'message' => string, 'email' => ?string, 'website' => ?string, 'timestamp' => string (or DateTimeInterface for list_rows)] // message is pre-escaped at insert time. // timestamp is DateTimeInterface::ISO8601_EXPANDED format. abstract class Database { public function append_row(string $name, string $message, ?string $email = NULL, ?string $website = NULL) { $timestamp = date(DATE_ISO8601_EXPANDED); $this->_append_row(compact('name', 'message', 'email', 'website', 'timestamp')); } public function list_rows(): array { $data = $this->_list_rows(); foreach($data as &$row) { $row['timestamp'] = DateTime::createFromFormat(DATE_ISO8601_EXPANDED, $row['timestamp']); } return $data; } public function delete_row(int $id): bool { return $this->_delete_row($id); } abstract protected function _append_row(array $db_row); abstract protected function _list_rows(): array; abstract protected function _delete_row(int $id): bool; } // notes: `id` is not stable final class JsonDatabase extends Database { private string $file; private array $data; public function __construct(string $file) { if (file_exists($file)) $this->data = json_decode(file_get_contents($file), associative: true); else $this->data = array(); $this->file = $file; } private function save() { file_put_contents($this->file, json_encode($this->data)); } protected function _append_row(array $db_row) { array_unshift($this->data, $db_row); $this->save(); } protected function _list_rows(): array { $data = $this->data; foreach($data as $id => &$row) { $row['id'] = $id; } return $data; } protected function _delete_row(int $id): bool { if (!isset($this->data[$id])) return false; unset($this->data[$id]); $this->data = array_values($this->data); $this->save(); return true; } }