2012-09-27 163 views
1

我們目前有一個投票系統設置,可以保存投票人停止重複投票的IP地址。投票重複投票

但是每天都會從數據庫中刪除IP地址,以便他們可以重新投票那裏最喜歡的項目。

通過下面的代碼,你可以看到我如何阻止這種情況,使得某個具有特定IP地址的人無法再對該特定項目投票?然而,他們可以對其他參賽者投票,但只能再投一次。

任何想法?

<?php 
// Script Voting - http://www.coursesweb.net/ 
class Voting { 
    // properties 
    static protected $conn = false;   // stores the connection to mysql 
    public $affected_rows = 0;  // number of affected, or returned rows in SQL query 
    protected $voter = '';     // the user who vote, or its IP 
    protected $nrvot = 0;     // if it is 1, the user can vote only one item in a day, 0 for multiple items 
    protected $svoting = 'mysql';   // 'mysql' to register data in database, any other value register in TXT files 
    public $votitems = 'voting';  // Table /or file_name to store items that are voted 
    public $votusers = 'votusers';    // Table /or filename that stores the users who voted in current day 
    protected $tdy;    // will store the number of current day 
    public $eror = false;   // to store and check for errors 

    // constructor 
    public function __construct() { 
    // sets $nrvot, $svoting, $voter, and $tdy properties 
    if(defined('NRVOT')) $this->nrvot = NRVOT; 
    if(defined('SVOTING')) $this->svoting = SVOTING; 
    if(defined('USRVOTE') && USRVOTE === 0) { if(defined('VOTER')) $this->voter = VOTER; } 
    else $this->voter = $_SERVER['REMOTE_ADDR']; 
    $this->tdy = date('j'); 

    // if set to use TXT files, set the path and name of the files 
    if($this->svoting != 'mysql') { 
     $this->votitems = '../votingtxt/'.$this->votitems.'.txt'; 
     $this->votusers = '../votingtxt/'.$this->votusers.'.txt'; 
    } 
    } 

    // for connecting to mysql 
    protected function setConn() { 
    try { 
     // Connect and create the PDO object 
     self::$conn = new PDO("mysql:host=".DBHOST."; dbname=".DBNAME, DBUSER, DBPASS); 

     // Sets to handle the errors in the ERRMODE_EXCEPTION mode 
     self::$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

     self::$conn->exec('SET CHARACTER SET utf8');  // Sets encoding UTF-8 

    } 
    catch(PDOException $e) { 
     $this->eror = 'Unable to connect to MySQL: '. $e->getMessage(); 
    } 
    } 

    // Performs SQL queries 
    public function sqlExecute($sql) { 
    if(self::$conn===false OR self::$conn===NULL) $this->setConn();  // sets the connection to mysql 
    $re = true;   // the value to be returned 

    // if there is a connection set ($conn property not false) 
    if(self::$conn !== false) { 
     // gets the first word in $sql, to determine whenb SELECT query 
     $ar_mode = explode(' ', trim($sql), 2); 
     $mode = strtolower($ar_mode[0]); 

     // performs the query and get returned data 
     try { 
     if($sqlprep = self::$conn->prepare($sql)) { 
      // execute query 
      if($sqlprep->execute()) { 
      // if $mode is 'select', gets the result_set to return 
      if($mode == 'select') { 
       $re = array(); 
       // if fetch() returns at least one row (not false), adds the rows in $re for return 
       if(($row = $sqlprep->fetch(PDO::FETCH_ASSOC)) !== false){ 
       do { 
        // check each column if it has numeric value, to convert it from "string" 
        foreach($row AS $k=>$v) { 
        if(is_numeric($v)) $row[$k] = $v + 0; 
        } 
        $re[] = $row; 
       } 
       while($row = $sqlprep->fetch(PDO::FETCH_ASSOC)); 
       } 
       $this->affected_rows = count($re);     // number of returned rows 
      } 
      } 
      else $this->eror = 'Cannot execute the sql query'; 
     } 
     else { 
      $eror = self::$conn->errorInfo(); 
      $this->eror = 'Error: '. $eror[2]; 
     } 
     } 
     catch(PDOException $e) { 
     $this->eror = $e->getMessage(); 
     } 
    } 

    // sets to return false in case of error 
    if($this->eror !== false) { echo $this->eror; $re = false; } 
    return $re; 
    } 

