How to control last visitor ip through sql and php? - php

I am trying to make a unique visitors counter for my pages using mysql and php. Im my DB table i have a "views" column and a "last_ip" column.
If the ip of the current user is equal to the last ip stored on DB the counter dies, if the current user ip is different from the last ip stored on DB the current user ip is stored as last ip on DB and the counter sums +1 to the views on DB.
The main idea is:
1 - check the ip of the current user and save it to variable $viewer_ip
2 - check the last ip stored on DB and save it to variable $last_viewer_ip
3 - compare those 2 variables, if $viewer_ip =! $last_viewer_ip the function should store $last_viewer_ip in "last_ip" field and sums +1 in "views" field. Else it should do nothing.
<?php
$viewer_ip = $user_ip = $h->cage->server->testIp('REMOTE_ADDR');
$sql = "SELECT post_last_viewer_ip FROM " . TABLE_POSTS . " WHERE post_id = %d";
$last_viewer_ip = $h->db->get_var($h->db->prepare($sql, $h->post->id));
if ($viewer_ip AND $viewer_ip != $last_viewer_ip) {
$sql = "UPDATE " . TABLE_POSTS . " SET post_last_viewer_ip = '" . $viewer_ip . "' WHERE post_id = %d";
$h->db->query($h->db->prepare($sql, $h->post->id));
}
if ($viewer_ip != $last_viewer_ip) {
$sql = "UPDATE " . TABLE_POSTS . " SET post_views = post_views + 1 WHERE post_id = %d";
$h->db->query($h->db->prepare($sql, $h->post->id));
}
?>
that code works in parts, cause it sums like 3 views, on each visit, as u can see, that code is a trash, cause i did it myself and i am no expert.
Anyone can try a fix on this? ty.

You need to store the session id of your visitors to the database to be able to do that kind of thing correctly. A unique visitor is considered one that is there for one specific session. If the user closes the browser and comes back, then it is another visitor.
If you want truly unique visitors, you need to store cookies too and use them to identify your visitors, but then again, cookie blocked? Cookie flushed? You're screwed...
Last method is to force a login, with a login you usually have a user_id, that user_id becomes your unicity.
I'll let you decide how you want to handle your unicity...
For the storage part, you need at least 1 table where you store the requests and identity of your requesters. Store in that table the following information:
Page/SpecificRequest
UserIP/SessionID/CookieID/UserId
RequestDate
RequestTime
Then on each page request, store a request in that table such as:
INSERT INTO myrequests VALUES(
$_REQUEST['URI'],
session_id(),
date('Y-m-d'),
date('G:i:s')
)
And then, to retrieve the unique visitor count, you just group on the data:
SELECT RequestDate, COUNT(*) AS uniquevisitors
FROM myrequests
GROUP BY RequestDate, session_id()
Good luck

