2, 'per_minute' => 30, 'per_hour' => 1000 ]; protected string $key; public function __construct(string $key, array $limits = []) { $this->key = sanitize_title($key); //Allow custom limits foreach ($limits as $interval => $limit) { $key = match($interval) { 'second', 'per_second','s' => 'per_second', 'minute', 'per_minute', 'm' => 'per_minute', 'hour', 'per_hour', 'h' => 'per_hour', default => false }; if (!$key) { error_log('Attempted to add custom limit: '.$interval.' for key '.$key); continue; } $this->limits[$key] = (int)$limit; } $this->getHistory(); add_action('shutdown', [$this, 'saveHistory']); } protected function getHistory():void { $stored = get_transient(BASE.$this->key.'_limits'); if (!$stored) { $stored = []; } $this->history = $stored; } public function recordRequest(string $endpoint = 'base'):void { if (!array_key_exists($endpoint, $this->history)) { $this->history[$endpoint] = []; } $this->history[$endpoint][] = time(); $this->checkHistorySize(); } public function saveHistory():void { set_transient(BASE.$this->key.'_limits', $this->history); } protected function checkHistorySize():void { foreach ($this->history as $endpoint => $history) { if (count($history) > $this->maxHistory) { $this->history[$endpoint] = array_slice($this->history[$endpoint], -$this->maxHistory); } } } public function checkRateLimit(string $endpoint = 'base'):bool { if (!array_key_exists($endpoint, $this->history)) { return true; } $now = time(); $this->cleanHistory($now); $counts = [ 'per_second' => 0, 'per_minute' => 0, 'per_hour' => count($this->history) ]; foreach ($this->history[$endpoint]??[] as $timestamp) { if ($now - $timestamp <=1) $counts['per_second']++; if ($now - $timestamp <=60) $counts['per_minute']++; } foreach ($this->limits as $interval => $limit) { if ($counts[$interval] >= $limit) return false; } return true; } protected function cleanHistory($now):void { $this->history = array_map(function ($endpointHistory) use ($now) { return array_filter($endpointHistory, function($timestamp) use ($now) { return ($now - $timestamp) < 3600; //Keep last hour }); }, $this->history); } }