prepare(" SELECT COUNT(*) AS c FROM ac_rate_limits WHERE action_name = :action_name AND subject_key = :subject_key AND created_at >= :cutoff "); $countStmt->execute([ ':action_name' => $action, ':subject_key' => $subjectKey, ':cutoff' => $cutoff, ]); $count = (int)(($countStmt->fetch(PDO::FETCH_ASSOC)['c'] ?? 0)); if ($count >= $limit) { return true; } $insertStmt = $db->prepare(" INSERT INTO ac_rate_limits (action_name, subject_key, created_at) VALUES (:action_name, :subject_key, NOW()) "); $insertStmt->execute([ ':action_name' => $action, ':subject_key' => $subjectKey, ]); $db->exec("DELETE FROM ac_rate_limits WHERE created_at < (NOW() - INTERVAL 2 DAY)"); } catch (Throwable $e) { return false; } return false; } private static function ensureTable(PDO $db): void { if (self::$tableEnsured) { return; } $db->exec(" CREATE TABLE IF NOT EXISTS ac_rate_limits ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, action_name VARCHAR(80) NOT NULL, subject_key VARCHAR(191) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_rate_limits_lookup (action_name, subject_key, created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci "); self::$tableEnsured = true; } }