genai/rate-limit
Composer 安装命令:
composer require genai/rate-limit
包简介
Fixed-window rate limiting. A RateLimiter over a pluggable store (file-backed by default), plus a RateLimitInterceptor that returns 429 when an IP exceeds the limit on a path (brute-force / flood protection). PHP 5.3-safe.
README 文档
README
Fixed-window rate limiting for the GenAI stack — a small RateLimiter over a
pluggable store, plus a drop-in RateLimitInterceptor that returns 429 when a
client IP exceeds the limit on a path. Brute-force and flood protection without a
line of glue in your controllers. PHP 5.3.29-safe at runtime.
How it counts
A fixed window: time is sliced into window-second buckets; each (key, bucket)
pair has a counter. The interceptor keys on "<ip>|<path>", so every endpoint is
throttled independently — POST /login floods don't burn POST /forgot's budget.
Only state-changing methods (POST/PUT/PATCH/DELETE) are counted, so normal page
browsing (GET) is never throttled.
Use it
-
Configure (
app.ini) — optional; defaults are 20 requests / 60s:[ratelimit] limit = 20 window = 60 path = cache/ratelimit
-
Expose the limiter as a bean:
#[Configuration] class RateLimitConfig { #[Bean(RateLimiter::class)] public function rateLimiter(RateLimitProperty $cfg) { return RateLimitFactory::build($cfg); } }
-
Enable the interceptor with a thin subclass (it stays opt-in, like CsrfInterceptor):
#[Intercept] // all requests; only POST-likes are counted class Throttle extends \GenAI\RateLimit\Interceptor\RateLimitInterceptor {}
Scope it to specific paths with
#[Intercept(path: '/login')], or run several subclasses for different endpoints.
Account lockout (failed logins)
RateLimiter is fixed-window flood control. For "lock the account after N wrong
passwords," use AttemptLimiter — it counts failures per key (e.g. an email) and
locks for a fixed duration once the threshold is hit; a success clears it.
#[Bean(AttemptLimiter::class)] public function loginLockout(RateLimitProperty $cfg) { return RateLimitFactory::lockout($cfg); // [ratelimit] login_max_fails / login_lock }
if (($wait = $lockout->lockedFor($email)) > 0) { return "locked — try again in ~" . ceil($wait / 60) . " min"; } if ($passwordOk) { $lockout->clear($email); } // reset on success else { $lockout->fail($email); } // count the failure
lockedFor() returns seconds remaining (0 = open); remaining() gives attempts
left before a lock, for "N tries left" messaging.
Tradeoff: locking by email lets an attacker lock a victim out on purpose by failing 3× against their address. That's inherent to account lockout. If that matters, key on
email|ipinstead — at the cost of letting IP-rotation retry.
Notes
- Client IP comes from
REMOTE_ADDR(not client-spoofable). Behind a trusted reverse proxy, overrideclientIp()to read a vetted forwarded header — never trustX-Forwarded-Forblindly. - FileStore is single-server. Counters are per-
(key, window)files swept opportunistically. For a cluster, implementRateStoreagainst a shared backend (DB / Redis / APCu) and pass it toRateLimiterdirectly. - Fail-open: if the store can't be read/written, requests are allowed — a broken counter never locks users out.
Layers (use any without the others)
RateLimiter+RateStore/FileStore— standalone; no web stack required.RateLimitInterceptor— needsgenai/web(theInterceptorcontract) andgenai/http(the 429Response); both aresuggest, loaded only if you use it.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: Apache-2.0
- 更新时间: 2026-07-10