avanboxel/php-rate-limiter
Composer 安装命令:
composer require avanboxel/php-rate-limiter
包简介
A flexible rate limiting library for PHP applications with multiple algorithm implementations and persistent storage support
README 文档
README
A flexible rate limiting library for PHP applications with multiple algorithm implementations and persistent storage support.
Installation
composer require avanboxel/php-rate-limiter
Rate Limiting Algorithms
This library provides four different rate limiting algorithms, each with unique characteristics:
1. Token Bucket
Best for: Allowing burst traffic while maintaining average rate
The token bucket starts full and refills at a constant rate. Requests consume tokens; when tokens are depleted, requests are rejected.
use PhpRateLimiter\Algorithm\TokenBucket; // Allow 10 requests max, refill 5 tokens per second $rateLimiter = new TokenBucket( capacity: 10, // Maximum tokens in bucket refillRate: 5, // Tokens added per refill period refillPeriod: 1 // Refill period in seconds (default: 1) ); if ($rateLimiter->attempt('user-123')) { echo "Request allowed\n"; } else { echo "Rate limited - try again at: " . date('Y-m-d H:i:s', $rateLimiter->availableAt('user-123')) . "\n"; } // Check remaining capacity echo "Retries left: " . $rateLimiter->retriesLeft('user-123') . "\n";
2. Leaky Bucket
Best for: Smoothing irregular traffic into steady output rate
The leaky bucket queues requests and processes them at a fixed rate. Excess requests overflow and are rejected.
use PhpRateLimiter\Algorithm\LeakyBucket; // Queue up to 5 requests, process 2 requests per second $rateLimiter = new LeakyBucket( capacity: 5, // Maximum requests in queue leakRate: 2, // Requests processed per leak period leakPeriod: 1 // Leak period in seconds (default: 1) ); if ($rateLimiter->attempt('api-client-456')) { echo "Request queued for processing\n"; } else { echo "Queue full - request rejected\n"; } // Check queue space echo "Queue slots available: " . $rateLimiter->retriesLeft('api-client-456') . "\n";
3. Fixed Window
Best for: Simple implementation with predictable windows
Divides time into fixed windows and counts requests within each window. Simple but can allow bursts at window boundaries.
use PhpRateLimiter\Algorithm\FixedWindow; // Allow 100 requests per 5-minute window $rateLimiter = new FixedWindow( maxRequests: 100, // Maximum requests per window windowSizeSeconds: 300 // Window size in seconds (5 minutes) ); if ($rateLimiter->attempt('endpoint-789')) { echo "Request allowed\n"; } else { $nextWindow = $rateLimiter->availableAt('endpoint-789'); echo "Rate limited until: " . date('Y-m-d H:i:s', $nextWindow) . "\n"; } // Check window capacity echo "Requests left in current window: " . $rateLimiter->retriesLeft('endpoint-789') . "\n";
4. Sliding Window
Best for: Most accurate rate limiting with precise time tracking
Maintains exact timestamps of requests and slides the window continuously. Provides the most accurate rate limiting.
use PhpRateLimiter\Algorithm\SlidingWindow; // Allow 50 requests per 60-second sliding window $rateLimiter = new SlidingWindow( maxRequests: 50, // Maximum requests in sliding window windowSizeSeconds: 60 // Window size in seconds ); if ($rateLimiter->attempt('premium-user-999')) { echo "Request allowed\n"; } else { echo "Rate limited - oldest request expires at: " . date('Y-m-d H:i:s', $rateLimiter->availableAt('premium-user-999')) . "\n"; } // Check sliding window capacity echo "Requests available: " . $rateLimiter->retriesLeft('premium-user-999') . "\n";
Algorithm Comparison
| Algorithm | Memory Usage | Precision | Burst Handling | Use Case |
|---|---|---|---|---|
| Token Bucket | Low | Good | Excellent | APIs allowing bursts |
| Leaky Bucket | Low | Good | Smoothing | Traffic shaping |
| Fixed Window | Very Low | Fair | Poor | Simple rate limits |
| Sliding Window | High | Excellent | Good | Precise rate limiting |
Quick Start
The RateLimiter class is the main entry point that provides persistence for your rate limiting algorithms. It works with storage backends to maintain rate limit state across requests.
Basic Usage with RateLimiter
use PhpRateLimiter\RateLimiter; use PhpRateLimiter\Storage\RedisRateLimitStorage; use PhpRateLimiter\Algorithm\TokenBucket; // Set up storage (Redis in this example) $redis = new \Predis\Client(); $storage = new RedisRateLimitStorage($redis); // Create the main RateLimiter instance $rateLimiter = new RateLimiter($storage); // Use rate limiting $key = 'user:' . $userId; // Get existing algorithm or create new one if empty $algorithm = $rateLimiter->get($key); if (empty($algorithm)) { $algorithm = new TokenBucket(capacity: 50, refillRate: 5, refillPeriod: 60); } if ($algorithm->attempt($key)) { echo "Request allowed\n"; // Persist the algorithm state $rateLimiter->persist($algorithm, $key); } else { echo "Rate limited\n"; }
Storage Backends
Redis Storage
use PhpRateLimiter\Storage\RedisRateLimitStorage; $redis = new \Predis\Client([ 'scheme' => 'tcp', 'host' => 'localhost', 'port' => 6379, ]); $storage = new RedisRateLimitStorage($redis);
MySQL Storage
MySQL/MariaDB storage implementation with TTL support:
Database Setup:
Before using MySQL storage, create the required table by running the default.sql file:
mysql -u username -p database_name < default.sql
You can also create a table with a different name by modifying the SQL statement and passing the custom table name to the constructor.
use PhpRateLimiter\Storage\MySqlRateLimitStorage; $pdo = new \PDO('mysql:host=localhost;dbname=myapp', $username, $password, [ \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION, \PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC, ]); // Uses default table name 'rate_limit_storage' $storage = new MySqlRateLimitStorage($pdo); // Or specify custom table name $storage = new MySqlRateLimitStorage($pdo, 'custom_rate_limits');
Features:
- JSON column for flexible state storage
- TTL support with automatic cleanup
- Proper indexing for performance
- UTF8MB4 charset for full Unicode support
Creating Custom Storage Solutions
You can create your own storage backend by implementing the RateLimitStorageInterface. This allows you to use any storage system (database, file system, in-memory cache, etc.) to persist rate limit state.
**Required Interface Methods:**
- `saveState(string $key, array $state, ?int $ttl = null): void` - Store rate limit state with optional TTL
- `getState(string $key): array` - Retrieve state array (return empty array if not found)
- `deleteState(string $key): void` - Remove stored state for a key
- `hasState(string $key): bool` - Check if state exists for a key
**Implementation Guidelines:**
1. **Data Format**: State is always stored as an associative array. Serialize to JSON or your preferred format.
2. **TTL Support**: Handle optional time-to-live for automatic cleanup of expired data.
3. **Error Handling**: Throw appropriate exceptions for storage failures (`\RuntimeException`, `\JsonException`).
4. **Performance**: Consider indexing on the key column for database implementations.
5. **Key Handling**: Work with keys exactly as provided - no internal prefixing or modification.
6. **Cleanup**: Implement mechanisms to clean up expired entries if using TTL.
**Simple Custom Storage Example:**
```php
use PhpRateLimiter\Storage\RateLimitStorageInterface;
class FileRateLimitStorage implements RateLimitStorageInterface
{
public function __construct(private string $storagePath) {}
public function saveState(string $key, array $state, ?int $ttl = null): void
{
$data = [
'state' => $state,
'expires_at' => $ttl ? time() + $ttl : null
];
$filename = $this->getFilename($key);
file_put_contents($filename, json_encode($data, JSON_THROW_ON_ERROR), LOCK_EX);
}
public function getState(string $key): array
{
$filename = $this->getFilename($key);
if (!file_exists($filename)) {
return [];
}
$data = json_decode(file_get_contents($filename), true, 512, JSON_THROW_ON_ERROR);
if ($data['expires_at'] && $data['expires_at'] <= time()) {
unlink($filename);
return [];
}
return $data['state'] ?? [];
}
public function deleteState(string $key): void
{
$filename = $this->getFilename($key);
if (file_exists($filename)) {
unlink($filename);
}
}
public function hasState(string $key): bool
{
return !empty($this->getState($key));
}
private function getFilename(string $key): string
{
return $this->storagePath . '/' . hash('sha256', $key) . '.json';
}
}
Common Interface
All rate limiters implement the same RateLimitAlgorithmInterface:
// Check if request is allowed $allowed = $rateLimiter->attempt(string $key): bool; // Check if rate limited (without consuming) $isLimited = $rateLimiter->isTooManyAttempts(string $key): bool; // Get remaining capacity $remaining = $rateLimiter->retriesLeft(string $key): int; // Get next available time $nextTime = $rateLimiter->availableAt(string $key): int; // Clear rate limit for key $rateLimiter->clear(string $key): void;
Advanced Usage
Custom Time Provider (for testing)
use PhpRateLimiter\Algorithm\TokenBucket; // Create a custom time keeper for testing class MockTimeKeeper implements TimeKeeperInterface { public function __construct(private float $currentTime) {} public function getCurrentUnixMicroTimestamp(): float { return $this->currentTime; } public function advance(float $seconds): void { $this->currentTime += $seconds; } } $timeKeeper = new MockTimeKeeper(microtime(true)); $rateLimiter = new TokenBucket(10, 1, 1, $timeKeeper);
Complete Example with Multiple Storage Backends
use PhpRateLimiter\RateLimiter; use PhpRateLimiter\Storage\RedisRateLimitStorage; use PhpRateLimiter\Storage\MySqlRateLimitStorage; use PhpRateLimiter\Algorithm\TokenBucket; use PhpRateLimiter\Algorithm\SlidingWindow; // Redis storage for high-frequency rate limits $redis = new \Predis\Client(); $redisStorage = new RedisRateLimitStorage($redis); $redisRateLimiter = new RateLimiter($redisStorage); // MySQL storage for persistent rate limits $pdo = new \PDO('mysql:host=localhost;dbname=myapp', $username, $password); $mysqlStorage = new MySqlRateLimitStorage($pdo); $mysqlRateLimiter = new RateLimiter($mysqlStorage); // Rate limit by user (using Redis for speed) $userKey = "user:{$userId}"; $userAlgorithm = $redisRateLimiter->get($userKey) ?? new TokenBucket(100, 10); if ($userAlgorithm->attempt($userKey)) { echo "User request allowed\n"; $redisRateLimiter->persist($userAlgorithm, $userKey); } else { echo "User rate limited\n"; } // Rate limit by IP (using MySQL for persistence) $ipKey = "ip:{$clientIP}"; $ipAlgorithm = $mysqlRateLimiter->get($ipKey) ?? new SlidingWindow(1000, 3600); if ($ipAlgorithm->attempt($ipKey)) { echo "IP request allowed\n"; $mysqlRateLimiter->persist($ipAlgorithm, $ipKey); } else { echo "IP rate limited\n"; }
Rate Limiting Different Resources (Direct Algorithm Usage)
// Rate limit by user $userLimiter = new TokenBucket(100, 10); // 100 req burst, 10/sec refill $userLimiter->attempt("user:{$userId}"); // Rate limit by IP $ipLimiter = new SlidingWindow(1000, 3600); // 1000 req/hour $ipLimiter->attempt("ip:{$clientIP}"); // Rate limit by API endpoint $endpointLimiter = new FixedWindow(500, 60); // 500 req/minute $endpointLimiter->attempt("endpoint:/api/heavy-operation");
Examples
The examples/ directory contains practical demonstrations of how to use this library:
ApiExample.php- Demonstrates API rate limiting using Token Bucket algorithm to limit requests per API key (10 requests per minute)CmsExample.php- Shows CMS edit rate limiting with Fixed Window algorithm, allowing users 10 page edits per hour with status checkingSimpleMiddleware.php- Complete middleware implementation for web applications with HTTP headers and proper 429 responses using Token Bucket
avanboxel/php-rate-limiter 适用场景与选型建议
avanboxel/php-rate-limiter 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 13 次下载、GitHub Stars 达 1, 最近一次更新时间为 2025 年 07 月 30 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「throttling」 「rate-limiting」 「token-bucket」 「leaky-bucket」 「sliding-window」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 avanboxel/php-rate-limiter 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 avanboxel/php-rate-limiter 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 avanboxel/php-rate-limiter 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A throttler for symfony/http-client to handle rate limits
Simple PHP execution throttler.
Throttle notifications on a per-channel basis
A modular queue system featuring multiple storage drivers, scalable workers, job batching, chaining, rate limiting, events, and an intuitive CLI for managing jobs.
PHP rate limiting library with Token Bucket and Leaky Bucket Algorithms, based on palepurple/rate-limit, grandson to touhonoob/rate-limit, and jeroenvisser101/LeakyBucket
A flexible rate limiting middleware for Laravel and Lumen applications
统计信息
- 总下载量: 13
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 28
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-07-30