2015-08-20 41 views
1

我正在研究一個類,其目的是限制用戶在任何30秒內只發出10個請求。它利用一個文件來維護IP地址,最後一次請求時間。以及他們嘗試的次數。問題是,無論我嘗試什麼,我都無法獲取文件大小。我試過使用clearstatcache(),並且我嘗試過使用我在PHP手冊的filesize()頁面的註釋中找到的函數。無法獲得實際的文件大小

這是代碼,它是當前的調試狀態。

// Makes sure user can only try to generate a coupon x number of times over x amount of seconds 
class IpChecker{  
    const WAIT_TIME = 30; //seconds until user can try again 
    const MAX_TRIES = 10; // maximum tries 

    const COUPON_IP = 0; 
    const COUPON_TIME = 1; 
    const COUPON_TRIES = 2; 

    private $ip_data; 
    private $path; 
    private $fh; 
    private $safe; 

    public function __construct(){ 
     clearstatcache(); 

     $this->path = realpath(dirname(__FILE__))."/ips/.ips"; 
     $this->fh = fopen($this->path,'w+'); 
     $this->filesize = $this->realfilesize($this->fh); 
     echo "fs: ".$this->filesize; exit; 


     $this->getIPs(); 
     $this->checkIP(); 
     $this->logRequest(); 
     fclose($this->fh); 
     $this->safe || die(json_encode("You have exhausted all available tries. Please try again later.")); 
    } 

    private function logRequest(){ 
     $str = ""; 
     foreach($this->ip_data as $data){ 
      foreach($data as $col){ 
       if(self::WAIT_TIME < (time() - $col[self::COUPON_TIME])) $str .= $col."\t"; 
      } 
      $str = rtrim($str, '\t'); 
      $str .= "\n"; 
     } 
     $str = rtrim($str, '\n'); 
     try{ 
      $fw = fwrite($this->fh, $str) || die(json_encode("Unable to check IP")); 
     }catch(Exception $e){ 
      die(json_encode($e)); 
     } 
    } 

    private function checkIP(){  
     $IP = $_SERVER['REMOTE_ADDR']; 
     $TIME = time(); 
     $safe = true; 
     $user_logged = false; 
     echo "<pre>"; var_dump($this->ip_data); exit; 
     foreach($this->ip_data as $key=>$data){ 
      echo "<prE>"; var_dump($data); exit; 
//   if($data[$key][self::COUPON_IP] == $IP){ 
//    $user_logged = true; 
//    if(
//     (($TIME - $data[$key][self::COUPON_TIME]) < self::WAIT_TIME) || 
//     (self::MAX_TRIES >= $data[$key][self::COUPON_TRIES]) 
//    ) $safe = false; 
//    $this->ip_data[$key][self::COUPON_TRIES] = $this->ip_data[$key][self::COUPON_TRIES]+1; 
//    $this->ip_data[$key][self::COUPON_TIME] = $TIME; 
//   } 
     } 
     if(!$user_logged){ 
      die("user not logged"); 
      $this->ip_data[] = array(
       self::COUPON_IP => $IP, 
       self::COUPON_TIME => $TIME, 
       self::COUPON_TRIES => 1 
      ); 
     } 
     $this->safe = $safe; 
    } 

    private function getIPs(){ 
     $IP_DATA = array(); 
     echo file_get_contents($this->path); exit; 
     // this always returns 0. 
     $size = filesize($this->path); 
     echo "filesize: ".$size; exit; 
     if($size){ 
      $IPs = fread($this->fh,$size); 
      $IP_ARR = explode("\n",$IPs); 
      foreach($IP_ARR as $line) $IP_DATA[] = explode("\t",$line); 
     } 
     $this->ip_data = $IP_DATA; 
    } 

    // Copied from the comments in the PHP Manual for filesize() 
    public function realfilesize($fp) { 
     $return = false; 
     if (is_resource($fp)) { 
      if (PHP_INT_SIZE < 8) { 
       // 32bit 
       if (0 === fseek($fp, 0, SEEK_END)) { 
        $return = 0.0; 
        $step = 0x7FFFFFFF; 
        while ($step > 0) { 
         if (0 === fseek($fp, - $step, SEEK_CUR)) { 
          $return += floatval($step); 
         } else { 
          $step >>= 1; 
         } 
        } 
       } 
      } elseif (0 === fseek($fp, 0, SEEK_END)) { 
       // 64bit 
       $return = ftell($fp); 
      } 
     } 
     return $return; 
    } 
} 

我怎樣才能得到真正的文件大小?我在PHP 5.2上。

+0

統計緩存是每個進程。腳本中的每個命中都是一個單獨的過程,因此除非在SAME腳本過程中多次執行基於統計的操作,否則在清除統計緩存方面沒有任何意義。 –

+0

