<?php

// Cache can be any sort of key/value cache: Memcached, Redis, or simple in memory cache
// Memory required per key should be about 40 - 50 bytes
// Thus one million users should require about 50MB or memory to watch

$m = new Memcached();
$m->addServer('127.0.0.1', 11211);
$m->setOption(Memcached::OPT_BINARY_PROTOCOL, 1); // Required for increment()

if (!empty($_GET['show_code'])) { highlight_file(__FILE__); exit; }

////////////////////////////////////////////////////////

$allowed_hits = 5; // Allowed number of hits per X seconds
$bucket_size  = 60; // Number of seconds to check
$key          = $_GET['api_key'] ?? $_SERVER['REMOTE_ADDR']; // Some unique key for the user

$debug   = true;
$allowed = is_allowed($key, $allowed_hits, $bucket_size, $debug);

////////////////////////////////////////////////////////////////

function is_allowed($key, $allowed_hits, $bucket_size, $debug = false) {
    global $m;

    $start  = microtime(1);
    $bucket = time() - (time() % $bucket_size);

    // Get the current number of hits for this key from the cache
    $ckey = "API_LIMIT:$bucket:$key";
    $hits = $m->increment($ckey, 1, 1, time() + $bucket_size);

    //Uncomment this if you can't use increment()
    //$hits = intval($m->get($ckey));
    //$ok   = $m->set($ckey, ++$hits, time() + $bucket_size);

    $ret = true;
    if ($hits > $allowed_hits) {
        $ret = false;
    }

    if ($debug) {
        $remain = $bucket_size - (time() % $bucket_size);

        $out  = "<h1>API Limit tester</h1>\n\n";
        $out .= "<p>You are allowed to hit this API $allowed_hits times every $bucket_size seconds</p>\n";
        $out .= "<p>You are $key and you've hit this API $hits times in the last $bucket_size seconds. Counter will reset in $remain seconds</p>\n";

        if (!$ret) {
            $out .= "<div style=\"color: red;\"><b>DENIED</b></div>\n";
        } else {
            $out .= "<div style=\"color: green;\"><b>Allowed</b></div>\n";
        }

        $out .= "<p><a href=\"?show_code=true\">[View code]</a></p>\n";
        $out .= sprintf("<p>%0.2f ms to process</p>\n", (microtime(1) - $start) * 1000);

        $text_only = preg_match("/curl/", $_SERVER['HTTP_USER_AGENT']);
        if ($text_only) {
            $out = strip_tags($out);
        }

        print $out;
    }

    return $ret;
}