prepare( "SELECT attempted_at FROM login_attempts WHERE $column = ? AND attempted_at > ? ORDER BY attempted_at DESC LIMIT 1 OFFSET ?" ); $stmt->execute([$value, $since, $max - 1]); $nth = $stmt->fetchColumn(); if ($nth !== false) { $wait = max($wait, (int) $nth + LOGIN_WINDOW_SECONDS - time()); } } return $wait; } function record_login_failure(string $ip, string $username): void { $pdo = get_db(); $pdo->prepare("INSERT INTO login_attempts (ip, username, attempted_at) VALUES (?, ?, ?)") ->execute([$ip, $username, time()]); // Prune anything outside the window so the table stays small. $pdo->prepare("DELETE FROM login_attempts WHERE attempted_at <= ?") ->execute([time() - LOGIN_WINDOW_SECONDS]); } function clear_login_failures(string $ip, string $username): void { get_db()->prepare("DELETE FROM login_attempts WHERE ip = ? AND username = ?") ->execute([$ip, $username]); }