Skip to content

Commit 99c479a

Browse files
committed
强类型声明
1 parent 327f519 commit 99c479a

15 files changed

+125
-108
lines changed

composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
}
3030
}
3131
},
32-
"minimum-stability": "dev",
3332
"require-dev": {
3433
"phpunit/phpunit": "^9.5",
3534
"guzzlehttp/guzzle": "^7.7",

src/Throttle.php

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use think\middleware\throttle\ThrottleAbstract;
1414
use think\Request;
1515
use think\Response;
16+
use TypeError;
1617
use function sprintf;
1718

1819
/**
@@ -26,7 +27,7 @@ class Throttle
2627
* 默认配置参数
2728
* @var array
2829
*/
29-
public static $default_config = [
30+
public static array $default_config = [
3031
'prefix' => 'throttle_', // 缓存键前缀,防止键与其他应用冲突
3132
'key' => true, // 节流规则 true为自动规则
3233
'visit_method' => ['GET', 'HEAD'], // 要被限制的请求类型
@@ -38,7 +39,7 @@ class Throttle
3839
'driver_name' => CounterFixed::class, // 限流算法驱动
3940
];
4041

41-
public static $duration = [
42+
public static array $duration = [
4243
's' => 1,
4344
'm' => 60,
4445
'h' => 3600,
@@ -49,20 +50,19 @@ class Throttle
4950
* 缓存对象
5051
* @var CacheInterface
5152
*/
52-
protected $cache;
53+
protected CacheInterface $cache;
5354

5455
/**
5556
* 配置参数
5657
* @var array
5758
*/
58-
protected $config = [];
59+
protected array $config = [];
5960

60-
protected $key = null; // 解析后的标识
61-
protected $wait_seconds = 0; // 下次合法请求还有多少秒
62-
protected $now = 0; // 当前时间戳
63-
protected $max_requests = 0; // 规定时间内允许的最大请求次数
64-
protected $expire = 0; // 规定时间
65-
protected $remaining = 0; // 规定时间内还能请求的次数
61+
protected int $wait_seconds = 0; // 下次合法请求还有多少秒
62+
protected int $now = 0; // 当前时间戳
63+
protected int $max_requests = 0; // 规定时间内允许的最大请求次数
64+
protected int $expire = 0; // 规定时间
65+
protected int $remaining = 0; // 规定时间内还能请求的次数
6666

6767
/**
6868
* Throttle constructor.
@@ -93,17 +93,17 @@ protected function allowRequest(Request $request): bool
9393
}
9494
[$max_requests, $duration] = $this->parseRate($this->config['visit_rate']);
9595

96-
$micronow = microtime(true);
96+
$micro_now = microtime(true); // float
9797

9898
$driver = Container::getInstance()->invokeClass($this->config['driver_name']);
9999
if (!($driver instanceof ThrottleAbstract)) {
100-
throw new \TypeError('The throttle driver must extends ' . ThrottleAbstract::class);
100+
throw new TypeError('The throttle driver must extends ' . ThrottleAbstract::class);
101101
}
102-
$allow = $driver->allowRequest($key, $micronow, $max_requests, $duration, $this->cache);
102+
$allow = $driver->allowRequest($key, $micro_now, $max_requests, $duration, $this->cache);
103103

104104
if ($allow) {
105105
// 允许访问
106-
$this->now = (int) $micronow;
106+
$this->now = (int) $micro_now;
107107
$this->expire = $duration;
108108
$this->max_requests = $max_requests;
109109
$this->remaining = $max_requests - $driver->getCurRequests();
@@ -135,7 +135,11 @@ public function handle(Request $request, Closure $next, array $params=[]): Respo
135135
$response = $next($request);
136136
if (200 <= $response->getCode() && 300 > $response->getCode() && $this->config['visit_enable_show_rate_limit']) {
137137
// 将速率限制 headers 添加到响应中
138-
$response->header($this->getRateLimitHeaders());
138+
$response->header([
139+
'X-Rate-Limit-Limit' => $this->max_requests,
140+
'X-Rate-Limit-Remaining' => max($this->remaining, 0),
141+
'X-Rate-Limit-Reset' => $this->now + $this->expire,
142+
]);
139143
}
140144
return $response;
141145
}
@@ -149,7 +153,7 @@ protected function getCacheKey(Request $request): ?string
149153
{
150154
$key = $this->config['key'];
151155

152-
if ($key instanceof \Closure) {
156+
if ($key instanceof Closure) {
153157
$key = Container::getInstance()->invokeFunction($key, [$this, $request]);
154158
}
155159

@@ -160,7 +164,7 @@ protected function getCacheKey(Request $request): ?string
160164

161165
if ($key === true) {
162166
$key = $request->ip();
163-
} elseif (is_string($key) && false !== strpos($key, '__')) {
167+
} elseif (is_string($key) && str_contains($key, '__')) {
164168
$key = str_replace(['__CONTROLLER__', '__ACTION__', '__IP__'], [$request->controller(), $request->action(), $request->ip()], $key);
165169
}
166170

@@ -172,7 +176,7 @@ protected function getCacheKey(Request $request): ?string
172176
* @param string $rate
173177
* @return int[]
174178
*/
175-
protected function parseRate($rate): array
179+
protected function parseRate(string $rate): array
176180
{
177181
[$num, $period] = explode("/", $rate);
178182
$max_requests = (int) $num;
@@ -213,19 +217,6 @@ public function setDriverClass(string $class_name): self
213217
return $this;
214218
}
215219

216-
/**
217-
* 获取速率限制头
218-
* @return array
219-
*/
220-
public function getRateLimitHeaders(): array
221-
{
222-
return [
223-
'X-Rate-Limit-Limit' => $this->max_requests,
224-
'X-Rate-Limit-Remaining' => $this->remaining < 0 ? 0 : $this->remaining,
225-
'X-Rate-Limit-Reset' => $this->now + $this->expire,
226-
];
227-
}
228-
229220
/**
230221
* 构建 Response Exception
231222
* @param int $wait_seconds
@@ -234,10 +225,10 @@ public function getRateLimitHeaders(): array
234225
*/
235226
public function buildLimitException(int $wait_seconds, Request $request): HttpResponseException {
236227
$visitFail = $this->config['visit_fail_response'] ?? null;
237-
if ($visitFail instanceof \Closure) {
228+
if ($visitFail instanceof Closure) {
238229
$response = Container::getInstance()->invokeFunction($visitFail, [$this, $request, $wait_seconds]);
239230
if (!$response instanceof Response) {
240-
throw new \TypeError(sprintf('The closure must return %s instance', Response::class));
231+
throw new TypeError(sprintf('The closure must return %s instance', Response::class));
241232
}
242233
} else {
243234
$content = str_replace('__WAIT__', (string) $wait_seconds, $this->config['visit_fail_text']);

src/throttle/CounterFixed.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace think\middleware\throttle;
55

66
use Psr\SimpleCache\CacheInterface;
7+
use Psr\SimpleCache\InvalidArgumentException;
78

89
/**
910
* 计数器固定窗口算法
@@ -13,10 +14,13 @@
1314
class CounterFixed extends ThrottleAbstract
1415
{
1516

16-
public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool
17+
/**
18+
* @throws InvalidArgumentException
19+
*/
20+
public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool
1721
{
1822
$cur_requests = (int) $cache->get($key, 0);
19-
$now = (int) $micronow;
23+
$now = (int) $micro_now;
2024
$wait_reset_seconds = $duration - $now % $duration; // 距离下次重置还有n秒时间
2125
$this->wait_seconds = $wait_reset_seconds % $duration + 1;
2226
$this->cur_requests = $cur_requests;

src/throttle/CounterSlider.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,22 @@
44
namespace think\middleware\throttle;
55

66
use Psr\SimpleCache\CacheInterface;
7+
use Psr\SimpleCache\InvalidArgumentException;
78

89
/**
910
* 计数器滑动窗口算法
10-
* Class CouterSlider
11+
* Class CounterSlider
1112
* @package think\middleware\throttle
1213
*/
1314
class CounterSlider extends ThrottleAbstract
1415
{
15-
public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool
16+
/**
17+
* @throws InvalidArgumentException
18+
*/
19+
public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool
1620
{
1721
$history = $cache->get($key, []);
18-
$now = (int) $micronow;
22+
$now = (int) $micro_now;
1923
// 移除过期的请求的记录
2024
$history = array_values(array_filter($history, function ($val) use ($now, $duration) {
2125
return $val >= $now - $duration;
@@ -31,7 +35,7 @@ public function allowRequest(string $key, float $micronow, int $max_requests, in
3135

3236
if ($history) {
3337
$wait_seconds = $duration - ($now - $history[0]) + 1;
34-
$this->wait_seconds = $wait_seconds > 0 ? $wait_seconds : 0;
38+
$this->wait_seconds = max($wait_seconds, 0);
3539
}
3640

3741
return false;

src/throttle/LeakyBucket.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace think\middleware\throttle;
55

66
use Psr\SimpleCache\CacheInterface;
7+
use Psr\SimpleCache\InvalidArgumentException;
78

89
/**
910
* 漏桶算法
@@ -13,19 +14,22 @@
1314
class LeakyBucket extends ThrottleAbstract
1415
{
1516

16-
public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool
17+
/**
18+
* @throws InvalidArgumentException
19+
*/
20+
public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool
1721
{
1822
if ($max_requests <= 0) return false;
1923

2024
$last_time = (float) $cache->get($key, 0); // 最近一次请求
2125
$rate = (float) $duration / $max_requests; // 平均 n 秒一个请求
22-
if ($micronow - $last_time < $rate) {
26+
if ($micro_now - $last_time < $rate) {
2327
$this->cur_requests = 1;
24-
$this->wait_seconds = ceil($rate - ($micronow - $last_time));
28+
$this->wait_seconds = (int) ceil($rate - ($micro_now - $last_time));
2529
return false;
2630
}
2731

28-
$cache->set($key, $micronow, $duration);
32+
$cache->set($key, $micro_now, $duration);
2933
return true;
3034
}
3135
}

src/throttle/ThrottleAbstract.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,28 @@
99
abstract class ThrottleAbstract
1010
{
1111
/** @var int */
12-
protected $cur_requests = 0; // 当前已有的请求数
12+
protected int $cur_requests = 0; // 当前已有的请求数
1313
/** @var int */
14-
protected $wait_seconds = 0; // 距离下次合法请求还有多少秒
14+
protected int $wait_seconds = 0; // 距离下次合法请求还有多少秒
1515

1616
/**
1717
* 是否允许访问
1818
* @param string $key 缓存键
19-
* @param float $micronow 当前时间戳,可含毫秒
19+
* @param float $micro_now 当前时间戳,可含毫秒
2020
* @param int $max_requests 允许最大请求数
2121
* @param int $duration 限流时长
2222
* @param CacheInterface $cache 缓存对象
2323
* @return bool
2424
*/
25-
abstract public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool;
25+
abstract public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool;
2626

2727
/**
2828
* 计算距离下次合法请求还有多少秒
2929
* @return int
3030
*/
3131
public function getWaitSeconds(): int
3232
{
33-
return (int) $this->wait_seconds;
33+
return $this->wait_seconds;
3434
}
3535

3636
/**
@@ -39,7 +39,7 @@ public function getWaitSeconds(): int
3939
*/
4040
public function getCurRequests(): int
4141
{
42-
return (int) $this->cur_requests;
42+
return $this->cur_requests;
4343
}
4444

4545
}

src/throttle/TokenBucket.php

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
namespace think\middleware\throttle;
55

66
use Psr\SimpleCache\CacheInterface;
7+
use Psr\SimpleCache\InvalidArgumentException;
78

89
/**
910
* 令牌桶算法
@@ -12,32 +13,35 @@
1213
*/
1314
class TokenBucket extends ThrottleAbstract
1415
{
15-
public function allowRequest(string $key, float $micronow, int $max_requests, int $duration, CacheInterface $cache): bool
16+
/**
17+
* @throws InvalidArgumentException
18+
*/
19+
public function allowRequest(string $key, float $micro_now, int $max_requests, int $duration, CacheInterface $cache): bool
1620
{
1721
if ($max_requests <= 0 || $duration <= 0) return false;
1822

1923
$assist_key = $key . 'store_num'; // 辅助缓存
2024
$rate = (float) $max_requests / $duration; // 平均一秒生成 n 个 token
2125

22-
$last_time = $cache->get($key, null);
23-
$store_num = $cache->get($assist_key, null);
26+
$last_time = $cache->get($key, 0);
27+
$store_num = $cache->get($assist_key, 0);
2428

25-
if ($last_time === null || $store_num === null) { // 首次访问
26-
$cache->set($key, $micronow, $duration);
29+
if ($last_time === 0 || $store_num === 0) { // 首次访问
30+
$cache->set($key, $micro_now, $duration);
2731
$cache->set($assist_key, $max_requests - 1, $duration);
2832
return true;
2933
}
3034

31-
$create_num = floor(($micronow - $last_time) * $rate); // 推算生成的 token 数
35+
$create_num = floor(($micro_now - $last_time) * $rate); // 推算生成的 token 数
3236
$token_left = (int) min($max_requests, $store_num + $create_num); //当前剩余 tokens 数量
3337

3438
if ($token_left < 1) {
3539
$tmp = (int) ceil($duration / $max_requests);
36-
$this->wait_seconds = $tmp - intval(($micronow - $last_time)) % $tmp;
40+
$this->wait_seconds = $tmp - intval(($micro_now - $last_time)) % $tmp;
3741
return false;
3842
}
3943
$this->cur_requests = $max_requests - $token_left;
40-
$cache->set($key, $micronow, $duration);
44+
$cache->set($key, $micro_now, $duration);
4145
$cache->set($assist_key, $token_left - 1, $duration);
4246
return true;
4347
}

0 commit comments

Comments
 (0)