get($key); if ($count && (int)$count >= $maxRequests) { header('Retry-After: ' . $timeWindow); json_error('Too Many Requests. Please slow down.', 429); } if (!$count) { $redis->setex($key, $timeWindow, 1); } else { $redis->incr($key); } return; // Success with Redis } catch (\Exception $e) { // Fallback to file-based if Redis fails } } // 2. Fallback: File-based rate limiter (original logic) $cacheDir = STORAGE_PATH . '/cache'; $cacheFile = $cacheDir . '/rl_' . md5($ip) . '.json'; if (!is_dir($cacheDir)) mkdir($cacheDir, 0755, true); $fp = fopen($cacheFile, 'c+'); if ($fp === false) return; try { flock($fp, LOCK_EX); $now = time(); $content = stream_get_contents($fp); $requests = []; if (!empty($content)) { $decoded = json_decode($content, true); if (is_array($decoded)) { $requests = array_values(array_filter($decoded, fn($ts) => $ts > ($now - $timeWindow))); } } if (count($requests) >= $maxRequests) { flock($fp, LOCK_UN); fclose($fp); header('Retry-After: ' . $timeWindow); json_error('Too Many Requests. Please slow down.', 429); } $requests[] = $now; ftruncate($fp, 0); rewind($fp); fwrite($fp, json_encode($requests)); } finally { flock($fp, LOCK_UN); fclose($fp); } } }