db = new PDO('sqlite:' . $dbFile); $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->db->exec('PRAGMA journal_mode = WAL'); $this->db->exec('PRAGMA busy_timeout = 5000'); $this->init(); } private function init() { $this->db->exec( 'CREATE TABLE IF NOT EXISTS user_settings ( user_id INTEGER NOT NULL, setting_key TEXT NOT NULL, setting_value TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, PRIMARY KEY(user_id, setting_key) )' ); } public function get($userId, $key, $default = null) { $stmt = $this->db->prepare( 'SELECT setting_value FROM user_settings WHERE user_id = :user_id AND setting_key = :setting_key' ); $stmt->execute(array( ':user_id' => $this->safeUserId($userId), ':setting_key' => $this->safeKey($key), )); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row ? (string)$row['setting_value'] : $default; } public function set($userId, $key, $value) { $now = time(); $stmt = $this->db->prepare( 'INSERT INTO user_settings (user_id, setting_key, setting_value, created_at, updated_at) VALUES (:user_id, :setting_key, :setting_value, :created_at, :updated_at) ON CONFLICT(user_id, setting_key) DO UPDATE SET setting_value = excluded.setting_value, updated_at = excluded.updated_at' ); $stmt->execute(array( ':user_id' => $this->safeUserId($userId), ':setting_key' => $this->safeKey($key), ':setting_value' => (string)$value, ':created_at' => $now, ':updated_at' => $now, )); } private function safeUserId($userId) { $userId = (int)$userId; if ($userId <= 0) { throw new UserSettingsStoreException('Invalid user id.'); } return $userId; } private function safeKey($key) { $key = trim((string)$key); if (!preg_match('/^[a-z0-9_.-]{1,80}$/', $key)) { throw new UserSettingsStoreException('Invalid setting key.'); } return $key; } }