@MarcB,我意識到這一點,但afaik,它也不會傷害anythign,我越來越絕望,所以我想我會給它一個鏡頭。 –

+1

並注意IP從來不是一種有效的方式來識別單個用戶。例如考慮到幾乎每一部手機都在攻擊NAT網關。這意味着你將大量用戶視爲一個人,並將他們限制爲一個羣體,而不是個人。 –

回答

0

我沒;噸能夠弄清楚什麼是錯我的代碼,如果anythign,但我工作圍繞它像這樣:

/** 
* Makes sure user can only access a given page 
* x number of times over x amount of seconds. 
* If multiple instances of this class are used, the $namespace 
* properties for each should be unique. 
* Default wait time is 90 seconds while default request limit 
* is 10 tries. 
* 
* -+ Usage +- 
* Just create the object with any parameters, no need to assign, 
* just make sure it's initialized at the top of the page: 
* new RequestThrottler; 
* 
* -+- Parameters -+- 
* null RequestThrottler ([ string $namespace [, int $WaitTime [, int $MaxTries ] ] ]) 
*/ 
class RequestThrottler{ 
    // settings 
    private static $WAIT_TIME; // seconds until count expires 
    private static $MAX_TRIES; // maximum tries 

    // used to keep session variables unique 
    // in the event that this class is used in multiple places. 
    private $namespace; 

    // for debugging 
    const DBG = false; 

    // array index constants 
    const _TIME = 0; 
    const _TRIES = 1; 

    // defines whether or not access is permitted 
    private $safe; 

    // seconds until reset 
    private $secs; 

    /** 
    * -+- Constructor -+- 
    * @param String $namespace - A unique prefix for SESSION data 
    * @param Int $WaitTime - Total seconds before user can try again 
    * @param Int $MaxTries - Total tries user can make until their request is denied 
    */ 
    public function __construct($namespace='Throttler', $WaitTime=90, $MaxTries=10){ 
     // make sure a session is available 
     if(!headers_sent() && !isset($_SESSION)) session_start(); 
     if(!isset($_SESSION)) die(json_encode("No session available")); 

     // save settings 
     $this->namespace = $namespace; 
     self::$MAX_TRIES = $MaxTries; 
     self::$WAIT_TIME = $WaitTime; 

     // do the footwork 
     $this->checkHistory(); 

     // if set to debug mode, print a short helpful string 
     if(self::DBG) die(json_encode(
       "You are ".($this->safe ? 'SAFE' : 'NOT SAFE')."! " 
       . "This is try number {$_SESSION[$this->namespace.'_ATTEMPTS'][self::_TRIES]} of ".self::$MAX_TRIES.". " 
       . $this->secs." seconds since last attempt. " 
       . (self::$WAIT_TIME - $this->secs)." seconds until reset." 
      )); 

     // if not safe, kill the script, show a message 
     $this->safe || die(json_encode("You're going too fast. Please try again in ".(self::$WAIT_TIME - $this->secs)." seconds.")); 
    } 

    /** 
    * -+- checkHistory -+- 
    * Does the footwork to determine whether 
    * or not to throttle the current user/request. 
    */ 
    private function checkHistory(){ 
     $TIME = time(); 
     $safe = true; 

     // make sure session is iniitialized 
     if(!isset($_SESSION[$this->namespace.'_ATTEMPTS']) || 
      !isset($_SESSION[$this->namespace.'_ATTEMPTS'][self::_TRIES]) || 
      !isset($_SESSION[$this->namespace.'_ATTEMPTS'][self::_TIME]) ) 
       $_SESSION[$this->namespace.'_ATTEMPTS'] = array(
        self::_TIME =>$TIME, 
        self::_TRIES => 1 
       ); 
     else $_SESSION[$this->namespace.'_ATTEMPTS'][self::_TRIES] = 
       $_SESSION[$this->namespace.'_ATTEMPTS'][self::_TRIES]+1; 

     // get seconds since last attempt 
     $secondSinceLastAttempt = $TIME - $_SESSION[$this->namespace.'_ATTEMPTS'][self::_TIME]; 

     // reset the counter if the wait time has expired 
     if($secondSinceLastAttempt > self::$WAIT_TIME){ 
      $_SESSION[$this->namespace.'_ATTEMPTS'][self::_TIME] = $TIME; 
      $_SESSION[$this->namespace.'_ATTEMPTS'][self::_TRIES] = 1; 
      $secondSinceLastAttempt = 0; 
     } 

     // finally, determine if we're safe 
     if($_SESSION[$this->namespace.'_ATTEMPTS'][self::_TRIES] >= self::$MAX_TRIES) $safe=false; 

     // log this for debugging 
     $this->secs = $secondSinceLastAttempt; 

     // save the "safe" flag 
     $this->safe = $safe; 
    } 
}