    // returns JSON string with item:['vote', 'nvotes', renot] for each element in $items array 
    public function getVoting($items, $vote = '') { 
    $votstdy = $this->votstdyCo($items);  // gets from Cookie array with items voted by the user today 

    // if $vote not empty, perform to register the vote, $items contains one item to vote 
    if(!empty($vote)) { 
     // if $voter empty means user not loged 
     if($this->voter === '') return "alert('Vote Not registered.\\nYou must be logged in to can vote')"; 
     else { 
     // gets array with items voted today from mysql, or txt-files (according to $svoting), and merge unique to $votstdy 
     if($this->svoting == 'mysql') { 
      $votstdy = array_unique(array_merge($votstdy, $this->votstdyDb())); 
     } 
     else { 
      $all_votstdy = $this->votstdyTxt();  // get 2 array: 'all'-rows voted today, 'day'-items by voter today 
      $votstdy = array_unique(array_merge($votstdy, $all_votstdy[$this->tdy])); 
     } 

     // if already voted, add in cookie, returns JSON from which JS alert message and will reload the page 
     // else, accesses the method to add the new vote, in mysql or TXT file 
     if(in_array($items[0], $votstdy) || ($this->nrvot === 1 && count($votstdy) > 0)) { 
      $votstdy[] = $items[0]; 
      setcookie("votings", implode(',', array_unique($votstdy)), strtotime('tomorrow')); 
      return '{"'.$items[0].'":[0,0,3]}'; 
     } 
     else if($this->svoting == 'mysql') $this->setVotDb($items, $vote, $votstdy);  // add the new vote in mysql 
     else $this->setVotTxt($items, $vote, $all_votstdy);   // add the new vote, and voter in TXT files 

     array_push($votstdy, $items[0]);  // adds curent item as voted 
    } 
    } 

    // if $nrvot is 1, and $votstdy has item, set $setvoted=1 (user already voted today) 
    // else, user can vote multiple items, after Select is checked if already voted the existend $item 
    $setvoted = ($this->nrvot === 1 && count($votstdy) > 0) ? 1 : 0; 

    // get array with items and their votings from mysql or TXT file 
    $votitems = ($this->svoting == 'mysql') ? $this->getVotDb($items, $votstdy, $setvoted) : $this->getVotTxt($items, $votstdy, $setvoted); 

    return json_encode($votitems); 
    } 

    // insert /update rating item in #votitems, delete rows in $votusers which are not from today, insert $voter in $votusers 
    protected function setVotDb($items, $vote, $votstdy) { 
    $this->sqlExecute("INSERT INTO `$this->votitems` (`item`, `vote`) VALUES ('".$items[0]."', $vote) ON DUPLICATE KEY UPDATE `vote`=`vote`+$vote, `nvotes`=`nvotes`+1"); 

    $this->sqlExecute("DELETE FROM `$this->votusers` WHERE `day`!=$this->tdy"); 

    $this->sqlExecute("INSERT INTO `$this->votusers` (`day`, `voter`, `item`) VALUES ($this->tdy, '$this->voter', '".$items[0]."')"); 

    // add curent voted item to the others today, and save them as string ',' in cookie (till the end of day) 
    $votstdy[] = $items[0]; 
    setcookie("votings", implode(',', array_unique($votstdy)), strtotime('tomorrow')); 
    } 

