detain/rate-limit

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# rate-limit

[![GitHub Build](https://github.com/detain/rate-limit/actions/workflows/php.yml/badge.svg)](https://github.com/detain/rate-limit/actions/workflows/php.yml)
[![Travis Build](https://app.travis-ci.com/detain/rate-limit.svg?branch=master)](https://app.travis-ci.com/detain/rate-limit)
[![Coverage Status](https://coveralls.io/repos/github/detain/rate-limit/badge.svg?branch=master)](https://coveralls.io/github/detain/rate-limit?branch=master)
[![Code Climate](https://codeclimate.com/github/detain/rate-limit/badges/gpa.svg)](https://codeclimate.com/github/detain/rate-limit)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/detain/rate-limit/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/detain/rate-limit/?branch=master)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/659523f63e16487ea71f6b763908d09e)](https://www.codacy.com/app/detain/rate-limit)

[![Latest Stable Version](https://poser.pugx.org/detain/rate-limit/version)](https://packagist.org/packages/detain/rate-limit)
[![Total Downloads](https://poser.pugx.org/detain/rate-limit/downloads)](https://packagist.org/packages/detain/rate-limit)
[![Latest Unstable Version](https://poser.pugx.org/detain/rate-limit/v/unstable)](//packagist.org/packages/detain/rate-limit)
[![License](https://poser.pugx.org/detain/rate-limit/license)](https://packagist.org/packages/detain/rate-limit)
[![Monthly Downloads](https://poser.pugx.org/detain/rate-limit/d/monthly)](https://packagist.org/packages/detain/rate-limit)
[![Daily Downloads](https://poser.pugx.org/detain/rate-limit/d/daily)](https://packagist.org/packages/detain/rate-limit)

PHP Rate Limiting library with both Token Bucket and Leaky Bucket Algorithms, minimal external dependencies, and many storage backends.

# Algorithms

- [x] [Token Bucket Algorithm](https://en.wikipedia.org/wiki/Token_bucket) Token Bucket is an algorithm which works as follows:
  - There is a bucket.
  - A token is added to the bucket every 1/r seconds.
  - The bucket can hold at the most b tokens. If a token arrives when the bucket is full, it is discarded.
  - When a packet (network layer PDU) of n bytes arrives,
    - If at least n tokens are in the bucket, n tokens are removed from the bucket, and the packet is sent to the network.
    - if fewer than n tokens are available, no tokens are removed from the bucket, and the packet is considered to be non-conformant.
    
- [x] [Leaky Bucket Algorithm](https://en.wikipedia.org/wiki/Leaky_bucket) Leaky Bucket is an algorithm which works as follows:
  - There is a bucket.
  - The bucket has a defined leak and defined capacity.
  - The bucket leaks at a constant rate.
  - Overflows when full, will not add other drops to the bucket.
  

# Storage Adapters

The RateLimiter needs to know where to get/set data. 

Depending on which adapter you install, you may need to install additional libraries (predis/predis or tedivm/stash) or PHP extensions (e.g. Redis, Memcache, APCu)


- [APCu](https://pecl.php.net/package/APCu)
- [Redis](https://pecl.php.net/package/redis) or [Predis](https://github.com/nrk/predis)
- [Stash](http://www.stashphp.com) (This supports many drivers - see http://www.stashphp.com/Drivers.html )
- [Memcached](http://php.net/manual/en/intro.memcached.php)


# Installing via Composer

````shell
curl -sS https://getcomposer.org/installer | php
composer.phar require detain/rate-limit
````

# Usage

## Token Bucket
````php
require 'vendor/autoload.php';

use \Detain\RateLimit\RateLimit;
use \Detain\RateLimit\Adapter\APCu as APCAdapter;
use \Detain\RateLimit\Adapter\Redis as RedisAdapter;
use \Detain\RateLimit\Adapter\Predis as PredisAdapter;
use \Detain\RateLimit\Adapter\Memcached as MemcachedAdapter;
use \Detain\RateLimit\Adapter\Stash as StashAdapter;


$adapter = new APCAdapter(); // Use APC as Storage
// Alternatives:
//
// $adapter = new RedisAdapter((new \Redis()->connect('localhost'))); // Use Redis as Storage
//
// $adapter = new PredisAdapter(new \Predis\Predis(['tcp://127.0.0.1:6379'])); // Use Predis as Storage
//
// $memcache = new \Memcached();
// $memcache->addServer('localhost', 11211);
// $adapter = new MemcacheAdapter($memcache); 
//
// $stash = new \Stash\Pool(new \Stash\Driver\FileSystem());
// $adapter = new StashAdapter($stash);

$rateLimit = new RateLimit("myratelimit", 100, 3600, $adapter); // 100 Requests / Hour

$id = $_SERVER['REMOTE_ADDR']; // Use client IP as identity
if ($rateLimit->check($id)) {
  echo "passed";
} else {
  echo "rate limit exceeded";
}
````

## Leaky Bucket

### Basic usage
``` php
<?php

use \Detain\RateLimit\LeakyBucket;
use \Detain\RateLimit\Adapter\Redis as RedisAdapter;

$adapter = new RedisAdapter((new \Redis()->connect('localhost'))); // Use Redis as Storage

// Define the bucket
$settings = [
    'capacity' => 10,
    'leak'     => 1
];

// Create the bucket
$bucket = new LeakyBucket('example-bucket', $storage, $settings);

// Fill the bucket
$bucket->fill();

// Check if it's full
if ($bucket->isFull()) {
    header('HTTP/1.1 429 Too Many Requests');
    exit '<!doctype html><html><body><h1>429 Too Many Requests</h1><p>You seem to be doing a lot of requests. You\'re now cooling down.</p></body></html>';
}

// ...
```

### Other functionality
You can also do great stuff with it through the methods that LeakyBucket provides.

``` php
// Get capacity information
$capacityTotal = $bucket->getCapacity();
$capacityLeft  = $bucket->getCapacityLeft();
$capacityUsed  = $bucket->getCapacityUsed();

// Get the drops/second that the bucket leaks
$leakPerSecond = $bucket->getLeak();

// Get the last timestamp from when the bucket was updated
$timestamp = $bucket->getLastTimestamp();

// Set additional data
$bucket->setData(
    [
        'timeout' => 3600
    ]
);

// Get additional data
$data = $bucket->getData();

// Update the bucket with the leaked drops
$bucket->leak();

// Remove excess drops
$bucket->overflow();

// Update the bucket's timestamp manually
$bucket->touch();

// Fill the bucket with one drop
$bucket->fill();

// Fill the bucket with 5 drops
$bucket->fill(5);

// Spill one drop from the bucket
$bucket->spill();

// Spill 5 drops from the bucket
$bucket->spill(5);

// Remove the bucket's content
$bucket->reset();

// Force save
$bucket->save();
```

# References

## Continuation or Expansion of these Projects

- [this code is forked from DavidGoodwin/RateLimit](https://github.com/DavidGoodwin/RateLimit)
- [which was built on touhonoob/RateLimit](https://github.com/touhonoob/RateLimit)
- [and then i merged it with jeroenvisser101/LeakyBucket](https://github.com/jeroenvisser101/LeakyBucket)

## Knowledge

- [Token Bucket Algorithm](http://en.wikipedia.org/wiki/Token_bucket)
- [Leaky Bucket Algorithm](https://en.wikipedia.org/wiki/Leaky_bucket)
- [stackoverflow post about Rate Limiting](http://stackoverflow.com/a/668327/670662)

## PSR-7 Middleware Handlers to Base the Future PSR7 Support Off Of

- [nikolaposa/rate-limit](https://github.com/nikolaposa/rate-limit) 🚔 Standalone component that facilitates rate-limiting functionality. Also provides a middleware designed for API and/or other application endpoints. 
- [robwittman/leaky-bucket-rate-limiter](https://github.com/robwittman/leaky-bucket-rate-limiter) PSR-7 Leaky Bucket Rate Limiter - This middleware enables API Rate-Limiting based on a Leaky Bucket algorithm.