├── LICENSE ├── README.md └── ratelimiter.php /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Alexander Kirk 2 | http://alexander.kirk.at/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | php-ratelimiter 2 | =============== 3 | 4 | A small class that uses Memcache to allow only a certain number of requests per a certain amount of minutes. 5 | 6 | The class works around the problem that the timeframe is constantly moving, i.e. every new minute the timeframe is different. See my [blogpost](http://alexander.kirk.at/2013/04/19/add-a-rate-limit-to-your-website/). 7 | 8 | The code is released under an MIT license. 9 | 10 | Usage 11 | ----- 12 | 13 | ```php 14 | $rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"]); 15 | try { 16 | // allow a maximum of 100 requests for the IP in 5 minutes 17 | $rateLimiter->limitRequestsInMinutes(100, 5); 18 | } catch (RateExceededException $e) { 19 | header("HTTP/1.0 529 Too Many Requests"); 20 | exit; 21 | } 22 | ``` 23 | 24 | Remarks 25 | ------- 26 | 27 | The script creates a memcached entry per IP and minute. 28 | 29 | If you want to protect multiple resources with different limits, use the third parameter of the constructor to namespace it: 30 | 31 | ```php 32 | // script1.php 33 | $rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"], "script1"); 34 | try { ... } 35 | // script2.php 36 | $rateLimiter = new RateLimiter(new Memcache(), $_SERVER["REMOTE_ADDR"], "script2"); 37 | try { ... } 38 | ``` 39 | 40 | You can also use something else as a second parameter, for example a `session_id` to limit the requests per user instead of IP address. 41 | -------------------------------------------------------------------------------- /ratelimiter.php: -------------------------------------------------------------------------------- 1 | memcache = $memcache; 33 | $this->prefix = $prefix . $ip; 34 | } 35 | 36 | public function limitRequestsInMinutes($allowedRequests, $minutes) { 37 | $requests = 0; 38 | 39 | foreach ($this->getKeys($minutes) as $key) { 40 | $requestsInCurrentMinute = $this->memcache->get($key); 41 | if (false !== $requestsInCurrentMinute) $requests += $requestsInCurrentMinute; 42 | } 43 | 44 | if (false === $requestsInCurrentMinute) { 45 | $this->memcache->set($key, 1, 0, $minutes * 60 + 1); 46 | } else { 47 | $this->memcache->increment($key, 1); 48 | } 49 | 50 | if ($requests > $allowedRequests) throw new RateExceededException; 51 | } 52 | 53 | private function getKeys($minutes) { 54 | $keys = array(); 55 | $now = time(); 56 | for ($time = $now - $minutes * 60; $time <= $now; $time += 60) { 57 | $keys[] = $this->prefix . date("dHi", $time); 58 | } 59 | 60 | return $keys; 61 | } 62 | } 63 | --------------------------------------------------------------------------------