dir)) { mkdir($this->dir, 0700, true); } $file = $this->dir . '/' . preg_replace('/[^A-Za-z0-9._-]/', '_', $bucket) . '.json'; $handle = fopen($file, 'c+'); if ($handle === false) { throw new RuntimeException('Cannot open rate limit bucket'); } if (!flock($handle, LOCK_EX)) { fclose($handle); throw new RuntimeException('Cannot lock rate limit bucket'); } $now = time(); $events = []; $contents = stream_get_contents($handle); $decoded = json_decode(is_string($contents) ? $contents : '', true); if (is_array($decoded)) { foreach ($decoded as $event) { if (is_int($event) && $event > $now - $windowSeconds) { $events[] = $event; } } } if (count($events) >= $limit) { flock($handle, LOCK_UN); fclose($handle); throw new RuntimeException('Rate limit exceeded'); } $events[] = $now; ftruncate($handle, 0); rewind($handle); fwrite($handle, json_encode($events) ?: '[]'); fflush($handle); flock($handle, LOCK_UN); fclose($handle); @chmod($file, 0600); } }