diff --git a/src/Abuse/Adapters/TimeLimit/Redis.php b/src/Abuse/Adapters/TimeLimit/Redis.php index 80a5572..d4b27e4 100644 --- a/src/Abuse/Adapters/TimeLimit/Redis.php +++ b/src/Abuse/Adapters/TimeLimit/Redis.php @@ -12,11 +12,17 @@ class Redis extends TimeLimit * @var \Redis */ protected \Redis $redis; + + /** + * @var int + */ + protected int $ttl; public function __construct(string $key, int $limit, int $seconds, \Redis $redis) { $this->redis = $redis; $this->key = $key; + $this->ttl = $seconds; $now = \time(); $this->timestamp = (int)($now - ($now % $seconds)); $this->limit = $limit; @@ -62,16 +68,13 @@ protected function hit(string $key, int $timestamp): void return; } - /** @var string $count */ - $count = $this->redis->get(self::NAMESPACE . '__'. $key .'__'. $timestamp); - if (!$count) { - $this->count = 0; - } else { - $this->count = intval($count); - } - - $this->redis->incr(self::NAMESPACE . '__'. $key .'__'. $timestamp); - $this->count++; + $key = self::NAMESPACE . '__' . $key . '__' . $timestamp; + $this->redis->multi() + ->incr($key) + ->expire($key, $this->ttl) + ->exec(); + + $this->count = ($this->count ?? 0) + 1; } /** @@ -85,18 +88,19 @@ protected function hit(string $key, int $timestamp): void */ public function getLogs(?int $offset = null, ?int $limit = 25): array { - // TODO limit potential is SCAN but needs cursor no offset - $cursor = null; - $keys = $this->redis->scan($cursor, self::NAMESPACE . '__*', $limit); - if (!$keys) { - return []; - } + $cursor = 0; + $matches = []; + $pattern = self::NAMESPACE . '__*'; + + do { + [$cursor, $keys] = $this->redis->scan($cursor, $pattern, $limit); + if (!empty($keys)) { + $values = $this->redis->mget($keys); + $matches = array_merge($matches, array_combine($keys, $values)); + } + } while ($cursor > 0 && (!$limit || count($matches) < $limit)); - $logs = []; - foreach ($keys as $key) { - $logs[$key] = $this->redis->get($key); - } - return $logs; + return $matches; } /** @@ -107,32 +111,7 @@ public function getLogs(?int $offset = null, ?int $limit = 25): array */ public function cleanup(int $timestamp): bool { - $iterator = null; - while ($iterator !== 0) { - $keys = $this->redis->scan($iterator, self::NAMESPACE . '__*__*', 1000); - $keys = $this->filterKeys($keys ? $keys : [], $timestamp); - $this->redis->del($keys); - } + // No need for manual cleanup - Redis TTL handles this automatically return true; } - - /** - * Filter keys - * - * @param array $keys - * @param integer $timestamp - * @return array - */ - protected function filterKeys(array $keys, int $timestamp): array - { - $filteredKeys = []; - foreach ($keys as $key) { - $parts = explode('__', $key); - $keyTimestamp = (int)end($parts); // Assuming the last part is always the timestamp - if ($keyTimestamp < $timestamp) { - $filteredKeys[] = $key; - } - } - return $filteredKeys; - } }