IPHitsLimit
1 <?php 2 /** 3 * @file IPHitsLimit.php 4 * @version 1.0 5 * @author guoxiaosong <> 6 * @date 2012-11-14 7 * @brief Limit ip frequency. 8 */ 9 class IPHitsLimit 10 { 11 const INDEX_DAY = 0; 12 const INDEX_HOUR = 1; 13 const INDEX_MINUTE = 2; 14 const INDEX_SECOND = 3; 15 // MemCahe link 16 private $_memCache; 17 // 1 day 18 private $_expiredTime = 86400; 19 private $_ipWhiteLists = array(); 20 private $_ipBlackLists = array(); 21 22 /** 23 * Constructor. 24 * 25 * @param boolean $init Initialize the shm or not. 26 */ 27 function __construct() 28 { 29 $this->_memCache = new MemCache; 30 $this->_memCache->pconnect(KOPU_MEMCACHED_HOST, KOPU_MEMCACHED_PORT); 31 32 $this->init(); 33 } 34 35 /** 36 * Initialize the shm. 37 * 38 */ 39 function init() 40 { 41 $this->_expiredTime = KOPU_IPHITS_EXPIRED_TIME; 42 $this->_ipWhiteLists = Yii::app()->params->ipwhitelists; 43 $this->_ipBlackLists = Yii::app()->params->ipblacklists; 44 } 45 46 /** 47 * Check whether to limit $ip. 48 * 49 * @param string $ip The ip to check. 50 * @return int 0: no limit, 1: limit it. 51 */ 52 53 public function check($ip) 54 { 55 $return = $this->_check($ip); 56 if ($return == 1) { 57 Yii::log($ip, CLogger::LEVEL_INFO, 'IPHitsLimit'); 58 } 59 return $return; 60 } 61 62 private function _check($ip) 63 { 64 if (!($key = $this->_checkIP($ip)) || $this->_checkIPBlackList($ip)) { 65 return 1; 66 } 67 68 if ($this->_checkIPWhiteList($ip)) { 69 return 0; 70 } 71 72 $info = $this->_memCache->get($key); 73 $time = time(); 74 if (empty($info)) {// new record 75 $info = array( 76 'time' => $time, 77 'count' => array(1, 1, 1, 1), 78 'ip' => $ip, 79 ); 80 $this->_memCache->set($key, $info, MEMCACHE_COMPRESSED, $this->_expiredTime); 81 return 0; 82 } else { 83 $countTimeMap = $this->countTimeMap(); 84 $info['count'] = $this->_addCount($info['count']); 85 if ($this->_campareTime($time, $info['time'], 'day')) { 86 // IP hits up to day limits. 87 if ($info['count'][self::INDEX_DAY] > $countTimeMap[self::INDEX_DAY]) { 88 $info['time'] = $time; 89 $this->_memCache->set($key, $info); 90 return 1; 91 } 92 93 if ($this->_campareTime($time, $info['time'], 'hour')) { 94 // Ip hits up to hour limits. 95 if ($info['count'][self::INDEX_HOUR] > $countTimeMap[self::INDEX_HOUR]) { 96 $info['time'] = $time; 97 $this->_memCache->set($key, $info); 98 return 1; 99 } 100 101 if ($this->_campareTime($time, $info['time'], 'minute')) { 102 // Ip hits up to minute limits 103 if ($info['count'][self::INDEX_MINUTE] > $countTimeMap[self::INDEX_MINUTE]) { 104 $info['time'] = $time; 105 $this->_memCache->set($key, $info); 106 return 1; 107 } 108 109 if ($this->_campareTime($time, $info['time'], 'second')) { 110 //Ip hits up to second limits. 111 if ($info['count'][self::INDEX_SECOND] > $countTimeMap[self::INDEX_SECOND]) { 112 $info['time'] = $time; 113 $this->_memCache->set($key, $info); 114 return 1; 115 } 116 } else { 117 $info['count'][self::INDEX_SECOND] = 1; 118 } 119 } else { 120 $info['count'][self::INDEX_MINUTE] = 1; 121 $info['count'][self::INDEX_SECOND] = 1; 122 } 123 } else { 124 // new hour need to reset day,minute,second count. 125 $info['count'][self::INDEX_HOUR] = 1; 126 $info['count'][self::INDEX_MINUTE] = 1; 127 $info['count'][self::INDEX_SECOND] = 1; 128 } 129 } else { 130 // new day need to reset all count. 131 $info['count'] = array(1, 1, 1, 1); 132 } 133 $info['time'] = $time; 134 $this->_memCache->set($key, $info); 135 return 0; 136 } 137 138 } 139 140 /** 141 * @return array 142 * key(seconds) 143 * value(limits) 144 */ 145 function countTimeMap() 146 { 147 return array( 148 self::INDEX_DAY => KOPU_IPHITS_PER_DAY, 149 self::INDEX_HOUR => KOPU_IPHITS_PER_HOUR, 150 self::INDEX_MINUTE => KOPU_IPHITS_PER_MINUTE, 151 self::INDEX_SECOND => KOPU_IPHITS_PER_SECOND 152 ); 153 } 154 155 /** 156 * Print ip info in shm. 157 * 158 * @param string $ip The ip. 159 */ 160 function dump($ip) 161 { 162 $key = ip2long($ip); 163 if ($key == -1 || $key === FALSE) {// Invalid IP 164 return; 165 } 166 $info = $this->_memCache->get($key); 167 print_r($info); 168 return $info; 169 } 170 171 /** 172 * @return mixed 173 */ 174 private function _addCount(array $count, $key = NULL) 175 { 176 if ($key == NULL || !isset($count[$key])) { 177 foreach ($count as &$v) { 178 $v++; 179 } 180 return $count; 181 } else { 182 $count[$key]++; 183 return $count; 184 } 185 } 186 187 private function _campareTime($t1, $t2, $type = 'day') 188 { 189 switch($type) { 190 case 'day' : 191 return (date('Y-m-d', $t1) == date('Y-m-d', $t2)); 192 break; 193 case 'hour' : 194 return (date('Y-m-d H:00:00', $t1) == date('Y-m-d H:00:00', $t2)); 195 break; 196 case 'minute' : 197 return (date('Y-m-d H:i:00', $t1) == date('Y-m-d H:i:00', $t2)); 198 break; 199 case 'second' : 200 return $t1 == $t2; 201 break; 202 default : 203 return false; 204 break; 205 } 206 } 207 208 private function _checkIP($ip) 209 { 210 if (empty($ip)) { 211 return false; 212 } 213 $key = ip2long($ip); 214 if ($key == -1 || $key === FALSE) {// Invalid IP 215 return false; 216 } 217 return $key; 218 } 219 220 private function _checkIPWhiteList($ip) 221 { 222 return $this->isIpMatched($ip, $this->_ipWhiteLists); 223 } 224 225 private function _checkIPBlackList($ip) 226 { 227 return $this->isIpMatched($ip, $this->_ipBlackLists); 228 } 229 230 /** 231 * @param string $ip the IP address 232 * @return boolean whether the rule applies to the IP address 233 */ 234 protected function isIpMatched($ip, $ips) 235 { 236 if(empty($ips)) 237 return false; 238 foreach($ips as $rule) 239 { 240 if($rule==='*' || $rule===$ip || (($pos=strpos($rule,'*'))!==false && !strncmp($ip,$rule,$pos))) 241 return true; 242 } 243 return false; 244 } 245 246 } 247 ?>