enabled = (bool) ($app['rate_limit']['enabled'] ?? true); $this->storageDir = (string) ($app['storage']['rate_limit'] ?? Bootstrap::rootPath() . '/storage/rate_limit'); if ($this->enabled && !is_dir($this->storageDir)) { mkdir($this->storageDir, 0775, true); } } public function allow(string $key, int $limit, int $windowSeconds): bool { if (!$this->enabled) { return true; } $hash = hash('sha256', $key); $path = $this->storageDir . '/' . $hash . '.json'; $now = time(); $handle = fopen($path, 'c+'); if ($handle === false) { return true; } try { if (!flock($handle, LOCK_EX)) { return true; } $contents = stream_get_contents($handle); $timestamps = []; if (is_string($contents) && $contents !== '') { $decoded = json_decode($contents, true); if (is_array($decoded)) { $timestamps = array_values(array_filter($decoded, static fn ($ts): bool => is_int($ts))); } } $threshold = $now - $windowSeconds; $timestamps = array_values(array_filter($timestamps, static fn (int $ts): bool => $ts >= $threshold)); if (count($timestamps) >= $limit) { return false; } $timestamps[] = $now; ftruncate($handle, 0); rewind($handle); fwrite($handle, json_encode($timestamps)); return true; } finally { flock($handle, LOCK_UN); fclose($handle); } } }