ez-php / rate-limiter
Rate limiter module for the ez-php framework — array, Redis, and cache-backed drivers with ThrottleMiddleware
1.3.0
2026-03-29 22:47 UTC
Requires
- php: ^8.5
- ez-php/cache: ^1.0
- ez-php/contracts: ^1.0
- ez-php/http: ^1.0
Requires (Dev)
- ez-php/docker: ^1.0
- ez-php/testing-application: ^1.0
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^13.0
README
Request throttling for ez-php applications — three backends, a unified interface, and a plug-in ThrottleMiddleware.
Installation
composer require ez-php/rate-limiter
Drivers
| Driver | Persistence | External requirement | Concurrency-safe |
|---|---|---|---|
ArrayDriver |
In-process (lost on restart) | None | No — single-process/test use only |
RedisDriver |
Redis | ext-redis |
Yes — atomic INCR |
CacheDriver |
Delegates to ez-php/cache |
Any configured cache driver | Driver-dependent |
Warning:
ArrayDriveruses a plain PHP array without atomic operations. Concurrent requests (e.g. PHP-FPM workers) can race and both be allowed through simultaneously. UseRedisDriverorCacheDriverin production.
Basic usage
use EzPhp\RateLimiter\ArrayDriver; $limiter = new ArrayDriver(); if (!$limiter->attempt('login:' . $ip, maxAttempts: 5, decaySeconds: 60)) { // Too many attempts — respond with 429 } $limiter->remainingAttempts('login:' . $ip, 5); // how many hits are still allowed $limiter->resetAttempts('login:' . $ip); // clear the counter (e.g. on success)
ThrottleMiddleware
Plug into the framework middleware pipeline for per-IP global or per-route throttling:
// Global — in AppServiceProvider::boot() $app->middleware(new ThrottleMiddleware($limiter, maxAttempts: 60, decaySeconds: 60)); // Per-route $router->get('/login', [LoginController::class, 'store']) ->middleware(new ThrottleMiddleware($limiter, maxAttempts: 5, decaySeconds: 60));
The middleware:
- Resolves the client IP from
X-Forwarded-For(first value) or falls back toREMOTE_ADDR. - Returns HTTP 429 with body
Too Many Requestswhen the limit is exceeded. - Adds
X-RateLimit-LimitandX-RateLimit-Remainingheaders on every passing response.
Service provider
Register RateLimiterServiceProvider in provider/modules.php:
\EzPhp\RateLimiter\RateLimiterServiceProvider::class,
Create config/rate_limiter.php:
<?php return [ 'driver' => env('RATE_LIMITER_DRIVER', 'array'), // array | redis | cache 'redis' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'port' => (int) env('REDIS_PORT', 6379), 'database' => (int) env('REDIS_RATE_LIMITER_DB', 0), ], ];
Interface
interface RateLimiterInterface { public function attempt(string $key, int $maxAttempts, int $decaySeconds): bool; public function tooManyAttempts(string $key, int $maxAttempts): bool; public function remainingAttempts(string $key, int $maxAttempts): int; public function resetAttempts(string $key): void; }
License
MIT