Undefined offset: 0 in first PHP app - php

I'm currently building my first PHP application. I want to read in bus times from a csv file and give back ti users the next bus from their residence to our university. It is my first try with PHP, so be gentle:
<?php
// configuration
require("../includes/config.php");
if (!empty($_SESSION["id"]))
{
//lookup database entries for house
$users = query("SELECT * FROM users WHERE id = ?", $_SESSION["id"]);
if ($users === false)
{
apologize("Sorry, there was an error");
}
//lookup database entries for house
$residences = query("SELECT * FROM residences WHERE id = ?", $users[0]["residence"]);
if ($residences === false)
{
apologize("Sorry, there was an error");
}
//if user specified a residence in his profile
if($residences[0]["id"] != 0)
{
$times = array();
//if there is no bus today, in this case sat and sun
if(date( "w", $timestamp) == 0 || date( "w", $timestamp) == 6)
{
$times[0] = "There is no bus today";
}
//load the busplan for his residence
else
{
//open file and load in array if time is higher than date("His");
$timesList = file_get_contents($users[0]["residence"] . ".csv");
$nextbuses = explode(',', $timesList);
$hoursMins = date("Gi");
$num = 0;
for($i = 0; $i < count($nextbuses); $i++)
{
if($hoursMins < $nextbuses[$i])
{
$times[$num] = $nextbuses[$i];
$num++;
}
}
}
render("shuttle_show.php", ["title" => "Next Shuttle from your residence.", "times" => $times]);
}
}
This uses the function query:
function query(/* $sql [, ... ] */)
{
// SQL statement
$sql = func_get_arg(0);
// parameters, if any
$parameters = array_slice(func_get_args(), 1);
// try to connect to database
static $handle;
if (!isset($handle))
{
try
{
// connect to database
$handle = new PDO("mysql:dbname=" . DATABASE . ";host=" . SERVER, USERNAME, PASSWORD);
// ensure that PDO::prepare returns false when passed invalid SQL
$handle->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
catch (Exception $e)
{
// trigger (big, orange) error
trigger_error($e->getMessage(), E_USER_ERROR);
exit;
}
}
// prepare SQL statement
$statement = $handle->prepare($sql);
if ($statement === false)
{
// trigger (big, orange) error
trigger_error($handle->errorInfo()[2], E_USER_ERROR);
exit;
}
// execute SQL statement
$results = $statement->execute($parameters);
// return result set's rows, if any
if ($results !== false)
{
return $statement->fetchAll(PDO::FETCH_ASSOC);
}
else
{
return false;
}
}
the other functions it uses are not relevant I guess. Now I cant seem to find why this keeps producing:
Notice: Undefined offset: 0 in /Applications/MAMP/htdocs/html/shuttle.php on line 16
Notice: Undefined offset: 0 in /Applications/MAMP/htdocs/html/shuttle.php on line 22
The relevant lines are
$residences = query("SELECT * FROM residences WHERE id = ?", $users[0]["residence"]);
and
if($residences[0]["id"] != 0)
Would appreciate some help! :)
Edit:
I transferred the same file to my linux system and without any changes I get a vardump. If I use the same vardump on MAMP on my Mac the array is empty. Now I get for:
var_dump($users);
array (size=1)
0 =>
array (size=5)
'id' => int 12
'username' => string 'frechdaxx' (length=9)
'mail' => string '*************#gmail.com' (length=23)
'hash' => string '$1$qr5axj4C$BET5zZGJza2DcHI8eD8fV0' (length=34)
'residence' => int 9573
Why is this a problem at all? the function query worked with the exact same syntax before when I accessed the user table and as we can see it gives back all other values correctly.
Why the difference between my environments? The array is empty on my MAMP server, but works on Linux. However, other code examples with the query function work perfectly on both environments
Why the big int of 9573? The value in the table is 2.

This is simply that $users[0]["residence"] doesn't exists (is not set). You should check it before attempting to use it. As you can see, it's only a notice, not a error. This means that in some case scenarios, the variable can be unset and PHP won't complain THAT much, but this is definitely not good as a general rule.
I would change it to:
$users = query("SELECT * FROM users WHERE id = ?", $_SESSION["id"]);
if (empty($users[0]["residence"]))
{
apologize("Sorry, there was an error");
}
Furthermore, that only "fixes" it. If you want to go to the root of the problem, it's in the query() function that is called in $users = query("SELECT * FROM users WHERE id = ?", $_SESSION["id"]);:
// execute SQL statement
$results = $statement->execute($parameters);
// return result set's rows, if any
if ($results !== false)
{
$fetched = $statement->fetchAll(PDO::FETCH_ASSOC);
// Check that ther was actually something fetched
if(!empty($fetched))
{
return $fetched;
}
}
/* You don't need to return false, null is the default returned value.
* If you *want* it, then delete the else. */
}
From the documentation, fetchall returns an empty array if there's nothing, so the function might return an empty array.

