File: //proc/self/cwd/guiaweb/htdocs/includes/restler/framework/Luracast/Restler/Filter/RateLimit.php
<?php
namespace Luracast\Restler\Filter;
use Luracast\Restler\Defaults;
use Luracast\Restler\iFilter;
use Luracast\Restler\iUseAuthentication;
use Luracast\Restler\RestException;
/**
* Describe the purpose of this class/interface/trait
*
* @category Framework
* @package restler
* @author R.Arul Kumaran <arul@luracast.com>
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
*
*/
class RateLimit implements iFilter, iUseAuthentication
{
/**
* @var \Luracast\Restler\Restler;
*/
public $restler;
/**
* @var int
*/
public static $usagePerUnit = 1200;
/**
* @var int
*/
public static $authenticatedUsagePerUnit = 5000;
/**
* @var string
*/
public static $unit = 'hour';
/**
* @var string group the current api belongs to
*/
public static $group = 'common';
protected static $units = array(
'second' => 1,
'minute' => 60,
'hour' => 3600, // 60*60 seconds
'day' => 86400, // 60*60*24 seconds
'week' => 604800, // 60*60*24*7 seconds
'month' => 2592000, // 60*60*24*30 seconds
);
/**
* @var array all paths beginning with any of the following will be excluded
* from documentation
*/
public static $excludedPaths = array('explorer');
/**
* @param string $unit
* @param int $usagePerUnit
* @param int $authenticatedUsagePerUnit set it to false to give unlimited access
*
* @throws \InvalidArgumentException
* @return void
*/
public static function setLimit(
$unit, $usagePerUnit, $authenticatedUsagePerUnit = null
)
{
static::$unit = $unit;
static::$usagePerUnit = $usagePerUnit;
static::$authenticatedUsagePerUnit =
is_null($authenticatedUsagePerUnit) ? $usagePerUnit : $authenticatedUsagePerUnit;
}
public function __isAllowed()
{
if (static::$authenticatedUsagePerUnit
== static::$usagePerUnit
) return $this->check();
return null;
}
public function __setAuthenticationStatus($isAuthenticated = false)
{
header('X-Auth-Status: ' . ($isAuthenticated ? 'true' : 'false'));
$this->check($isAuthenticated);
}
private static function validate($unit)
{
if (!isset(static::$units[$unit]))
throw new \InvalidArgumentException(
'Rate Limit time unit should be '
. implode('|', array_keys(static::$units)) . '.'
);
}
private function check($isAuthenticated = false)
{
$path = $this->restler->url;
foreach (static::$excludedPaths as $exclude) {
if (empty($exclude) && empty($path)) {
return true;
} elseif (0 === strpos($path, $exclude)) {
return true;
}
}
static::validate(static::$unit);
$timeUnit = static::$units[static::$unit];
$maxPerUnit = $isAuthenticated
? static::$authenticatedUsagePerUnit
: static::$usagePerUnit;
if ($maxPerUnit) {
$user = Defaults::$userIdentifierClass;
if (!method_exists($user, 'getUniqueIdentifier')) {
throw new \UnexpectedValueException('`Defaults::$userIdentifierClass` must implement `iIdentifyUser` interface');
}
$id = "RateLimit_" . $maxPerUnit . '_per_' . static::$unit
. '_for_' . static::$group
. '_' . $user::getUniqueIdentifier();
$lastRequest = $this->restler->cache->get($id, true)
? : array('time' => 0, 'used' => 0);
$time = $lastRequest['time'];
$diff = time() - $time; # in seconds
$used = $lastRequest['used'];
header("X-RateLimit-Limit: $maxPerUnit per " . static::$unit);
if ($diff >= $timeUnit) {
$used = 1;
$time = time();
} elseif ($used >= $maxPerUnit) {
header("X-RateLimit-Remaining: 0");
$wait = $timeUnit - $diff;
sleep(1);
throw new RestException(429,
'Rate limit of ' . $maxPerUnit . ' request' .
($maxPerUnit > 1 ? 's' : '') . ' per '
. static::$unit . ' exceeded. Please wait for '
. static::duration($wait) . '.'
);
} else {
$used++;
}
$remainingPerUnit = $maxPerUnit - $used;
header("X-RateLimit-Remaining: $remainingPerUnit");
$this->restler->cache->set($id,
array('time' => $time, 'used' => $used));
}
return true;
}
private function duration($secs)
{
$units = array(
'week' => (int)($secs / 86400 / 7),
'day' => $secs / 86400 % 7,
'hour' => $secs / 3600 % 24,
'minute' => $secs / 60 % 60,
'second' => $secs % 60);
$ret = array();
//$unit = 'days';
foreach ($units as $k => $v) {
if ($v > 0) {
$ret[] = $v > 1 ? "$v {$k}s" : "$v $k";
//$unit = $k;
}
}
$i = count($ret) - 1;
if ($i) {
$ret[$i] = 'and ' . $ret[$i];
}
return implode(' ', $ret); //." $unit.";
}
}