    // select 'vote' and 'nvotes' of each element in $items, $votstdy stores items voted by the user today 
    // returns array with item:['vote', 'nvotes', renot] for each element in $items array 
    protected function getVotDb($items, $votstdy, $setvoted) { 
    $re = array_fill_keys($items, array(0,0,$setvoted)); // makes each value of $items as key with an array(0,0,0) 

    function addSlhs($elm){return "'".$elm."'";}  // function to be used in array_map(), adds "'" to each $elm 
    $resql = $this->sqlExecute("SELECT * FROM `$this->votitems` WHERE `item` IN(".implode(',', array_map('addSlhs', $items)).")"); 
    if($this->affected_rows > 0) { 
     for($i=0; $i<$this->affected_rows; $i++) { 
     $voted = in_array($resql[$i]['item'], $votstdy) ? $setvoted + 1 : $setvoted; // add 1 if the item was voted by the user today 
     $re[$resql[$i]['item']] = array($resql[$i]['vote'], $resql[$i]['nvotes'], $voted); 
     } 
    } 

    return $re; 
    } 

    // add /update rating item in TXT file, keep rows from today in $votusers, and add new row with $voter 
    protected function setVotTxt($items, $vote, $all_votstdy) { 
    // get the rows from file with items, if exists 
    if(file_exists($this->votitems)) { 
     $rows = file($this->votitems, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 
     $nrrows = count($rows); 

     // if exist rows registered, get array for each row, with - item^vote^nvotes 
     // if row with item, update it and stop, else, add the row at the end 
     if($nrrows > 0) { 
     for($i=0; $i<$nrrows; $i++) { 
      $row = explode('^', $rows[$i]); 
      if($row[0] == $items[0]) { 
      $rows[$i] = $items[0].'^'.($row[1] + $vote).'^'.($row[2] + 1); 
      $rowup = 1; break; 
      } 
     } 
     } 
    } 
    if(!isset($rowup)) $rows[] = $items[0].'^'.$vote.'^1'; 

    file_put_contents($this->votitems, implode(PHP_EOL, $rows));  // save the items in file 

    // add row with curent item voted and the voter (today^voter^item), and save all the rows 
    $all_votstdy['all'][] = $this->tdy.'^'.$this->voter.'^'.$items[0]; 
    file_put_contents($this->votusers, implode(PHP_EOL, $all_votstdy['all'])); 

    // add curent voted item to the others today, and save them as string ',' in cookie (till the end of day) 
    $all_votstdy[$this->tdy][] = $items[0]; 
    setcookie("votings", implode(',', array_unique($all_votstdy[$this->tdy])), strtotime('tomorrow')); 
    } 

    // get from TXT 'vote' and 'nvotes' of each element in $items, $votstdy stores items voted by the user today 
    // returns array with item:['vote', 'nvotes', renot] for each element in $items array 
    protected function getVotTxt($items, $votstdy, $setvoted) { 
    $re = array_fill_keys($items, array(0,0,$setvoted)); // makes each value of $items as key with an array(0,0,0) 

    if(file_exists($this->votitems)) { 
     $rows = file($this->votitems, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 
     $nrrows = count($rows); 

     // if exist rows registered, get array for each row, with - item^vote^nvotes 
     // if row with item is in $items, add its data in $re 
     if($nrrows > 0) { 
     for($i=0; $i<$nrrows; $i++) { 
      $row = explode('^', $rows[$i]); 
      $voted = in_array($row[0], $votstdy) ? $setvoted + 1 : $setvoted; // add 1 if the item was voted by the user today 
      if(in_array($row[0], $items)) $re[$row[0]] = array($row[1], $row[2], $voted); 
     } 
     } 
    } 

    return $re; 
    } 

    // gets and returns from Cookie an array with items voted by the user ($voter) today 
    protected function votstdyCo() { 
    $votstdy = array(); 

    // if exists cookie 'votings', adds items voted today in $votstdy (array_filter() - removes null, empty elements) 
    if(isset($_COOKIE['votings'])) { 
     $votstdy = array_filter(explode(',', $_COOKIE['votings']));  // cookie stores string with: item1, item2, ... 
    } 

    return $votstdy; 
    } 

    // returns from mysql an array with items voted by the user today 
    protected function votstdyDb() { 
    $votstdy = array(); 
    $resql = $this->sqlExecute("SELECT `item` FROM `$this->votusers` WHERE `day`=$this->tdy AND `voter`='$this->voter'"); 
    if($this->affected_rows > 0) { 
     for($i=0; $i<$this->affected_rows; $i++) { 
     $votstdy[] = $resql[$i]['item']; 
     } 
    } 

    return $votstdy; 
    } 

    // returns from TXT file an array with 2 arrays: all rows voted today, and items voted by the user today 
    protected function votstdyTxt() { 
    $re['all'] = array(); $re[$this->tdy] = array(); 
    if(file_exists($this->votusers)) { 
     $rows = file($this->votusers, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); 
     $nrrows = count($rows); 

     // if exist rows registered, get array for each row, with - day^voter^item , compare 'day', and 'voter' 
     if($nrrows > 0) { 
     for($i=0; $i<$nrrows; $i++) { 
      $row = explode('^', $rows[$i]); 
      if($row[0] == $this->tdy) { 
      $re['all'][] = $rows[$i]; 
      if($row[1] == $this->voter) $re[$this->tdy][] = $row[2]; 
      } 
     } 
     } 
    } 

    return $re; 
    } 
} 

回答

0

創建要禁止的IP地址的SQL數據庫,或者如果列表足夠短,我們可以使用數組。

檢查IP地址是否在數據庫或數組中。如果不允許他們投票。然而,如果讓他們認爲他們投票將其放入數據庫並進行投票,但將投票標記爲不計,因此他們不會試圖繞過您的區塊。

+0

謝謝對我來說,我可以看到你在看什麼,當然看起來像我需要的東西,但是我不確定如何去解決這個問題,還有什麼想法? – craigclicky

+0

創建條目的數據庫。選擇 *。 foreach結果,如果$ _SERVER ['REMOTE_ADDR'] == $行的foreach – Iscariot

0

這個功能似乎刪除無法從「今天」票:

// insert /update rating item in #votitems, delete rows in $votusers which are not from today, insert $voter in $votusers 
    protected function setVotDb($items, $vote, $votstdy) { 
     $this->sqlExecute("INSERT INTO `$this->votitems` (`item`, `vote`) VALUES ('".$items[0]."', $vote) ON DUPLICATE KEY UPDATE `vote`=`vote`+$vote, `nvotes`=`nvotes`+1"); 

     //$this->sqlExecute("DELETE FROM `$this->votusers` WHERE `day`!=$this->tdy"); 

     $this->sqlExecute("INSERT INTO `$this->votusers` (`day`, `voter`, `item`) VALUES ($this->tdy, '$this->voter', '".$items[0]."')"); 

     // add curent voted item to the others today, and save them as string ',' in cookie (till the end of day) 
     $votstdy[] = $items[0]; 
     setcookie("votings", implode(',', array_unique($votstdy)), strtotime('tomorrow')); 
    } 

我註釋掉刪除查詢。在投入生產之前,您應該對其進行測試。

+0

嗨韋恩,謝謝你回到我身邊!這顯然會停止從「今天」中刪除投票,但是這會將IP地址保留在數據庫中,並且不允許再次用於該特定投票嗎?我可以測試這一點,但因爲它現在活着,我想看看這是否理論上如何運作。 – craigclicky

0

你的數據庫結構似乎有點混亂,你有一個表來計算每個項目的投票數和一個表來計算每個用戶的投票數。

您應該檢查您的數據庫結構,將這兩個表合併到一個表中:item,user,vote字段。

所以,你可以,只要你想保持一個記錄每一個用戶對每一個項目的每票和eventualy使計數()或SUM()請求得到總的每個項目的票數爲取回