$users = query("SELECT * FROM users WHERE id = ?", $_SESSION["id"]);
if ($users === false) { }
You are strictly checking if query returned false, that only occurs when something was wrong with query or parameters. That does not check if any result was returned, and you are referring to the first returned record ($users[0]["residence"]) that is not present ($users is an empty array).
Since user id comes from $_SESSION["id"] it should be present and available in database, but there is possibility that somewhere in your code you are overwriting it with other id.

Related

[vtiger][Webservice] Database error while performing requested operation

I use the Vtiger webservice with its different methods CRUD, login ... etc. I created a task via the Calendar module, it worked well, but when I want to delete it does not work.
If I'm not mistaken, I need to have the object's id (so Calendar) in order to delete it. So I do:
public static function getTaskUser($user)
{
$idUser = self::getUserAccountId('XXXX');
$vtiger = new VtigerWS();
$connect = $vtiger->getConnection();
if ($connect && $connect['success'] == true) {
$response = $vtiger->query("id", "Calendar", "parent_id = '$idUser' ORDER BY id");
if ($response['success'] == false) {
$result = $response['error'];
} else {
$result = array();
foreach ($response['result'] as $task) {
$result[] = $task['id'];
}
}
} else {
$result = $connect['error'];
}
$vtiger->logout();
return $result;
}
==> In this function, I first look for the user id in vtiger (the user who was assigned to the previously created task). It works well.
But when the query is running:
($ vtiger-> query ("id", "Calendar", "parent_id = '$ idBaz' ORDER BY id");)
I get an error:
"code" => "DATABASE_QUERY_ERROR"
"message" => "Database error while performing requested operation"
The connection is good, I tried a query SELECT * FROM Calendar it does not work either ... I do not see where the error can come from.

PHP PDO SQL Server Null result causes exception

I have a PDO class so I can easily switch between database types.
//*** Function: createQuery, Purpose: Create the query object ***
function createQuery($theQuery)
{
$theQuery = str_replace('`', "'", $theQuery);
$this->query = $theQuery;
$this->sth = $this->pdo->prepare($theQuery, array(PDO::ATTR_CURSOR
=> PDO::CURSOR_FWDONLY));
}
//*** Function: query, Purpose: Execute the PDO ***
function query($paramArray = [], $column = null)
{
try
{
if(!empty($paramArray))
foreach ($paramArray as $key => &$val)
{ $this->sth->bindParam($key, $val);}
$this->sth->execute();
}
catch(PDOException $e) { $this->error_catcher( $e->getMessage());}
if($column != null)
{
$column = ($this->sth->columnCount() < $column) ? 0 : $column;
return $this->sth->fetchAll( PDO::FETCH_COLUMN, $column );
}
else
return $this->sth->fetchAll();
}
and it all works spiffingly well. However, sometimes, if the result is NULL the code throws an exception
here is a typical query to build a menu :
SELECT TOP (100) PERCENT caption, id
FROM dbo.com_menuitems
WHERE (suite = 'chrimbotu') AND (parentid = 0) AND (enabled = 1) AND (id IN
(
SELECT parentid AS id FROM dbo.com_menuitems AS com_menuitems_1
WHERE (suite = 'chrimbotu') AND (perms <= - 1)GROUP BY parentid
HAVING (parentid > 0))
)
ORDER BY parentid, vieworder, caption
and i load the query (from another script) like this :
function getTopLevel($mySQL)
{
$this->_myConn->createQuery($mySQL);
$result = $this->_myConn->query() or die (DumpSQL($mySQL).__FUNCTION__ .' Query failed.');
$this->_topLevel = $result[0]['perms'];
}
If I run the query with no 'chrimbotu' in the database, SSMS says [NULL, NULL] my script carks and throws the helpful error "Query failed".
If I add 'chrimbotu' to the database, but give it a perms of 9, SSMS says [NULL, NULL] but my script is happy.
Both queries return a NULL, but is one more "NULLER" than the other?
FYI: 0 is the lowest permission, < 0 is "everybody" stuff

PHP Improve performance to execute multiple queries while reading a file with thousand lines

