PHP code example of colinmollenhour / rate-limiter

1. Go to this page and download the library: Download colinmollenhour/rate-limiter library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

colinmollenhour / rate-limiter example snippets


use Cm\RateLimiter\RateLimiterFactory;
use Credis_Client;

$redis = new Credis_Client('127.0.0.1', 6379);
$factory = new RateLimiterFactory($redis);

// Choose your algorithm
$rateLimiter = $factory->createSlidingWindow(); // or createFixedWindow(), createLeakyBucket(), createGCRA(), createTokenBucket()

// Rate limit: 10 burst capacity, 1 request/second sustained, 60 second window
$result = $rateLimiter->attempt('user:123', 10, 1.0, 60);

if ($result->successful()) {
    echo "Request allowed. {$result->retriesLeft} requests remaining";
} else {
    echo "Rate limited. Try again in {$result->retryAfter} seconds";
}

// Create GCRA rate limiter with concurrency control
$limiter = $factory->createConcurrencyAware('gcra');
$requestId = uniqid('req_', true);

$result = $limiter->attemptWithConcurrency(
    key: 'api:user:123',
    requestId: $requestId,
    maxConcurrent: 5,        // Max 5 concurrent requests
    burstCapacity: 10,       // Rate limit burst allowance  
    sustainedRate: 2.0,      // 2 requests/second sustained
    window: 60,              // 60-second window
    timeoutSeconds: 30       // 30s timeout for slow requests
);

if ($result->successful()) {
    try {
        // Process request - guaranteed max 5 concurrent + 2/s rate
    } finally {
        $limiter->releaseConcurrency('api:user:123', $requestId);
    }
} elseif ($result->rejectedByConcurrency()) {
    // Handle concurrency limit (don't count against rate limit)
    http_response_code(503);
    echo "Service temporarily unavailable";
} else {
    // Handle rate limit
    http_response_code(429);
    header("Retry-After: " . $result->retryAfter);
    echo "Rate limit exceeded";
}

// GCRA + concurrency control
$limiter = $factory->createConcurrencyAware('gcra');

// Token bucket + concurrency control  
$limiter = $factory->createConcurrencyAware('token');

// Pure concurrency limiting (no rate limiting)
$limiter = $factory->createConcurrencyAware(null);

// Pure rate limiting (no concurrency control)
$limiter = $factory->createGCRA(); // or any other algorithm

// Web API Protection (GCRA for performance + concurrency for slow queries)
$limiter = $factory->createConcurrencyAware('gcra');
$result = $limiter->attemptWithConcurrency('api:search', $requestId, 5, 20, 1.0, 60, 30);

// File Upload Service (Token bucket for bursts + concurrency for upload congestion)
$limiter = $factory->createConcurrencyAware('token');
$result = $limiter->attemptWithConcurrency('upload:user:'.$userId, $requestId, 2, 5, 0.5, 3600, 300);

// Background Job Processing (Pure concurrency for resource control)
$limiter = $factory->createConcurrencyAware(null);
$result = $limiter->attemptWithConcurrency('jobs:heavy', $jobId, 3, 0, 0, 0, 1800);

// Enhanced API: attempt(key, burstCapacity, sustainedRate, window)
$result = $rateLimiter->attempt('user:123', 10, 1.0, 60);
// Allows: 10 requests immediately (burst), then 1 request/second (sustained)

$count = $rateLimiter->attempts($key, $window);
$remaining = $rateLimiter->remaining($key, $burstCapacity, $sustainedRate, $window);
$availableIn = $rateLimiter->availableIn($key, $burstCapacity, $sustainedRate, $window);
$rateLimiter->resetAttempts($key);

$tokenBucket = $factory->createTokenBucket();
// 5 requests immediately, then 2 requests/second
$result = $tokenBucket->attempt('user:123', 5, 2.0, 60);

$fixedWindow = $factory->createFixedWindow();
// 100 requests per 60-second window (sustained rate ignored)
$result = $fixedWindow->attempt('user:123', 100, 1.0, 60);

$slidingWindow = $factory->createSlidingWindow();
// Smooth rate limiting at 10 requests/minute (burst ignored)
$result = $slidingWindow->attempt('user:123', 10, 10.0/60, 60);

$leakyBucket = $factory->createLeakyBucket();
// 20 request capacity, leaks at 20/window rate (sustained rate ignored)
$result = $leakyBucket->attempt('user:123', 20, 1.0, 60);

$factory->createSlidingWindow();      // Smooth rate limiting
$factory->createFixedWindow();        // Window-based with burst
$factory->createLeakyBucket();        // Bucket capacity with leak
$factory->createGCRA();               // Memory-efficient smooth limiting
$factory->createTokenBucket();        // Perfect burst + sustained rate
$factory->createConcurrencyAware('gcra');  // GCRA + concurrency control
$factory->createConcurrencyAware(null);   // Pure concurrency limiting

