php
46 lines · 7 steps
How a token bucket rate limiter works
A token bucket smooths request bursts by refilling credits over time and blocking callers until enough tokens are available.
Explained by
highlit
1final class TokenBucketLimiter
2{
3 private float $tokens;
4 private float $lastRefill;
5
6 public function __construct(
7 private readonly float $capacity,
8 private readonly float $refillPerSecond
9 ) {
10 $this->tokens = $capacity;
11 $this->lastRefill = microtime(true);
12 }
13
14 public function acquire(float $cost = 1.0): void
15 {
16 if ($cost > $this->capacity) {
17 throw new InvalidArgumentException('Cost exceeds bucket capacity');
18 }
19
20 while (true) {
21 $this->refill();
22
23 if ($this->tokens >= $cost) {
24 $this->tokens -= $cost;
25 return;
26 }
27
28 $deficit = $cost - $this->tokens;
29 $waitSeconds = $deficit / $this->refillPerSecond;
30 usleep((int) ceil($waitSeconds * 1_000_000));
31 }
32 }
33
34 private function refill(): void
35 {
36 $now = microtime(true);
37 $elapsed = $now - $this->lastRefill;
38
39 if ($elapsed <= 0) {
40 return;
41 }
42
43 $this->tokens = min($this->capacity, $this->tokens + $elapsed * $this->refillPerSecond);
44 $this->lastRefill = $now;
45 }
46}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A token bucket allows short bursts up to capacity while enforcing a steady average rate over time.
- 2Refilling lazily from elapsed time avoids running a background timer to add tokens.
- 3Computing the wait from the exact token deficit lets a blocked caller sleep just long enough instead of busy-polling.
Related explainers
php
<?php class NameParser {
Parsing a full name into components in PHP
string-parsing
arrays
normalization
Intermediate
8 steps
php
<?php namespace App\Services\Checkout;
Validating coupons with Laravel's Pipeline
pipeline
chain of responsibility
transactions
Intermediate
7 steps
php
<?php namespace App\Services;
How a password strength validator works in PHP
validation
regular-expressions
data-driven
Intermediate
8 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
php
<?php namespace App\Services;
Building a cached daily leaderboard in Laravel
caching
aggregation
eager-loading
Intermediate
9 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-a-token-bucket-rate-limiter-works-explained-php-2aad/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.