Try it this way..
You need one separate table (TABLE_WITH_LOGS) for IP logs, which has 3 columns post_viewer_ip varchar(15), timestamp timestamp, post_id bigint (or whatever type you have for you post ids).
The code will look something like that one..
$timeout = 30*60; // count as a new visit after 30 minutes
$viewer_ip = $user_ip = $h->cage->server->testIp('REMOTE_ADDR');
// check if there is a record within the timeout period
$sql = "SELECT count(*) FROM " . TABLE_WITH_LOGS . " WHERE post_id = %d
AND ip = '%s' AND `timestamp` > NOW() - $timeout";
$rows = $h->db->get_var($h->db->prepare($sql, $h->post->id, $viewer_ip));
if ($rows == 0) { // no recent records in DB, insert new one
$sql = "INSERT INTO " . TABLE_WITH_LOGS . " SET post_viewer_ip = '" .
$viewer_ip . "', `timestamp` = NOW() WHERE post_id = %d";
$h->db->query($h->db->prepare($sql, $h->post->id));
// update counter
$sql = "UPDATE " . TABLE_POSTS . " SET post_views = post_views + 1
WHERE post_id = %d";
$h->db->query($h->db->prepare($sql, $h->post->id));
}
else { // there is at least one record, update it with current timestamp
$sql = "UPDATE " . TABLE_WITH_LOGS . " SET `timestamp` = NOW()
WHERE post_id = %d and post_viewer_ip = '$viewer_ip' LIMIT 1";
$h->db->query($h->db->prepare($sql, $h->post->id));
}
// cleanup table from time to time
if (rand(1,5)<2) {
$sql = "DELETE " . TABLE_WITH_LOGS . " WHERE `timestamp` < NOW() - $timeout";
$h->db->query($sql);
}
ps: you may skip part with timestamp update and always insert a new record. It will make less (only by one :)) SQL requests.
$timeout = 30*60; // count as a new visit after 30 minutes
$viewer_ip = $user_ip = $h->cage->server->testIp('REMOTE_ADDR');
// check if there is a record within the timeout period
$sql = "SELECT count(*) FROM " . TABLE_WITH_LOGS . " WHERE post_id = %d
AND ip = '%s' AND `timestamp` > NOW() - $timeout";
$rows = $h->db->get_var($h->db->prepare($sql, $h->post->id, $viewer_ip));
if ($rows == 0) { // no recent records in DB, update counter
$sql = "UPDATE " . TABLE_POSTS . " SET post_views = post_views + 1
WHERE post_id = %d";
$h->db->query($h->db->prepare($sql, $h->post->id));
}
// record last visit
$sql = "INSERT INTO " . TABLE_WITH_LOGS . " SET post_viewer_ip = '" .
$viewer_ip . "', `timestamp` = NOW() WHERE post_id = %d";
$h->db->query($h->db->prepare($sql, $h->post->id));
// cleanup table from time to time
if (rand(1,5)<2) {
$sql = "DELETE " . TABLE_WITH_LOGS . " WHERE `timestamp` < NOW() - $timeout";
$h->db->query($sql);
}

Related

Pull values from a row based on ID