$slidingWindow = new \Cm\RateLimiter\SlidingWindow\RateLimiter($redis);
$fixedWindow = new \Cm\RateLimiter\FixedWindow\RateLimiter($redis);
$leakyBucket = new \Cm\RateLimiter\LeakyBucket\RateLimiter($redis);
$gcra = new \Cm\RateLimiter\GCRA\RateLimiter($redis);
$tokenBucket = new \Cm\RateLimiter\TokenBucket\RateLimiter($redis);
$concurrencyAware = new \Cm\RateLimiter\ConcurrencyAware\RateLimiter($redis, $tokenBucket);

class RateLimiterResult {
    public function successful(): bool;     // Was the request allowed?
    public int $retryAfter;                // Seconds until next request allowed
    public int $retriesLeft;               // Requests remaining in current window
    public int $limit;                     // Total limit (burst capacity)
}

class ConcurrencyAwareResult extends RateLimiterResult {
    public bool $concurrencyAcquired;           // Was concurrency slot acquired?
    public ?string $concurrencyRejectionReason; // Why was concurrency rejected?
    public int $currentConcurrency;              // Current concurrent requests
    public int $maxConcurrency;                  // Maximum allowed concurrent requests
    
    public function rejectedByConcurrency(): bool; // Was blocked by concurrency limit?
    public function rejectedByRateLimit(): bool;   // Was blocked by rate limit?
}

$tokenBucket = $factory->createTokenBucket();
// Allow 20 requests immediately, then 5 requests/second
$result = $tokenBucket->attempt("api:{$userId}", 20, 5.0, 60);

// Perfect for APIs with bursty traffic AND slow backend operations
$limiter = $factory->createConcurrencyAware('token');
$requestId = uniqid('req_', true);

$result = $limiter->attemptWithConcurrency(
    key: "api:upload:{$userId}",
    requestId: $requestId,
    maxConcurrent: 3,        // Max 3 concurrent uploads per user
    burstCapacity: 10,       // Allow 10 uploads immediately
    sustainedRate: 2.0,      // Then 2 uploads/second sustained
    window: 60,              // 60-second rate limit window
    timeoutSeconds: 300      // 5-minute timeout for slow uploads
);

if ($result->successful()) {
    try {
        // Process file upload - guaranteed max 3 concurrent + 2/s rate
        processFileUpload($file);
    } finally {
        // Always release concurrency slot when done
        $limiter->releaseConcurrency("api:upload:{$userId}", $requestId);
    }
} elseif ($result->rejectedByConcurrency()) {
    // Too many concurrent uploads - don't count against rate limit
    http_response_code(503);
    echo "Too many concurrent uploads. Try again shortly.";
} else {
    // Rate limit exceeded
    http_response_code(429);
    header("Retry-After: " . $result->retryAfter);
    echo "Upload rate limit exceeded. Try again in {$result->retryAfter} seconds.";
}

// Perfect for resource-intensive operations like database migrations or heavy processing
$limiter = $factory->createConcurrencyAware(null); // null = no rate limiting
$jobId = uniqid('job_', true);

$result = $limiter->attemptWithConcurrency(
    key: 'jobs:heavy-processing',
    requestId: $jobId,
    maxConcurrent: 2,        // Only 2 heavy jobs at once
    burstCapacity: 0,        // No rate limiting
    sustainedRate: 0,        // No rate limiting
    window: 0,               // No rate limiting
    timeoutSeconds: 1800     // 30-minute timeout for long jobs
);

if ($result->successful()) {
    try {
        // Process heavy job - guaranteed max 2 concurrent, no rate limits
        processHeavyJob($jobData);
    } finally {
        $limiter->releaseConcurrency('jobs:heavy-processing', $jobId);
    }
} else {
    // Only concurrency rejection possible (no rate limiting)
    echo "Too many concurrent jobs. Current: {$result->currentConcurrency}/{$result->maxConcurrency}";
}

$slidingWindow = $factory->createSlidingWindow();
// Smooth 100 requests per hour (no bursts)
$result = $slidingWindow->attempt("user:{$id}", 100, 100.0/3600, 3600);

$fixedWindow = $factory->createFixedWindow();
// 1000 requests per minute window
$result = $fixedWindow->attempt("endpoint:{$route}", 1000, 1.0, 60);

$gcra = $factory->createGCRA();
// 50 requests per minute, smooth distribution
$result = $gcra->attempt("service:{$key}", 50, 50.0/60, 60);
bash
php -S localhost:8000 playground/index.php
open http://localhost:8000
bash
# Maximum precision latency analysis
php stress-test.php --latency-precision=5 --algorithms=gcra,sliding

# Memory-efficient sampling for high-load tests
php stress-test.php --latency-sample=100 --max-speed --processes=10
bash
# Ubuntu/Debian
sudo apt-get install php-pcntl