diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57872d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/vendor/ diff --git a/README.md b/README.md index 6011aa6..df7294c 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,18 @@ The class works around the problem that the timeframe is constantly moving, i.e. The code is released under an MIT license. -Usage +Installation ----- + composer require akirk/php-ratelimiter +Usage +----- ```php -$rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"]); +$rateLimiter = new \Akirk\Ratelimiter\Ratelimiter(new \Memcache(), $_SERVER["REMOTE_ADDR"]); try { // allow a maximum of 100 requests for the IP in 5 minutes $rateLimiter->limitRequestsInMinutes(100, 5); -} catch (RateExceededException $e) { +} catch (\Akirk\Ratelimiter\RateExceededException $e) { header("HTTP/1.0 529 Too Many Requests"); exit; } @@ -30,10 +33,11 @@ If you want to protect multiple resources with different limits, use the third p ```php // script1.php -$rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"], "script1"); +$rateLimiter = new \Akirk\Ratelimiter\Ratelimiter(new \Memcache(), $_SERVER["REMOTE_ADDR"], "script1"); try { ... } + // script2.php -$rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"], "script2"); +$rateLimiter = new \Akirk\Ratelimiter\Ratelimiter(new \Memcache(), $_SERVER["REMOTE_ADDR"], "script2"); try { ... } ``` diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..aa0e671 --- /dev/null +++ b/composer.json @@ -0,0 +1,16 @@ +{ + "name": "akirk/php-ratelimiter", + "description": "A small class that uses Memcache to allow only a certain number of requests per a certain amount of minutes.", + "type": "library", + "license": "MIT", + "minimum-stability": "stable", + "require": { + "php": ">=5.5", + "ext-memcache": "*" + }, + "autoload": { + "psr-4": { + "Akirk\\Ratelimiter\\": "src/" + } + } +} diff --git a/ratelimiter.php b/src/RateExceededException.php similarity index 53% rename from ratelimiter.php rename to src/RateExceededException.php index 24bb875..e0f685c 100644 --- a/ratelimiter.php +++ b/src/RateExceededException.php @@ -1,4 +1,5 @@ memcache = $memcache; - $this->prefix = $prefix . $ip; - } - - public function limitRequestsInMinutes($allowedRequests, $minutes) { - $requests = 0; - - foreach ($this->getKeys($minutes) as $key) { - $requestsInCurrentMinute = $this->memcache->get($key); - if (false !== $requestsInCurrentMinute) $requests += $requestsInCurrentMinute; - } - - if (false === $requestsInCurrentMinute) { - $this->memcache->set($key, 1, 0, $minutes * 60 + 1); - } else { - $this->memcache->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; - } -} +namespace Akirk\Ratelimiter; + +use Exception; + +class RateExceededException extends Exception { } diff --git a/src/Ratelimiter.php b/src/Ratelimiter.php new file mode 100644 index 0000000..bef9614 --- /dev/null +++ b/src/Ratelimiter.php @@ -0,0 +1,77 @@ +memcache = $memcache; + $this->prefix = $prefix . $ip; + } + + public function limitRequestsInMinutes($allowedRequests, $minutes) + { + $requests = 0; + + foreach ($this->getKeys($minutes) as $key) { + $requestsInCurrentMinute = $this->memcache->get($key); + + if (false !== $requestsInCurrentMinute) { + $requests += $requestsInCurrentMinute; + } + } + + if (false === $requestsInCurrentMinute) { + $this->memcache->set($key, 1, 0, $minutes * 60 + 1); + } else { + $this->memcache->increment($key, 1); + } + + if ($requests > $allowedRequests) { + throw new RateExceededException; + } + } + + private function getKeys($minutes) + { + $keys = []; + + $now = time(); + for ($time = $now - $minutes * 60; $time <= $now; $time += 60) { + $keys[] = $this->prefix . date("dHi", $time); + } + + return $keys; + } +}