How to search an IP in an IP range using PHP? - php

I have about 1000 IP address. I calculate IP ranges for them using the bellow code:
public function ipRange($mainIp, $mask)
{
$mainIpLong = ip2long($mainIp);
$maskLong = ip2long($mask);
$netid = long2ip($mainIpLong & $maskLong);
$broadcast = long2ip($mainIpLong | ~$maskLong);
//here I insert $netid and $broadcast to a MySQL table
//so I have abount 1000 records
}
It calculates IP range correctly, for example if I call like this:
ipRange('91.99.98.243', '255.255.255.240');
Result will be:
$netid -> 91.99.98.240
$broadcast -> 91.99.98.255
Now I need to have a search function. It should find the sub-range for the given IP address, so if I call search('91.99.98.249'), the search() function should show the record that netid is 91.99.98.240 and broadcast field is 91.99.98.255.
How can I do that?

function find($ip){
//get $netid and $bcast from db one by one
//loop through all records
if(ip2long($ip)>=ip2long($netid) && ip2long($ip)<=ip2long($bcast) ){
echo $netid;
echo $bcast;
}
}

I solved it.
Note: _getConnection() function connects to database.
ip_address fields includes: IP, netid and broadcast.
public function search($ip)
{
$ranges = $this->_getConnection()->fetchAll("SELECT netid, broadcast FROM ip_address");
foreach($ranges as $range) {
$min = ip2long($range['netid']);
$max = ip2long($range['broadcast']);
if(ip2long($ip) > $min && ip2long($ip) < $max) {
$result = $this->_getConnection()->fetchAll("SELECT * FROM ip_address WHERE netid = ? AND broadcast = ?", array($range['netid'], $range['broadcast']));
}
}
return $result;
}

Related

Same serial number is generating for different requests