I'm trying to build a script where I need to read a txt file and execute some process with the lines on the file. For example, I need to check if the ID exists, if the information has updated, if yes, then update the current table, if no, then insert a new row on another temporary table to be manually checked later.
These files may contain more than 20,30 thousand lines.
When I just read the file and print some dummie content from the lines, it takes up to 40-50ms. However, when I need to connect to the database to do all those verifications, it stops before the end due to the timeout.
This is what I'm doing so far:
$handle = fopen($path, "r") or die("Couldn't get handle");
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
$segment = explode('|', $buffer);
if ( strlen($segment[0]) > 6 ) {
$param = [':code' => intval($segment[0])];
$codeObj = Sql::exec("SELECT value FROM product WHERE code = :code", $param);
if ( !$codeObj ) {
$param = [
':code' => $segment[0],
':name' => $segment[1],
':value' => $segment[2],
];
Sql::exec("INSERT INTO product_tmp (code, name, value) VALUES (:code, :name, :value)", $param);
} else {
if ( $codeObj->value !== $segment[2] ) {
$param = [
':code' => $segment[0],
':value' => $segment[2],
];
Sql::exec("UPDATE product SET value = :value WHERE code = :code", $param);
}
}
}
}
fclose($handle);
}
And this is my Sql Class to connect with PDO and execute the query:
public static function exec($sql, $param = null) {
try {
$conn = new PDO('mysql:charset=utf8mb4;host= '....'); // I've just deleted the information to connect to the database (password, user, etc.)
$q = $conn->prepare($sql);
if ( isset($param) ) {
foreach ($param as $key => $value) {
$$key = $value;
$q->bindParam($key, $$key);
}
}
$q->execute();
$response = $q->fetchAll();
if ( count($response) ) return $response;
return false;
} catch(PDOException $e) {
return 'ERROR: ' . $e->getMessage();
}
}
As you can see, each query I do through Sql::exec(), is openning a new connection. I don't know if this may be the cause of such a delay on the process, because when I don't do any Sql query, the script run within ms.
Or what other part of the code may be causing this problem?
First of all, make your function like this,
to avoid multiple connects and also o get rid of useless code.
public static function getPDO() {
if (!static::$conn) {
static::$conn = new PDO('mysql:charset=utf8mb4;host= ....');
}
return static::$conn;
}
public static function exec($sql, $param = null) {
$q = static::getPDO()->prepare($sql);
$q->execute($param);
return $q;
}
then create unique index for the code field
then use a single INSERT ... ON DUPLICATE KEY UPDATE query instead of your thrree queries
you may also want to wrap your inserts in a transaction, it may speed up the inserts up to 70 times.

Class returns integer instead of object while trying to get column data

Just getting into OOP, and changing from mysql queries to PDO. I am trying to create a class that will return the column names and meta data for a table. This is so I can output copy/paste data for all the tables I use. I have used such a tool, based on the mysql extension, for ages and it spits out variants such as complete SELECT/INSERT/UPDATE queries. Amongst other things I now want to add DECLARE listings for Stored Procedures - so getting meta data like type and length are essential. With about 150 tables across two schemas, such automation is essential.
With uncertainty about the reliability of getColumnMeta I hunted for code and found what looked good in a Sitepoint answer. I have attempted to wrap it in a class and mimic its original context but I am simply getting a number 1 when I try to echo or print_r the response. I have also had 'not an object' error messages while trying solutions.
This is the calling code
$db_host="localhost";
$db_username='root';
$db_pass='';
$db_name='mydatabase';
try{
$db= new PDO('mysql:host='.$db_host.';dbname='.$db_name,$db_username,$db_pass, array(PDO::ATTR_PERSISTENT=>false));
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
catch(PDOException $e){echo "Error: ".$e->getMessage()."<br />"; die(); }
include 'ColMetaData.php'; //the file containing the class for getting a column listing for each table
$coldat= new supplyColumnMeta($db);
$tablemet=$coldat->getColumnMeta('groups'); // a manual insertion of a table name for testing
echo $tablemet;
And this is the class that sits in the include file
class supplyColumnMeta{
public function __construct($db){
$this->db=$db;
}
/**
* Automatically get column metadata
*/
public function getColumnMeta($table)
{$this->tableName=$table;
// Clear any previous column/field info
$this->_fields = array();
$this->_fieldMeta = array();
$this->_primaryKey = NULL;
// Automatically retrieve column information if column info not specified
if(count($this->_fields) == 0 || count($this->_fieldMeta) == 0)
{
// Fetch all columns and store in $this->fields
$columns = $this->db->query("SHOW COLUMNS FROM " . $this->tableName, PDO::FETCH_ASSOC);
foreach($columns as $key => $col)
{
// Insert into fields array
$colname = $col['Field'];
$this->_fields[$colname] = $col;
if($col['Key'] == "PRI" && empty($this->_primaryKey)) {
$this->_primaryKey = $colname;
}
// Set field types
$colType = $this->parseColumnType($col['Type']);
$this->_fieldMeta[$colname] = $colType;
}
}
return true;
}
protected function parseColumnType($colType)
{
$colInfo = array();
$colParts = explode(" ", $colType);
if($fparen = strpos($colParts[0], "("))
{
$colInfo['type'] = substr($colParts[0], 0, $fparen);
$colInfo['pdoType'] = '';
$colInfo['length'] = str_replace(")", "", substr($colParts[0], $fparen+1));
$colInfo['attributes'] = isset($colParts[1]) ? $colParts[1] : NULL;
}
else
{
$colInfo['type'] = $colParts[0];
}
// PDO Bind types
$pdoType = '';
foreach($this->_pdoBindTypes as $pKey => $pType)
{
if(strpos(' '.strtolower($colInfo['type']).' ', $pKey)) {
$colInfo['pdoType'] = $pType;
break;
} else {
$colInfo['pdoType'] = PDO::PARAM_STR;
}
}
return $colInfo;
}
/**
* Will attempt to bind columns with datatypes based on parts of the column type name
* Any part of the name below will be picked up and converted unless otherwise sepcified
* Example: 'VARCHAR' columns have 'CHAR' in them, so 'char' => PDO::PARAM_STR will convert
* all columns of that type to be bound as PDO::PARAM_STR
* If there is no specification for a column type, column will be bound as PDO::PARAM_STR
*/
protected $_pdoBindTypes = array(
'char' => PDO::PARAM_STR,
'int' => PDO::PARAM_INT,
'bool' => PDO::PARAM_BOOL,
'date' => PDO::PARAM_STR,
'time' => PDO::PARAM_INT,
'text' => PDO::PARAM_STR,
'blob' => PDO::PARAM_LOB,
'binary' => PDO::PARAM_LOB
);
}
Here's the problem:
public function getColumnMeta($table)
{
$this->tableName=$table;
// Clear any previous column/field info
$this->_fields = array();
$this->_fieldMeta = array();
$this->_primaryKey = NULL;
// Automatically retrieve column information if column info not specified
if(count($this->_fields) == 0 || count($this->_fieldMeta) == 0)
{
// Fetch all columns and store in $this->fields
$columns = $this->db->query("SHOW COLUMNS FROM " . $this->tableName, PDO::FETCH_ASSOC);
foreach($columns as $key => $col)
{
// Insert into fields array
$colname = $col['Field'];
$this->_fields[$colname] = $col;
if($col['Key'] == "PRI" && empty($this->_primaryKey)) {
$this->_primaryKey = $colname;
}
// Set field types
$colType = $this->parseColumnType($col['Type']);
$this->_fieldMeta[$colname] = $colType;
}
}
return true;//<<--- not returning an object/array!
}
Your getColumnMeta method returns a boolean, true. The string representation of this value is, of course, 1. If you want this method to return all of the meta-data, change the return statement to something like:
return array(
'fields' => $this->_fields,
'meta' => $this->_fieldMeta,
'primary' => $this->_primaryKey
);
There are some other issues with your code, too, but seeing as this is not codereview.stackexchange, I'm not going to go into too much details. I will say this, though: Please, try to follow the coding standards that most major players adhere to: these standards can be found here: PHP-FIG.
Oh, and if you want to show the meta-data, don't echo them, but rather var_dump or print_r them, seeing as you return an array or an object.
Or at least echo json_encode($instance->getColumnMeta($table)); to get a correct string representation of the returned value(s).

Voting Duplicate Votes

We currently have a voting system setup that saves the IP address of the person making a vote to stop duplicate votes.
However everyday the IP address is removed from the database so they can re-vote on there favourite item.
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.
Any ideas?
<?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;
}
}
Make an SQL database of the IP addresses you want to ban, or if the list is short enough us an array.
Check if the IP address is in the database or array. If it's not allow them to vote. However, if it is let them think they voted put it in the database and make it as voted, but mark the vote as does not count so they will not try to get around your block.
This function seems to be deleting votes that are not from "today":
// 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'));
}
I've commented out the delete query. You should test it out before putting it into production.
Your database structure seems a bit confusing, you have a table to count the vote per items and a table to count the vote per user.
You should review your database structure to merge those two tables in one table, with the : item, user, vote fields.
So you could keep a record for each vote of each user for each item as long as you want and eventualy make count() or sum() requests to get the total of votes per item

Categories