Rate Limiting in PHP

Here’s the translation of the rate limiting example from Go to PHP, presented in Markdown format suitable for Hugo:

Rate limiting is an important mechanism for controlling resource utilization and maintaining quality of service. PHP can support rate limiting using time-based checks and sleep functions.

Our first example demonstrates basic rate limiting:

<?php

// First we'll look at basic rate limiting. Suppose
// we want to limit our handling of incoming requests.
// We'll serve these requests from an array.
$requests = [1, 2, 3, 4, 5];

// This $limiter variable will be used to ensure we process
// a request only every 200 milliseconds.
$limiter = microtime(true);

foreach ($requests as $req) {
    // By checking the time before processing each request,
    // we limit ourselves to 1 request every 200 milliseconds.
    while (microtime(true) - $limiter < 0.2) {
        usleep(1000); // Sleep for 1ms to avoid busy waiting
    }
    
    echo "request $req " . date('Y-m-d H:i:s') . PHP_EOL;
    $limiter = microtime(true);
}

// Now let's look at bursty rate limiting. We may want to allow
// short bursts of requests in our rate limiting scheme while
// preserving the overall rate limit.

// This burstyLimiter array will allow bursts of up to 3 events.
$burstyLimiter = [microtime(true), microtime(true), microtime(true)];
$burstyRequests = [1, 2, 3, 4, 5];

foreach ($burstyRequests as $req) {
    // Use the oldest timestamp in the burstyLimiter
    $oldestTime = min($burstyLimiter);
    
    // If the oldest timestamp is less than 200ms ago, we need to wait
    $timeToWait = 0.2 - (microtime(true) - $oldestTime);
    if ($timeToWait > 0) {
        usleep($timeToWait * 1000000);
    }
    
    echo "request $req " . date('Y-m-d H:i:s') . PHP_EOL;
    
    // Update the oldest timestamp with the current time
    $burstyLimiter[array_search($oldestTime, $burstyLimiter)] = microtime(true);
}

Running our program, we see the first batch of requests handled once every ~200 milliseconds as desired:

request 1 2023-06-01 12:00:00
request 2 2023-06-01 12:00:00
request 3 2023-06-01 12:00:00
request 4 2023-06-01 12:00:00
request 5 2023-06-01 12:00:01

For the second batch of requests, we serve the first 3 immediately because of the burstable rate limiting, then serve the remaining 2 with ~200ms delays each:

request 1 2023-06-01 12:00:01
request 2 2023-06-01 12:00:01
request 3 2023-06-01 12:00:01
request 4 2023-06-01 12:00:01
request 5 2023-06-01 12:00:01

This example demonstrates how to implement basic and bursty rate limiting in PHP. The concept is similar to the original, but the implementation is adapted to PHP’s features and limitations. Instead of channels and goroutines, we use arrays and time-based checks to achieve rate limiting.