title: Voting Duplicate Votes
body: <p>We currently have a voting system setup that saves the IP address of the person making a vote to stop duplicate votes.</p>
  
  <p>However everyday the IP address is removed from the database so they can re-vote on there favourite item.</p>
  
  <p>Looking through the code below can you see how I can stop this so that a person with a certain IP address can never vote for that certain item again? They are however able to vote on the other entrants, but again only vote once.</p>
  
  <p>Any ideas?</p>
  
  <pre><code>&lt;?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-&gt;nrvot = NRVOT;
      if(defined('SVOTING')) $this-&gt;svoting = SVOTING;
      if(defined('USRVOTE') &amp;&amp; USRVOTE === 0) { if(defined('VOTER')) $this-&gt;voter = VOTER; }
      else $this-&gt;voter = $_SERVER['REMOTE_ADDR'];
      $this-&gt;tdy = date('j');
  
      // if set to use TXT files, set the path and name of the files
      if($this-&gt;svoting != 'mysql') {
        $this-&gt;votitems = '../votingtxt/'.$this-&gt;votitems.'.txt';
        $this-&gt;votusers = '../votingtxt/'.$this-&gt;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-&gt;setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
        self::$conn-&gt;exec('SET CHARACTER SET utf8');      // Sets encoding UTF-8
  
      }
      catch(PDOException $e) {
        $this-&gt;eror = 'Unable to connect to MySQL: '. $e-&gt;getMessage();
      }
    }
  
    // Performs SQL queries
    public function sqlExecute($sql) {
      if(self::$conn===false OR self::$conn===NULL) $this-&gt;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-&gt;prepare($sql)) {
            // execute query
            if($sqlprep-&gt;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-&gt;fetch(PDO::FETCH_ASSOC)) !== false){
                  do {
                    // check each column if it has numeric value, to convert it from "string"
                    foreach($row AS $k=&gt;$v) {
                      if(is_numeric($v)) $row[$k] = $v + 0;
                    }
                    $re[] = $row;
                  }
                  while($row = $sqlprep-&gt;fetch(PDO::FETCH_ASSOC));
                }
                $this-&gt;affected_rows = count($re);                   // number of returned rows
              }
            }
            else $this-&gt;eror = 'Cannot execute the sql query';
          }
          else {
            $eror = self::$conn-&gt;errorInfo();
            $this-&gt;eror = 'Error: '. $eror[2];
          }
        }
        catch(PDOException $e) {
          $this-&gt;eror = $e-&gt;getMessage();
        }
      }
  
      // sets to return false in case of error
      if($this-&gt;eror !== false) { echo $this-&gt;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-&gt;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-&gt;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-&gt;svoting == 'mysql') {
            $votstdy = array_unique(array_merge($votstdy, $this-&gt;votstdyDb()));
          }
          else {
            $all_votstdy = $this-&gt;votstdyTxt();     // get 2 array: 'all'-rows voted today, 'day'-items by voter today
            $votstdy = array_unique(array_merge($votstdy, $all_votstdy[$this-&gt;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-&gt;nrvot === 1 &amp;&amp; count($votstdy) &gt; 0)) {
            $votstdy[] = $items[0];
            setcookie("votings", implode(',', array_unique($votstdy)), strtotime('tomorrow'));
            return '{"'.$items[0].'":[0,0,3]}';
          }
          else if($this-&gt;svoting == 'mysql') $this-&gt;setVotDb($items, $vote, $votstdy);       // add the new vote in mysql
          else $this-&gt;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-&gt;nrvot === 1 &amp;&amp; count($votstdy) &gt; 0) ? 1 : 0;
  
      // get array with items and their votings from mysql or TXT file
      $votitems = ($this-&gt;svoting == 'mysql') ? $this-&gt;getVotDb($items, $votstdy, $setvoted) : $this-&gt;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-&gt;sqlExecute("INSERT INTO `$this-&gt;votitems` (`item`, `vote`) VALUES ('".$items[0]."', $vote) ON DUPLICATE KEY UPDATE `vote`=`vote`+$vote, `nvotes`=`nvotes`+1");
  
      $this-&gt;sqlExecute("DELETE FROM `$this-&gt;votusers` WHERE `day`!=$this-&gt;tdy");
  
      $this-&gt;sqlExecute("INSERT INTO `$this-&gt;votusers` (`day`, `voter`, `item`) VALUES ($this-&gt;tdy, '$this-&gt;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-&gt;sqlExecute("SELECT * FROM `$this-&gt;votitems` WHERE `item` IN(".implode(',', array_map('addSlhs', $items)).")");
      if($this-&gt;affected_rows &gt; 0) {
        for($i=0; $i&lt;$this-&gt;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-&gt;votitems)) {
        $rows = file($this-&gt;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 &gt; 0) {
          for($i=0; $i&lt;$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-&gt;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-&gt;tdy.'^'.$this-&gt;voter.'^'.$items[0];
      file_put_contents($this-&gt;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-&gt;tdy][] = $items[0];
      setcookie("votings", implode(',', array_unique($all_votstdy[$this-&gt;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-&gt;votitems)) {
        $rows = file($this-&gt;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 &gt; 0) {
          for($i=0; $i&lt;$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-&gt;sqlExecute("SELECT `item` FROM `$this-&gt;votusers` WHERE `day`=$this-&gt;tdy AND `voter`='$this-&gt;voter'");
      if($this-&gt;affected_rows &gt; 0) {
        for($i=0; $i&lt;$this-&gt;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-&gt;tdy] = array();
      if(file_exists($this-&gt;votusers)) {
        $rows = file($this-&gt;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 &gt; 0) {
          for($i=0; $i&lt;$nrrows; $i++) {
            $row = explode('^', $rows[$i]);
            if($row[0] == $this-&gt;tdy) {
              $re['all'][] = $rows[$i];
              if($row[1] == $this-&gt;voter) $re[$this-&gt;tdy][] = $row[2];
            }
          }
        }
      }
  
      return $re;
    }
  }
  </code></pre>