I am inserting a serial number in a table that is increment by one always but when multiple request is coming in same time it is inserting same serial number for different requests.I am using mysql database.
I know i am fetching the max serial number too early in the code and if request is come in same time so it will fetching same serial number for both. is it good idea to update serial number after all work done. what if inserting a record for new request and updating the serial number for previous one is in same time.
public function add(){
$session = $this->request->session();
$company_id = $session->read('Admin.company_id');
$emp_id = $session->read('Admin.emp_id');
$user_email_id = $session->read('Admin.email_id');
$employee_name = $session->read('Admin.employee_name');
$conn = ConnectionManager::get('default');
if ($this->request->is('post')) {
try{
$conn->begin();
$department = $this->request->data['department'];
$data = $this->request->data;
if(!array_key_exists('is_requisition_for_contractor', $data)){
$is_requisition_for_contractor = 0;
} else {
$is_requisition_for_contractor = $data['is_requisition_for_contractor'];
}
if(!array_key_exists('is_requisition_for_employee', $data)){
$is_requisition_for_employee = 0;
} else {
$is_requisition_for_employee = $data['is_requisition_for_employee'];
}
if(!array_key_exists('is_boulder_requisition', $data)){
$is_requisition_for_boulder = 0;
} else {
if($data['is_boulder_requisition'] == ''){
$is_requisition_for_boulder = 0;
} else {
$is_requisition_for_boulder = $data['is_boulder_requisition'];
}
}
$is_requisition_for_plant = 0;
if(!array_key_exists('is_plant_requisition', $data)){
$is_requisition_for_plant = 0;
} else {
if($data['is_plant_requisition'] == ''){
$is_requisition_for_plant = 0;
} else {
$is_requisition_for_plant = $data['is_plant_requisition'];
}
}
if(array_key_exists("files",$this->request->data)) {
$files = $this->request->data['files'];
if (count($files)) {
$files_uploading_response = $this->uploadMultipleFiles($files, 'files/requisitions/');
}
}
$last_material_insert_id = '';
if($this->request->data('material_id')[0] == ''){
if($this->request->data('department') == 1){
$type = 1;
} elseif($this->request->data('department') == 3){
$type = 3;
} elseif($this->request->data('department') == 2){
$type = 2;
}
if($this->request->data('department') == 1 || $this->request->data('department') == 3){
$conn->execute("INSERT INTO material (material_name, material_type_id, company_id, status, is_approved_by_admin) VALUES (?,?,?,?,?)",[$this->request->data('material_name'), $type, $company_id, 1,0]);
$last_material_insert_id = $conn->execute("SELECT LAST_INSERT_ID() AS last_id")->fetchAll('assoc');
} elseif($this->request->data('department') == 2) {
//todo for unapproved material
$conn->execute("INSERT INTO material (part_no, material_type_id, company_id, status, is_approved_by_admin,unique_category_id) VALUES (?,?,?,?,?,?)",[$this->request->data('part_no')[0], $type, $company_id, 1,0,$this->request->data('unique_category_id')[0]]);
$last_material_insert_id = $conn->execute("SELECT LAST_INSERT_ID() AS last_id")->fetchAll('assoc');
}
}
// here i am fatching max serial number from table
$requistion_number = $conn->execute("SELECT IF(MAX(requisition_no) IS NULL, 0,MAX(requisition_no)) AS requisition_no FROM requisition WHERE site_id = ?",[$this->request->data('site_id')])->fetchAll('assoc');
$Requisition = TableRegistry::get('requisition');
$requisition = $Requisition->newEntity();
$requisition->registered_on = $this->request->data['date'];
$requisition->department_id = $this->request->data('department');
$requisition->site_id = $this->request->data('site_id');
$requisition->issues_to_id = $this->request->data['prepared_by_id'];
$requisition->prepared_by_id = $this->request->data['prepared_by_id'];
$requisition->approved_by_id = $this->request->data['hod_id'];
$requisition->hod_id = $this->request->data['hod_id'];
$requisition->is_diesel_requisition_for_employee = $is_requisition_for_employee;
$requisition->is_diesel_requisition_for_contractor = $is_requisition_for_contractor;
$requisition->is_requisition_for_boulder = $is_requisition_for_boulder;
$requisition->is_requisition_for_plant = $is_requisition_for_plant;
if(array_key_exists('for_tanker_stock', $this->request->data)) {
$requisition->for_tanker_stock = 1;
}
if($last_material_insert_id != ''){
$requisition->is_material_approved_by_admin = 0;
}
$requisition->status = 1;
$site_id = $this->request->data['site_id'];
$requisition->requisition_no = $requistion_number[0]['requisition_no'] + 1;
$requistionnumber = $requistion_number[0]['requisition_no'] + 1;
$saveRequsition = $Requisition->save($requisition);
$conn->commit();
}
I am expecting the output different serial number for each request.any optimise way to do this. thanks in advance.
Ok, how about the same strategy, setting the $requisition_number after the row has been inserted (see my other answer), but using a single query with the same method you use to determine the new requisition id:
$conn->execute("UPDATE requisition
SET requisition_no = (SELECT IF(MAX(requisition_no) IS NULL, 0,MAX(requisition_no)) AS requisition_no FROM requisition WHERE site_id = ?) + 1",
[$this->request->data('site_id')]);
The idea here is that a single query will be executed in one step, without another, similar query, being able to interfere.
What you currently do is to first get the old requistion number like this:
$requistion_number = $conn->execute("SELECT IF(MAX(requisition_no) IS NULL, 0,MAX(requisition_no)) AS requisition_no
FROM requisition WHERE site_id = ?",[$this->request->data('site_id')])->fetchAll('assoc');
and then increase it before you save and commit.
My suggestion is to not set the $requistion_number at all before you save and commit the requisition row, but to determine the $requistion_number afterwards.
You now wonder how?
Well, you need to count the total number of requisition rows in the table for the site the requisition is for, and add one, like this:
$last_requisition_id = $conn->execute("SELECT LAST_INSERT_ID() AS last_id")->fetchAll('assoc');
$site_id = $this->request->data('site_id');
$requisition_number = $conn->execute("SELECT COUNT(*) AS requisitionsCount
FROM requisition
WHERE <primary_key> <= ? AND
site_id = ?",
[$last_requisition_id, $site_id]) + 1;
$conn->execute("UPDATE requisition
SET requisition_no = ?
WHERE <primary_key> <= ?",
[$requisition_number, $last_requisition_id]);
I know this code is not working. The $requisition_number will probably contain an array with the requisitionsCount as a value, but you can correct that.
Because you're using data that is already present in the database table you don't run the risk that two rows will get the same $requisition_number. The assumption here is that requisitions are never deleted.

Count Array where Column Value Equals Given

I have a controller where an array of checkbox values are passed on to the controller and then sent on to do a variety of tasks. My problem is focused on getting a count of the arrays' shipment's customer's billing method, but I'm not entirely sure how I would get those values since I'm posting an array and I want a count.
Here's my controller:
public function sendNow(Request $request)
{
$now = Carbon::now();
$sendNowShipment = array();
$method = array();
$sendNowShipment = request('send');
foreach($sendNowShipment as $SNS){
$shipment = Shipment::findOrFail($SNS);
if($shipment->billtoAccount->billingMethods->label == "Mail"){
$timestamp = Carbon::now()->timestamp;
$shipment_details = $shipment->shipment_details;
$path = 'temp/freightbill_'.$shipment->pro_number.'-'.$timestamp.'.pdf';
$sessionPath[] = $path;
$pdf = PDF::loadView('shipments.pdf', compact('shipment', 'shipment_details'))
->save($path);
}elseif($shipment->billtoAccount->billingMethods->label == "Email"){
$billToAccount = $shipment->billtoAccount;
$billToAccountUsers = $billToAccount->users;
if ($billToAccount->primary_email){
$billToEmail[] = $billToAccount->primary_email;
}
if ($billToAccountUsers->count() > 0){
foreach ($billToAccountUsers as $billToAccountUser){
$billToEmail[] = $billToAccountUser->email;
}
}
foreach ($billToEmail as $bte){
Mail::to($bte)->send(new newBillToShipment($shipment));
}
}
}
if(count($shipment->billtoAccount->billingMethods->label == "Mail") > 0){// count where shipments->customers->billingMethods = Mail) > 0
$pdf = new PDFMerger();
// Add 2 PDFs to the final PDF
foreach($sessionPath as $sp){
$pdf->addPDF($sp, 'all');
}
// Merge the files and retrieve its PDF binary content
$timestamp = Carbon::now()->timestamp;
$binaryContent = $pdf->merge('download', $timestamp."-printBatch.pdf");
// Return binary content as response
//return response($binaryContent)
// ->header('Content-type' , 'application/pdf');
return back();
//dd($sendNowShipment,$sendMethod);
}
}
If you look at this line (a little past the middle):
if(count($shipment->billtoAccount->billingMethods->label == "Mail") > 0){
// count where shipments->customers->billingMethods = Mail) > 0
You'll see that I'm going through a multitude of relationships just to get the value of "Mail" that I am looking for.
So I'm wondering if this should be a DB:: query through laravel, but I'm not sure how I would perform the query using the given checkbox array I have being POSTed to this controller.

Algorithm of getting free IP address

I use yii2. And I need to find IP which is not used (is not in database) by method getFreeIPAddress. I have class like this:
class Radreply extends ActiveRecord {
const ATTRIBUTE_DEFAULT_IP_ADDRESS = 'Framed-IP-Address';
const IP_ADDRESS_MAX = '10.255.255.255'; // max value for IP
const IP_ADDRESS_MIN = '10.0.0.11'; // min value for IP
public function getIntegerIP(){ // converts IP from string to integer format
return ip2long($this->value);
}
public static function getFreeIPAddress(){
$records = self::findAll(['attribute'=>self::ATTRIBUTE_DEFAULT_IP_ADDRESS]); // get all record which contain IP address
$existIPs = ArrayHelper::getColumn($records,'integerIP'); // get array of IP which is converted to integer by method getIntegerIP
for ($integerIP = ip2long(self::IP_ADDRESS_MIN); $integerIP<=ip2long(self::IP_ADDRESS_MAX); $integerIP++){
// increasing one by one IP address in integer format from value IP_ADDRESS_MIN to value IP_ADDRESS_MAX
if (!in_array($integerIP, $existIPs)){
$stringIP = long2ip($integerIP);
$arrayDigits = explode('.', $stringIP);
$lastDigit = array_pop($arrayDigits);
if ($lastDigit!='0'){ // check if last digit of IP is not 0
return $stringIP;
}
}
}
return '';
}
}
Method getFreeIPAddress works find, but in db there are a lot of records with IP and increasing one by one IP and checking if this IP exist in db is very long way. How I can optimize this algorithm? Is there faster way to get unused IP?
I think, I've found better solution without extra table in database
class Radreply extends ActiveRecord {
const ATTRIBUTE_DEFAULT_IP_ADDRESS = 'Framed-IP-Address';
const IP_ADDRESS_MAX = '10.255.255.255'; // max value for IP
const IP_ADDRESS_MIN = '10.0.0.11'; // min value for IP
public function getIntegerIP(){ // converts IP from string to integer format
return ip2long($this->value);
}
public static function getFreeIPAddress(){
$records = self::findAll(['attribute'=>self::ATTRIBUTE_DEFAULT_IP_ADDRESS]); // gets all record which contain IP address
$existIPs = ArrayHelper::getColumn($records,'integerIP'); // gets array of IP which is converted to integer by method getIntegerIP
$intIpAddressMin = ip2long(self::IP_ADDRESS_MIN); // gets min IP in integer format
$endRange = empty($existIPs) ? $intIpAddressMin : max($existIPs); // checks if at least one IP is used
$availableIPs = range( $intIpAddressMin, $endRange + 2); // generates array with available IP addresses (+2 because next address can be with last digit 0)
$missingIPs = array_diff($availableIPs,$existIPs); // removes all used IP
foreach ($missingIPs as $value){
$lastDigit = $value % 256;
if ($lastDigit != 0){
return long2ip($value);
}
}
return '';
}
}
bool in_array ( mixed $needle , array $haystack [, bool $strict = FALSE ] )
In my opinion,you can set strict true .
my php code with strict = false
<?php
$y="1800";
$x = array();
for($j=0;$j<50000;$j++){
$x[]= "{$j}";
}
for($i=0;$i<30000;$i++){
if(in_array($y,$x)){
continue;
}
}
time php test.php
real 0m4.418s
user 0m4.404s
sys 0m0.012s
when strict is true
for($i=0;$i<30000;$i++){
if(in_array($y,$x ,true)){
continue;
}
}
time php test.php
real 0m1.548s
user 0m1.540s
sys 0m0.004s
what‘s more ,if you can get the used ip with ascending order . you can get the o(m+n) time complexity,which m is the length of all ip you should try , n is the length of all ip in db with merge algorithm .
if you can get the used ip with ascending order
in Pseudocode .
tmpIp = minIp;
while(temIp <= maxIp){
if( dbIsEmpty){
break;
}
dbIp =getNextFromDb();
while(temIp < dbIp){
printf temIp ;
temIp ++;
}
temIp ++;
}
while(temIp <= maxIp){
printf temIp ;
temIp++;
}
here is my php code, where i repalce echo ip by $count++;
In this demo there is about 80000 ip with type of long
<?php
function mergeSort( $result){
$minIp = ip2long('10.0.0.11') ;
$maxIp = ip2long('10.255.255.255');
$count =0;
$tmpIp = $minIp;
while($temIp <= $maxIp){
if( empty($result)){
break ;
}
$tmp = array_pop($result);
$dbIp =$tmp['ip'];
while($temIp < $dbIp){
// echo temIp ;
// i repalce it by count ++ , i don't want it
//full my teminal .
$count ++;
$temIp ++;
}
$temIp ++;
}
while($temIp <= $maxIp){
//echo $temIp ; replace by $count++
$count ++;
$temIp++;
}
return $count -1;
}
$servername = "localhost";
$username = "root";
$password = "aaaaa";
$dbname = "IP";
$conn = new PDO('mysql:host=' . $servername . ';dbname=' . $dbname , $username, $password);
$conn->setAttribute(PDO::ATTR_AUTOCOMMIT , true);
$stmt = $conn->prepare("select * from ipTable order by ip desc");
$stmt->execute();
$result = $stmt->fetchAll();
$count = mergeSort($result);
echo $count ;
?>
it take about 10s ;
time php test.php
184460881
real 0m10.626s
user 0m10.416s
sys 0m0.168s

PHP: How do I check if one UNIX timestamp range overlaps any UNIX timestamp range?

How to check if one UNIX timestamp range is overlapping another UNIX timestamp range in PHP?
I am developing an application which takes future reservations. But, only one (1) reservation is allowed per period.
Example:
Mr. X has a reservation for a resource from 10:00 A.M. to 12:00 P.M. (noon). Later, Ms. Y wants to reserve that same resource from 8:00 A.M. to 11:00 P.M.. My application should reject Ms. Y's attempted reservation because it overlaps Mr. X's prior reservation.
I am storing the start and end times of existing reservations in UNIX timestamps (integers), but I could convert them into the following format "yyyy-mm-dd hh:mm:ss" if required, or vice versa.
I do not understand how to solve this problem. If I check the new start time with all the existing reservation start times, and the new end time in a similar fashion, the logic will have many if statements and make the application slow.
Would you please help me to solve this issue in an efficient way without using lots of server resources.
Your help is greatly appreciated.
Thanks
Introduction
In other words, you need to do a comparison of all reservation intervals (UNIX timestamps) for a particular resource to determine if a new reservation is valid (within the domain for new reservations).
Step 1
First, a SQL query similar to this might help. While key words like ANY, ALL, NOT, EXISTS and others may seem tempting, it is up to you to decide how much information you need in the event of a scheduling conflict (based on your UI). This query provides the opportunity to extract the maximum amount of information (in PHP, etc ...) about a potential reservation with look ahead forecasting.
// A query like this might help. It's not perfect, but you get the idea.
// This query looks for ALL potential conflicts, starting and ending.
$sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
FROM `reservations` AS `t1`
INNER JOIN `resources` AS `t2`
ON `t1`.`resourceId` = `t2`.`resourceId`
WHERE `t2`.`resourceId` = :resourceId
AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
ORDER BY `t1`.`startTime` ASC";
Potentially. this will leave you with a multi-dimentional array. The following logic allows you to get a report detailing why the reservation cannot be made. It is up to you to interpret the report in another module.
Step 2
Generalize the solution as a methods of a Reservation class. Depending on your RDBMS, you may be able to do something like this in SQL. Although, it will probably be far less specific and you may want that granularity later. You could send the report in JSON format to a JavaScript front end (just something to think about).
private function inOpenDomain(array $exclusion, $testStart, $testEnd)
{
$result = null;
$code = null;
$start = $exclusion[0];
$end = $exclusion[1];
if (($testStart > $end) || ($testEnd < $start)) {
$result = true;
$code = 0; //Good! No conflict.
} elseif ($testStart === $start) {
$result = false;
$code = 1;
} elseif ($testStart === $end) {
$result = false;
$code = 2;
} elseif ($testEnd === $start) {
$result = false;
$code = 3;
} elseif ($testEnd === $end) {
$result = false;
$code = 4;
} elseif (($testStart > $start) && ($testEnd < $end)) { //Middle
$result = false;
$code = 5;
} elseif (($testStart < $start) && ($testEnd > $start)) { //Left limit
$result = false;
$code = 6;
} elseif (($testStart < $end) && ($testEnd > $end)) { //Right limit
$result = false;
$code = 7;
} elseif (($testStart < $start) && ($testEnd > $end)) { //Both limits
$result = false;
$code = 8;
} else {
$result = false;
$code = 9;
}
return ['start' => $start, 'end' => $end, 'result' => $result => 'code' => $code];
}
Step 3
Make a method that manages the checking of prior reservation times (assuming PDO::FETCH_ASSOC).
private function checkPeriods(array $periods, $newStartTime, $newEndTime)
{
$report = [];
if (!isset($periods[0])) { //If NOT multi-dimensional
$report = inOpenDomain($periods, $newStartTime, $newEndTime)
} else {
for ($i = 0, $length = $count($periods); $i < $length; ++$i) {
$report[$i] = inOpenDomain($periods[$i], $newStartTime, $newEndTime);
}
}
return $report;
}
Step 4
Fashion a method for doing a SELECT on the reservations table using a PDO prepared statement. Generally, ...
private function getReservationTimes($resourceId, $minTime, $maxTime)
{
$sql = "SELECT DISTINCT `t1`.`startTime`, `t1`.`endTime`
FROM `reservations` AS `t1`
INNER JOIN `resources` AS `t2`
ON `t1`.`resourceId` = `t2`.`resourceId`
WHERE `t2`.`resourceId` = :resourceId
AND (`t1`.`startTime` BETWEEN :minTime1 AND :maxTime1)
OR (`t1`.`endTime` BETWEEN :minTime2 AND :maxTime2)
ORDER BY `t1`.`startTime` ASC";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(:resourceId , $resourceId);
$stmt->bindParam(:minTime1 , $minTime);
$stmt->bindParam(:maxTime1 , $maxTime);
$stmt->bindParam(:minTime2 , $minTime);
$stmt->bindParam(:maxTime2 , $maxTime);
$stmt->execute();
return $stmt->fetchAll();
}
Step 5
Make a public method (interface) for the entire process.
public function isOpen($minTime, $maxTime)
{
$periods = $this->getReservationTimes($this->resource->getResourceId(), $minTime, $maxTime);
if (empty($periods)) {
return true; //You may reserve the resource during the time period.
}
return $this->checkPeriods($periods, $this->start, $this->end));
}
Step 6
Separate the concerns.
Create a class hierarchy for the actual items being reserved.
abstact class Product
{
}
class Resource extends Product implements Reservable //Or, something ...
{
private $resourceId;
//etc ....
}
Create a class hierarchy for reservations.
abstract class Interval
{
private $start;
private $end;
public function __construct($start, $end)
{
$this->start = $start;
$this->end = $end;
}
}
class Reservation extends Interval
{
private $db;
private $resource;
public function __construct(PDO $pdo, Reservable $resource, $reqStartTime, $reqEndTime)
{
parent::__construct($reqStartTime, $reqEndTime);
$this->db = $pdo;
$this->resource = $resource;
}
}
Step 7
Run within try/catch
When you instantiate the Reservation object, supply at least a Reservable object, the requested start time, and requested end time (as UNIX timestamps, in this case).
try
{
$day = 84600; // Seconds per day.
$future = $day * 90; // Application specific.
//User requested times.
$reqStartTime = 1488394687 + $day; // Tomorrow.
$reqEndTime = 1488394687 + ($day * 2); // Two day duration.
//Search constraints.
$minTime = time(); // Today. Right now.
$maxTime = 1488394687 + $future; // 90 day look ahead.
$reservation = new Reservation($pdo, $resourceObj, $reqStartTime, $reqEndTime);
$availability = $reservation->isOpen($minTime, $maxTime);
if($availability === true){
$reservation->confirm();
} else {
//Have some other object deal with the report
$reporter = new Reporter($availability);
$reporter->analyzeReport();
//Have some other object update the view, etc ...
}
}
catch(Exception $e)
{
//Handle it.
}

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