0, 'window_start' => $now ]; } $rateData = &$_SESSION[$key]; // Check if window has expired if ($now - $rateData['window_start'] >= self::WINDOW_SECONDS) { // Reset for new window $rateData['count'] = 0; $rateData['window_start'] = $now; } // Increment request count $rateData['count']++; // Check if over limit if ($rateData['count'] > $limit) { return false; } return true; } /** * Apply rate limiting and send error response if exceeded * * @param string $type 'default' or 'api' */ public static function apply($type = 'default') { if (!self::check($type)) { http_response_code(429); header('Content-Type: application/json'); header('Retry-After: ' . self::WINDOW_SECONDS); echo json_encode([ 'success' => false, 'error' => 'Rate limit exceeded. Please try again later.', 'retry_after' => self::WINDOW_SECONDS ]); exit; } } /** * Get current rate limit status * * @param string $type 'default' or 'api' * @return array Rate limit status */ public static function getStatus($type = 'default') { if (session_status() === PHP_SESSION_NONE) { session_start(); } $limit = $type === 'api' ? self::API_LIMIT : self::DEFAULT_LIMIT; $key = 'rate_limit_' . $type; $now = time(); if (!isset($_SESSION[$key])) { return [ 'limit' => $limit, 'remaining' => $limit, 'reset' => $now + self::WINDOW_SECONDS ]; } $rateData = $_SESSION[$key]; // Check if window has expired if ($now - $rateData['window_start'] >= self::WINDOW_SECONDS) { return [ 'limit' => $limit, 'remaining' => $limit, 'reset' => $now + self::WINDOW_SECONDS ]; } return [ 'limit' => $limit, 'remaining' => max(0, $limit - $rateData['count']), 'reset' => $rateData['window_start'] + self::WINDOW_SECONDS ]; } /** * Add rate limit headers to response * * @param string $type 'default' or 'api' */ public static function addHeaders($type = 'default') { $status = self::getStatus($type); header('X-RateLimit-Limit: ' . $status['limit']); header('X-RateLimit-Remaining: ' . $status['remaining']); header('X-RateLimit-Reset: ' . $status['reset']); } }