Rate Limiter
webman rate limiter with annotation-based limiting support.
Supports apcu, redis, and memory drivers.
Source Repository
https://github.com/webman-php/limiter
Installation
composer require webman/limiter
Usage
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\annotation\Limit;
class UserController
{
#[Limit(limit: 10)]
public function index(): string
{
// Default is IP-based rate limiting, default time window is 1 second
return 'Maximum 10 requests per IP per second';
}
#[Limit(limit: 100, ttl: 60, key: Limit::UID)]
public function search(): string
{
// key: Limit::UID, rate limit by user ID, requires session('user.id') to be non-empty
return 'Maximum 100 searches per user per 60 seconds';
}
#[Limit(limit: 1, ttl: 60, key: Limit::SID, message: 'Only 1 email per person per minute')]
public function sendMail(): string
{
// key: Limit::SID, rate limit by session_id
return 'Email sent successfully';
}
#[Limit(limit: 100, ttl: 24*60*60, key: 'coupon', message: 'Today\'s coupons are sold out, please try again tomorrow')]
#[Limit(limit: 1, ttl: 24*60*60, key: Limit::UID, message: 'Each user can only claim one coupon per day')]
public function coupon(): string
{
// key: 'coupon', custom key for global rate limiting, max 100 coupons per day
// Also rate limit by user ID, each user can only claim one coupon per day
return 'Coupon sent successfully';
}
#[Limit(limit: 5, ttl: 24*60*60, key: [UserController::class, 'getMobile'], message: 'Maximum 5 SMS per phone number per day')]
public function sendSms2(): string
{
// When key is a variable, use [class, static_method] to get the key, e.g. [UserController::class, 'getMobile'] calls UserController::getMobile() return value as key
return 'SMS sent successfully';
}
/**
* Custom key, get mobile number, must be a static method
* @return string
*/
public static function getMobile(): string
{
return request()->get('mobile');
}
#[Limit(limit: 1, ttl: 10, key: Limit::IP, message: 'Rate limited', exception: RuntimeException::class)]
public function testException(): string
{
// Default exception on limit exceeded is support\limiter\RateLimitException, can be changed via exception parameter
return 'ok';
}
}
Notes
- Uses fixed window algorithm
- Default ttl time window is 1 second
- Set time window via ttl, e.g.
ttl:60for 60 seconds - Default rate limit dimension is IP (default
127.0.0.1is not limited, see configuration section below) - Built-in IP, UID (requires
session('user.id')non-empty), and SID (bysession_id) rate limiting - When using nginx proxy, pass
X-Forwarded-Forheader for IP-based limiting, see nginx proxy - Triggers
support\limiter\RateLimitExceptionwhen limit exceeded, custom exception class viaexception:xx - Default error message on limit exceeded is
Too Many Requests, custom message viamessage:xx - Default error message can also be modified via translation, Linux reference:
composer require symfony/translation mkdir resource/translations/zh_CN/ -p echo "<?php return [ 'Too Many Requests' => '请求频率受限' ];" > resource/translations/zh_CN/messages.php php start.php restart
API
Sometimes developers want to call the rate limiter directly in code, see the following example:
<?php
namespace app\controller;
use RuntimeException;
use support\limiter\Limiter;
class UserController {
public function sendSms(string $mobile): string
{
// mobile is used as key here
Limiter::check($mobile, 5, 24*60*60, 'Maximum 5 SMS per phone number per day');
return 'SMS sent successfully';
}
}
Configuration
config/plugin/webman/limiter/app.php
<?php
use support\limiter\RateLimitException;
return [
'enable' => true,
'driver' => 'auto', // auto, apcu, memory, redis
'stores' => [
'redis' => [
'connection' => 'default',
]
],
// These IPs are not rate limited (only effective when key is Limit::IP)
'ip_whitelist' => [
'127.0.0.1',
],
'exception' => RateLimitException::class
];
- enable: Whether to enable rate limiting
- driver: One of
auto,apcu,memory,redis;autoautomatically selects betweenapcu(preferred) andmemory - stores: Redis configuration,
connectioncorresponds to the key inconfig/redis.php - ip_whitelist: Whitelisted IPs are not rate limited (only effective when key is
Limit::IP)
Driver Selection
memory
-
Introduction
No extensions required, best performance. -
Limitations
Rate limiting is effective only for the current process, data is not shared across processes, cluster rate limiting not supported. -
Use Cases
Windows development environment; business scenarios that do not require strict rate limiting; defending against CC attacks.
apcu
-
Extension Installation
Requires apcu extension, and php.ini settings:apc.enabled=1 apc.enable_cli=1To find php.ini location, run
php --ini -
Introduction
Excellent performance, supports multi-process shared rate limit data. -
Limitations
Cluster not supported -
Use Cases
Any development environment; single-server production rate limiting; cluster scenarios that do not require strict rate limiting; defending against CC attacks.
redis
-
Dependencies
Requires redis extension and Redis component, install with:composer require -W webman/redis illuminate/events -
Introduction
Lower performance than apcu, supports both single-server and cluster precise rate limiting -
Use Cases
Development environment; single-server production; cluster environment