PHPackages                             avanboxel/php-rate-limiter - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. avanboxel/php-rate-limiter

ActiveLibrary[Database &amp; ORM](/categories/database)

avanboxel/php-rate-limiter
==========================

A flexible rate limiting library for PHP applications with multiple algorithm implementations and persistent storage support

v0.0.1(9mo ago)18MITPHPPHP &gt;=8.0

Since Jul 30Pushed 9mo agoCompare

[ Source](https://github.com/avanboxel/php-rate-limiter)[ Packagist](https://packagist.org/packages/avanboxel/php-rate-limiter)[ Docs](https://github.com/avanboxel/php-rate-limiter)[ RSS](/packages/avanboxel-php-rate-limiter/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (2)Versions (2)Used By (0)

PHP Rate Limiter
================

[](#php-rate-limiter)

A flexible rate limiting library for PHP applications with multiple algorithm implementations and persistent storage support.

Installation
------------

[](#installation)

```
composer require avanboxel/php-rate-limiter
```

Rate Limiting Algorithms
------------------------

[](#rate-limiting-algorithms)

This library provides four different rate limiting algorithms, each with unique characteristics:

### 1. Token Bucket

[](#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

[](#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

[](#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

[](#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-comparison)

AlgorithmMemory UsagePrecisionBurst HandlingUse Case**Token Bucket**LowGoodExcellentAPIs allowing bursts**Leaky Bucket**LowGoodSmoothingTraffic shaping**Fixed Window**Very LowFairPoorSimple rate limits**Sliding Window**HighExcellentGoodPrecise rate limitingQuick Start
-----------

[](#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

[](#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
----------------

[](#storage-backends)

### Redis Storage

[](#redis-storage)

```
use PhpRateLimiter\Storage\RedisRateLimitStorage;

$redis = new \Predis\Client([
    'scheme' => 'tcp',
    'host'   => 'localhost',
    'port'   => 6379,
]);

$storage = new RedisRateLimitStorage($redis);
```

### MySQL Storage

[](#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  \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

[](#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'] 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
----------------

[](#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
--------------

[](#advanced-usage)

### Custom Time Provider (for testing)

[](#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

[](#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-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
--------

[](#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 checking
- **`SimpleMiddleware.php`** - Complete middleware implementation for web applications with HTTP headers and proper 429 responses using Token Bucket

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance56

Moderate activity, may be stable

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity31

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

292d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5e75e15d7324c07178a9b795436ac563857356e30e2394a5c3f93fccd37a4705?d=identicon)[avanboxel](/maintainers/avanboxel)

---

Top Contributors

[![avanboxel](https://avatars.githubusercontent.com/u/6064753?v=4)](https://github.com/avanboxel "avanboxel (2 commits)")

---

Tags

api-limitapi-limiterfixed-windowleaky-bucketno-frameworkphprate-limitingredissliding-windowsqlthrottlethrottlingtoken-bucketvanillaphpthrottlingrate limitingtoken bucketleaky bucketsliding-windowapi-limiting

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/avanboxel-php-rate-limiter/health.svg)

```
[![Health](https://phpackages.com/badges/avanboxel-php-rate-limiter/health.svg)](https://phpackages.com/packages/avanboxel-php-rate-limiter)
```

###  Alternatives

[bandwidth-throttle/token-bucket

Implementation of the Token Bucket algorithm.

5121.9M10](/packages/bandwidth-throttle-token-bucket)[maba/gentle-force

Library for limiting both brute-force attempts and ordinary requests, using leaky/token bucket algorithm, based on Redis

45591.0k2](/packages/maba-gentle-force)[maba/gentle-force-bundle

Symfony bundle that integrates gentle-force library for limiting both brute-force attempts and ordinary requests, using leaky/token bucket algorithm, based on Redis

53517.6k1](/packages/maba-gentle-force-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