Hoping someone can shed light on this. I am trying to pull the value from 2 fields from a row and based on the row being expired, exclude those 2 values from a drop down list.
I have a table (schedule)
gameID
homeID
visitorID
gameTimeEastern
weekNum
each week there are matchups where 2 teams play each other. Those 2 teams are in a row based on gameID with a specific start time (gameTimeEastern).
I have a function that determines when the matchup is locked, meaning the game has started:
function gameIsLocked($gameID) {
//find out if a game is locked
global $mysqli, $cutoffDateTime;
$sql = "select (DATE_ADD(NOW(), INTERVAL " . SERVER_TIMEZONE_OFFSET . " HOUR) > gameTimeEastern or DATE_ADD(NOW(), INTERVAL " . SERVER_TIMEZONE_OFFSET . " HOUR) > '" . $cutoffDateTime . "') as expired from " . DB_PREFIX . "schedule where gameID = " . $gameID;
$query = $mysqli->query($sql);
if ($query->num_rows > 0) {
$row = $query->fetch_assoc();
return $row['expired'];
}
$query->free;
die('Error getting game locked status: ' . $mysqli->error);
This basically determines if the row is expired (gameTimeEastern has passed). I then have a drop down on a form that has a list of all the teams from each matchup for that week.If the row is expired, then I do not want to include the homeID or visitorID from that row in the drop down.
On my page I am trying to show those teams from the expired row but it is failing as the page stops processing when it hit this:
//get expired teams
$expiredGames =gameIsLocked(gameID);
// echo 'Expired games are GAME ' . $expiredGames . '<br>';
for ($eti=1; $eti<=$gameID; $eti++)
{
if ($gameID[$eti]>''){
$sql = "select * from " . DB_PREFIX . "schedule WHERE gameID = '" . $gameID[$eti] . "';";
$query = $mysqli->query($sql);
if ($query->num_rows > 0) {
$result = $query->fetch_assoc();
$expiredHomeTeam = $result['homeID'];
$expiredVisitorTeam = $result['visitorID'];
}
}
echo 'Expired teams for GAME '.$gameID.' are '.$expiredHomeTeam.' and '.$expiredVisitorTeam.'<br>';
}
NEW CODE - Actually giving me the first result
//get expired teams
$expiredGames =gameIsLocked(gameID);
$sql = "select * from " . DB_PREFIX . "schedule WHERE weekNum = '6';";
$query = $mysqli->query($sql);
if ($query->num_rows > 0) {
$result = $query->fetch_assoc();
$expiredHomeTeam = $result['homeID'];
$expiredVisitorTeam = $result['visitorID'];
}
echo 'Expired teams for GAME ' . $expiredGames . ' are '.$expiredHomeTeam.' and '.$expiredVisitorTeam.'<br>';
Ended up using the SQL query to schedule to get results I needed. Thanks for the direction. The logic was already there, just needed to add an if statement to how I populated the array for teams in the drop down.

PHP/SQL UPDATE value WHERE date equals maximum/newest date

I have a table in Database PhpMyAdmin with certain values, and I want to UPDATE only ONE value of these, WHERE the latest initial_date (TIMESTAMP) is.
I write down here the code I generated as an example, so you can see I actually obtain that date value through SELECT, but I don't manage to UPDATE it. Thank you very much.
$select = "SELECT MAX(initial_date) AS max_value FROM services WHERE matricula = '" . $_POST["taxi"] . "'";
$select_results = mysqli_query($conexion, $select);
while($row = mysqli_fetch_array($select_results)){
echo $row['max_value'];
$update_carrera = "UPDATE services SET";
$update_carrera .= " costo_carrera = costo_carrera + " . $_POST["costo_carrera"] . ",";
$update_carrera .= " final_date = CURRENT_TIMESTAMP";
$update_carrera .= " WHERE initial_date = ''";
$update_carrera_results = mysqli_query($conexion, $update_carrera);
}
I leave WHERE initial_date = '' empty so you can tell what should it be. I get a correct date value in the echo $row['max_value']; if I solve the WHERE with a WHERE initial_date = '20160405153315' (INTEGER), but I don't want to put myself the integer, of course I want to get the newest date from the table database.

Can't get score to update with this mysql statement

I'm guessing that I'm just a little rusty or something because it seems like this should be working. Am I missing something here...
Here is the code I am trying to use...
<?php
echo dbConn();
$existing_time = mysql_result(mysql_query("SELECT p_time FROM scores WHERE p_uid=$uid"), 0);
$existing_category = mysql_result(mysql_query("SELECT p_cat FROM scores WHERE p_uid=$uid AND p_cat=$pieces"), 0);
if ($existing_category == "") {
mysql_query(
"INSERT INTO scores VALUES (
'',
'$uid',
'$pusername',
'$time',
'$pieces'
)");
} elseif ($existing_time <= $time) {
echo "No Change! Old Score Was Better (Lower)";
} elseif ($existing_time > $time) {
mysql_query("UPDATE scores SET p_time = " . $time . " WHERE p_uid = " . $uid . " AND p_cat = " . $pieces . "");
};
?>
Now... Here is what I am trying to do...
I am collecting info from the database where the users username AND category match. If the category for that user does not exist, it inserts the latest score. (This much works.)
Then, if the category does exist but the old score is better, it just does nothing. (This part works too)...
However, what I can't seem to do is get it to update the last score, if the current score is better (lower score, since this is a time based game.) It doesn't update the score.
I am trying it this way: By updating a row in "scores" where the USERNAME and the CATEGORY match at the same time.
Please note... where it says "pieces". this is a category. Where it says "time", this is a score. The score is returned as 00:00:00 for hours minutes and seconds.
EXAMPLE: (in parentheses is the database row name)
id (ID) = just KEY id in sequencial order
user id (p_uid) = 123456789
username (p_username) = somename
score (p_time) = 00:01:03
category (p_cat) = 10
Change you update statement to:
mysql_query("UPDATE scores SET p_time = '" . $time . "' WHERE p_uid = " . $uid . " AND p_cat = " . $pieces . "");
You have missed quotes in the update statement around $time.

Count records from multiple tables in real-time

I want to count a record by current date from different tables and return as one row with different column in the new table. The code will update a record every three hours and insert new record if current date changes. I've current date and time data (2013-05-20 14:12:12) in "created_at" column. Here my current code:
require_once('./db_connect.php');
$dbcon = new db;
//test to see if a specific field value is already in the DB
public function in_table($table,$where) {
$query = 'SELECT * FROM ' . $table . ' WHERE ' . $where;
$result = mysqli_query($this->dbh,$query);
$this->error_test('in_table',$query);
return mysqli_num_rows($result) > 0;
}
//running in background
while (true) {
$select= "SELECT (SELECT CURDATE()) AS time," .
"(SELECT COUNT(tweet_id) FROM tweets WHERE created_at= 'CURDATE() %') AS total_count," .
"(SELECT COUNT(fid) FROM fun WHERE ftime= 'CURDATE() %') AS f_count," .
"(SELECT COUNT(sid) FROM sad WHERE stime= 'CURDATE() %') AS s_count";
$results = mysqli_query( $dbcon, $select );
while($row = mysqli_fetch_assoc($result)) {
$time = $row['time'];
$total = $row['total_count'];
$fcount = $row['f_count'];
$scount = $row['s_count'];
$field_values = 'time = "' . $time . '", ' . 'total_count = ' . $total . ', ' . 'fun_count = ' . $fcount . ', ' . 'sad_count = ' . $scount;
if ($dbcon->in_table('count','time= "' . $time . '"')) {
$update = "UPDATE count SET $field_values WHEN time= '$time'";
mysqli_query( $dbcon, $update );
}
else {
$insert = "INSERT INTO count SET $field_values";
mysqli_query( $dbcon, $insert );
}
}
//update record every 3 hour
sleep(10800);
}
With this code I can't get a count record. The result return | 2013-05-18 | 0 | 0 | 0 |. How can I correct this?
I not familiar with PHP, but you can retrieve the count of all records dated any time today using:
SELECT COUNT(tweet_id)
FROM tweets
WHERE created_at >= curDate()
AND created_at < date_add(curDate(), interval 1 day)
It is equivalent to saying
..
WHERE created_at >= (today at midnight *incusive*)
AND created_at < (tomorrow at midnight *exclusive*)
Update:
The advantage of this method is it is index friendly. While using WHERE DATE(Column) = currDate() works, it can prevent the database from using indexes on that column, making the query slower.
Replace the parts where you have this:
WHERE created_at= 'CURDATE() %'
with this:
WHERE DATE(created_at) = CURDATE()
Your existing WHERE clause is comparing created_at to the string constant CURDATE() %, and they'll never match.
You are comparing against created_at= 'CURDATE() %', which is looking for that exact string, not for the result of a function. If the field created_at is a date, it will never match.
And, you are doing that for all counts.

how to limit the amount of comments or replies to comments a user can post per day

I have a comment section and a reply to comment section on my social network. We are having some trouble with manual spammers, and I was going to limit the amount of comments someone could post a day.
Here are the insert queries for comments and reply to comments:
//COMMENTS
$query = "INSERT INTO `CysticAirwaves` (
`FromUserID`,
`ToUserID`,
`comment`,
`status`,
`statusCommentAirwave`,
`date`,
`time`
) VALUES (
'" . $auth->id ."',
'" . $prof->id ."',
'" . mysql_real_escape_string($_POST['ProfileComment']) ."',
'active',
'active',
'" . date("Y-m-d") . "',
'" . date("G:i:s") . "')";
mysql_query($query,$connection);
if($auth->id == $prof->id) {
$just_inserted = mysql_insert_id();
$query = "UPDATE `CysticAirwaves` SET `status` = 'dead' WHERE `FromUserID` = '" . $auth->id . "' AND `ToUserID` = '" . $prof->id . "' AND `id` != '" . $just_inserted . "'";
$request = mysql_query($query,$connection);
}
//REPLIES
$query = "INSERT INTO `CysticAirwaves_replies` (
`AirwaveID`,
`FromUserID`,
`comment`,
`status`,
`date`,
`time`
) VALUES (
'" . mysql_real_escape_string($_POST['comment']) . "',
'" . $auth->id . "',
'" . mysql_real_escape_string($_POST['reply']) . "',
'active',
'" . date("Y-m-d") . "',
'" . date("G:i:s") . "'
)";
mysql_query($query,$connection);
$mailto = array();
/* get the person that wrote the inital comment */
$query = "SELECT `FromUserID` FROM `CysticAirwaves` WHERE `id` = '" . mysql_real_escape_string($_POST['comment']) . "' LIMIT 1";
$request = mysql_query($query,$connection);
$result = mysql_fetch_array($request);
$comment_author = new User($result['FromUserID']);
thanks in advance
You can perform a select to see how many entries are in the table already by that user for the current date:
SELECT COUNT(*)
FROM CysticAirwaves
WHERE userid = $auth->id
AND date = CURDATE()
Then only perform the INSERT if the number is below your threshold. Alternatively, you can place a trigger on the INSERT that does this check with every INSERT and bounces the call as well. ("Best practice" would be to place it in the database as this would be a database-related limitation, but that's your call)
It's been a while since I've done MySQL triggers, but I think think is what you're after:
delimeter |
CREATE TRIGGER reply_threshold BEFORE INSERT ON CysticAirwaves_replies
FOR EACH ROW BEGIN
DECLARE reply_count INT;
SET reply_count = (SELECT COUNT(*) FROM CysticAirwaves_replies WHERE userid = NEW.userid AND `date` = CURDATE());
IF reply_count > 5 THEN
SIGNAL SQLSTATE SET MESSAGE_TEXT = 'Too many replies for today';
END IF;
END;
|
delimeter ;
Essentially, if you go to insert a reply in the table and the threshold has been exceeded, a sql error will be raised stopping the action. You can't "prevent" an insert per-say, but you can raise an exception that makes it fall-through.
You can only limit this by the ip address when you don't have a login system. But the ip can change and this is here the problem.
The best way is to secure the form by a login. That only user can post when they are logged in.
Last technique is to use a captcha like Recaptcha then at most time bots fill out your form and spam to your system.
When you have a login. Then make a table related to your usertable and count the INSERTS. Before you INSERT a new comment check the table if there was a INSERT today.
Before to insert the comment, you check if the user has posted more than 5 comments in the day.
If yes, you don't insert the comment and you display a message.
SELECT COUNT(*) FROM CysticAirwaves_replies WHERE FromUserID = the_user_id AND date = CURDATE()
Besides counting before each insertion, you can store the number of comments made by an user somewhere directly, so you don't have to do the count(*) every time (which can be expensive if an user has lots of comments and the table you have is somewhat big).
Like, on commenting:
SELECT comment_count FROM comment_count_table WHERE user_id = ?
If that value is small enough, you do:
UPDATE comment_count_table SET comment_count = comment_count + 1 WHERE user_id = ?
Be careful with this since you'd need to reset that counter somehow.
At my company we implemented this setting a "last modified field". When we do the SELECT, if the "last modified day" is not today, then we reset the counter.
Another option is to have a cron job that resets the counter for all users once every day, but that is way too expensive.

Categories