Skip to content

Instantly share code, notes, and snippets.

@joeyboli
Forked from freekrai/demo.php
Created June 14, 2023 20:30
Show Gist options
  • Save joeyboli/559a9e3de23200b7b4e227beefc564d7 to your computer and use it in GitHub Desktop.
Save joeyboli/559a9e3de23200b7b4e227beefc564d7 to your computer and use it in GitHub Desktop.

Revisions

  1. @freekrai freekrai revised this gist Oct 26, 2016. 2 changed files with 19 additions and 9 deletions.
    11 changes: 4 additions & 7 deletions demo.php
    Original file line number Diff line number Diff line change
    @@ -6,18 +6,15 @@
    // in this sample, we are using the originating IP, but you can modify to use API keys, or tokens or what-have-you.
    $rateLimiter = new RateLimiter($_SERVER["REMOTE_ADDR"]);

    // how many connections to allow per session within the number of $minutes specified.
    $limit = 100;

    // how many minutes to limit the $limit connections to
    // For example if we set it to 100 connections over 2 minutes, then at 101 connections we tell them too many.
    $minutes = 2;
    $limit = 100; // number of connections to limit user to per $minutes
    $minutes = 1; // number of $minutes to check for.
    $seconds = floor($minutes * 60); // retry after $minutes in seconds.

    try {
    $rateLimiter->limitRequestsInMinutes($limit, $minutes);
    } catch (RateExceededException $e) {
    header("HTTP/1.1 429 Too Many Requests");
    header(sprintf("Retry-After: %d", (60 * $minutes)));
    header(sprintf("Retry-After: %d", $seconds));
    $data = 'Rate Limit Exceeded ';
    die (json_encode($data));
    }
    17 changes: 15 additions & 2 deletions ratelimiter.php
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,23 @@
    <?php
    /*
    This is a really simple plug and play rate limiter class meant to be used with APIs.
    Using sessions means we can throw this into any PHP API quickly.
    $token can be anything to uniquely identify a user, either an IP address, an API key, a JWT token, anything that is unique to a single user.
    $prefix can be whatever you want, we default it to "rate"
    On init, we create a md5 hash of our $prefix and our $token, this becomes the prefix throughout the class.
    We then append a timestamp to this prefix
    */
    class RateExceededException extends Exception {}

    class RateLimiter {
    private $prefix;
    public function __construct($ip, $prefix = "rate") {
    $this->prefix = md5($prefix . $ip);
    public function __construct($token, $prefix = "rate") {
    $this->prefix = md5($prefix . $token);

    if( !isset($_SESSION["cache"]) ){
    $_SESSION["cache"] = array();
  2. @freekrai freekrai created this gist Oct 26, 2016.
    28 changes: 28 additions & 0 deletions demo.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,28 @@
    <?php
    date_default_timezone_set('America/Los_Angeles');
    session_start();
    include("ratelimiter.php");

    // in this sample, we are using the originating IP, but you can modify to use API keys, or tokens or what-have-you.
    $rateLimiter = new RateLimiter($_SERVER["REMOTE_ADDR"]);

    // how many connections to allow per session within the number of $minutes specified.
    $limit = 100;

    // how many minutes to limit the $limit connections to
    // For example if we set it to 100 connections over 2 minutes, then at 101 connections we tell them too many.
    $minutes = 2;

    try {
    $rateLimiter->limitRequestsInMinutes($limit, $minutes);
    } catch (RateExceededException $e) {
    header("HTTP/1.1 429 Too Many Requests");
    header(sprintf("Retry-After: %d", (60 * $minutes)));
    $data = 'Rate Limit Exceeded ';
    die (json_encode($data));
    }

    // ok, they were within their limit, so let's continue with our app....
    $data = "Data Returned from API ";
    header('Content-Type: application/json');
    die(json_encode($data));
    71 changes: 71 additions & 0 deletions ratelimiter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,71 @@
    <?php
    class RateExceededException extends Exception {}

    class RateLimiter {
    private $prefix;
    public function __construct($ip, $prefix = "rate") {
    $this->prefix = md5($prefix . $ip);

    if( !isset($_SESSION["cache"]) ){
    $_SESSION["cache"] = array();
    }

    if( !isset($_SESSION["expiries"]) ){
    $_SESSION["expiries"] = array();
    }else{
    $this->expireSessionKeys();
    }
    }

    public function limitRequestsInMinutes($allowedRequests, $minutes) {
    $this->expireSessionKeys();
    $requests = 0;

    foreach ($this->getKeys($minutes) as $key) {
    $requestsInCurrentMinute = $this->getSessionKey($key);
    if (false !== $requestsInCurrentMinute) $requests += $requestsInCurrentMinute;
    }

    if (false === $requestsInCurrentMinute) {
    $this->setSessionKey( $key, 1, ($minutes * 60 + 1) );
    } else {
    $this->increment($key, 1);
    }
    if ($requests > $allowedRequests) throw new RateExceededException;
    }

    private function getKeys($minutes) {
    $keys = array();
    $now = time();
    for ($time = $now - $minutes * 60; $time <= $now; $time += 60) {
    $keys[] = $this->prefix . date("dHi", $time);
    }
    return $keys;
    }

    private function increment( $key, $inc){
    $cnt = 0;
    if( isset($_SESSION['cache'][$key]) ){
    $cnt = $_SESSION['cache'][$key];
    }
    $_SESSION['cache'][$key] = $cnt + $inc;
    }

    private function setSessionKey( $key, $val, $expiry ){
    $_SESSION["expiries"][$key] = time() + $expiry;
    $_SESSION['cache'][$key] = $val;
    }

    private function getSessionKey( $key ){
    return isset($_SESSION['cache'][$key]) ? $_SESSION['cache'][$key] : false;
    }

    private function expireSessionKeys() {
    foreach ($_SESSION["expiries"] as $key => $value) {
    if (time() > $value) {
    unset($_SESSION['cache'][$key]);
    unset($_SESSION["expiries"][$key]);
    }
    }
    }
    }