<?php
 namespace ModStart\Core\RateLimit; use ModStart\Core\Util\RedisUtil; class RedisRateLimiter extends AbstractRateLimiter { public function hit($key, $limit, $window) { $lua = <<<EOT
local key = KEYS[1]
local ttl = ARGV[1]
local tmp_key = string.format('rrt:%s', key)
local r0 = redis.call('setex', tmp_key, ttl, 0)
local r1 = redis.call('renamenx', tmp_key, key)
local r2 = redis.call('incr', key)
local r3 = redis.call('pttl', key)
return {r0, r1, r2, r3}
EOT;
$luaSha = sha1($lua); $client = RedisUtil::client(); $response = $client->script('EXISTS', $luaSha); if (0 === $response[0]) { $response = $client->script('LOAD', $lua); } $response = $client->evalsha($luaSha, 1, $key, $window); $current = (int)$response[2]; $currentTTLms = (int)$response[3]; if ($currentTTLms < 0) { $client->expire($runKey, $window); $currentTTLms = 0; } $over = ($current > $limit); $result = [ 'current' => $current, 'left' => max(0, $limit - $current), 'over' => $over, 'delay' => $over ? max(0, $currentTTLms) : 0, ]; return $result; } }