A user will request a file by number via a URL like script.php?userid=222. This example would show the record of file #222.
Now I want to limit the number of files per (remote IP) user to a maximum of 5 different records in a minute. However, the user should be able to access the same id record any number of time.
So the user could access file #222 any number of times, but if the (remote IP) user accesses more than 5 other different records in a minute, then it should show an error.
For example, suppose within a minute the following requests are made:
script.php?userid=222
script.php?userid=523
script.php?userid=665
script.php?userid=852
script.php?userid=132
script.php?userid=002
then at the last request it should show the error message.
Here is the basic code:
$id = $_GET['userid'];
if (!isset($_GET['userid']) || empty($_GET['userid'])) {
echo "Please enter the userid";
die();
}
if (file_exists($userid.".txt") &&
(filemtime($userid.".txt") > (time() - 3600 * $ttime ))) {
$ffile = file_get_contents($userid.".txt");} else {
$dcurl = curl_init();
$ffile = fopen($userid.".txt", "w+");
curl_setopt($dcurl, CURLOPT_URL,"http://remoteserver.com/data/$userid");
curl_setopt($dcurl, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($dcurl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
curl_setopt($dcurl, CURLOPT_TIMEOUT, 50);
curl_setopt($dcurl, CURLOPT_FILE, $ffile);
$ffile = curl_exec($dcurl);
if(curl_errno($dcurl)) // check for execution errors
{
echo 'Script error: ' . curl_error($dcurl);
exit;
}
curl_close($dcurl);
$ffile = file_get_contents($userid.".txt");
}
Instead of relying on the IP address, you could use the session mechanism. You can create a session scope via session_start(), and then store information that sticks with the same user session.
I would then suggest to keep in this session scope the list of unique IDs used in previous requests that user made, together with the time of the request, ignoring any repeated requests, which are always allowed. As soon as this list contains 5 elements with a time stamp within the last minute and a new ID is requested, you show the error and refuse the lookup.
Here is the code that does this. You should place it right after you have checked the presence of the userid argument, and before the retrieval of the file contents:
// set the variables that define the limits:
$min_time = 60; // seconds
$max_requests = 5;
// Make sure we have a session scope
session_start();
// Create our requests array in session scope if it does not yet exist
if (!isset($_SESSION['requests'])) {
$_SESSION['requests'] = [];
}
// Create a shortcut variable for this array (just for shorter & faster code)
$requests = &$_SESSION['requests'];
$countRecent = 0;
$repeat = false;
foreach($requests as $request) {
// See if the current request was made before
if ($request["userid"] == $id) {
$repeat = true;
}
// Count (only) new requests made in last minute
if ($request["time"] >= time() - $min_time) {
$countRecent++;
}
}
// Only if this is a new request...
if (!$repeat) {
// Check if limit is crossed.
// NB: Refused requests are not added to the log.
if ($countRecent >= $max_requests) {
die("Too many new ID requests in a short time");
}
// Add current request to the log.
$countRecent++;
$requests[] = ["time" => time(), "userid" => $id];
}
// Debugging code, can be removed later:
echo count($requests) . " unique ID requests, of which $countRecent in last minute.<br>";
// if execution gets here, then proceed with file content lookup as you have it.
Deleted session cookies...
Sessions are maintained by cookies on the client. The user may delete such cookies, an so get a new session, which would allow the user to make new requests without regard of what was previously requested.
One way to get around this is to introduce a cool-down period for each new session. For instance, you could have them wait for 10 seconds before a first request can be made. To do that, replace in above code:
if (!isset($_SESSION['requests'])) {
$_SESSION['requests'] = [];
}
By:
$initial_delay = 10; // 10 seconds delay for new sessions
if (!isset($_SESSION['requests'])) {
$_SESSION['requests'] = array_fill(0, $max_requests,
["userid" => 0, "time" => time()-$min_time+$initial_delay]
);
}
This is of course less user friendly as it affects any new session, also of users who are not trying to cheat by deleting cookies.
Registration
The better way is to only allow the lookup services to registered users. For this you must provide a user database and authentication system (for example password based). The requests should be logged in the database, keyed by the user's ID. If then a new session starts, the user must first authenticate again, and once authenticated the request history is retrieved from the database. This way the user cannot cheat around it by changing something on their client configuration (IP address, cookies, employing multiple devices in parallel, ...)
<?php
// session_start();
// session_destroy();
// exit;
echo index();
function index()
{
$id = rand(000,020);
$min_time = 60;
$max_requests = 5;
// $id = 0;
session_start();
$repeat = false;
if(!isset($_SESSION["countRecent"]) && !isset($_SESSION["countRecent"]) && !isset($_SESSION["countRecent"])){
$_SESSION["countRecent"] = 1;
$_SESSION["time"] = time();
$_SESSION['userid'][] = $id;
}else{
if ($_SESSION["countRecent"] >= $max_requests) {
if(!in_array($id,$_SESSION['userid'])){
if ($_SESSION["time"] <= time() - $min_time) {
$_SESSION["countRecent"] = 1;
$_SESSION["time"] = time();
}else{
return("Too many requests in a short time wait ". ( $_SESSION["time"] - (time() - $min_time) )). " Seconds";
}
}
}else{
if(!in_array($id,$_SESSION['userid'])){
$_SESSION["countRecent"] = $_SESSION["countRecent"] + 1;
$_SESSION['userid'][] = $id;
}
}
}
return "Your Result goes here.. id: $id Counts: ". $_SESSION["countRecent"];
}
Try this. Fast , low memory usage
But not secure;
also use database
<?php
$conn = mysqli_connect("localhost", "root", "","db_name") or die("Could not connect database");
$id = rand(0,99);
// $id = 100;
echo index($id);
function index($id,$user=1,$ip='192.168.0.10',$max_requests = 5,$min_time = 20)
{
global $conn;
$time = time();
$req = "INSERT INTO `limit_api_by_ip2`(`id`, `ip`, `time`, `user`, `req`)
VALUES (null,INET_ATON('$ip'),'$time','$user',1)
ON DUPLICATE KEY UPDATE req=req+1;";
$req2 = "INSERT INTO `limit_api_by_ip2`(`id`, `ip`, `time`, `user`, `req`)
VALUES (null,INET_ATON('$ip'),'$time','$user',1)
ON DUPLICATE KEY UPDATE req=1,`time`='".time()."' ;";
$reqid = "INSERT INTO `limit_api_by_ip2_count`(`id`, `user`, `ids`) VALUES (null,'$user',$id)";
$getid = "SELECT `ids` FROM `limit_api_by_ip2_count` WHERE user = $user and ids = $id limit 1;";
$gettime = "SELECT `time`,`req` FROM `limit_api_by_ip2` WHERE user = $user and ip = INET_ATON('$ip') limit 1;";
// $id = 0;
$q = mysqli_query($conn,$getid);
$c = mysqli_num_rows($q);
if($c==0){
$get_time = mysqli_query($conn,$gettime);
$c1 = mysqli_num_rows($get_time);
if($c1==0){
mysqli_query($conn,$req);
mysqli_query($conn,$reqid);
}else{
$row = mysqli_fetch_row($get_time);
if ($row[1] >= $max_requests) {
if ($row[0] <= (time() - $min_time)) {
mysqli_query($conn,$req2);
mysqli_query($conn,$reqid);
}else{
return "Too many requests in a short time wait ".($row[0]-(time() - $min_time))." Seconds";
}
}else{
mysqli_query($conn,$req);
mysqli_query($conn,$reqid);
}
}
}else{
}
if(isset($row[1]))
{
$cc = "Counts: ".$row[1];
$dd = "new id: $id";
}else{
$cc = '';
$dd = "old id: $id";
}
return "Your Result goes here.. $dd ".$cc;
}
Related
The error that I occurred:
Fatal error: Call to a member function bind_param() on boolean in C:\wamp64\www\APU\SDP\reg-list-function.php on line 82
I'm writing a php script where the Admins are able to approve the registration of the user. I've checked through the formats of my database, column names, and even query, and still I've no idea why this error pops out. Any help or suggestions will be appreciated!
<?php
// we will only start the session with session_start() IF the session isn"t started yet //
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
?>
<?php
// including the conn.php to establish connection with database //
include "conn.php";
?>
<?php
// Begin of the function: Registration List's Verification Form: Registration //
// First we check the form has submitted or not //
if (isset($_POST['submit-list-reg'])) {
// If it is, then we will retreive data from the input forms //
$regid = $_POST["regid"];
$reg_acccode = mysqli_real_escape_string($con, $_POST['reg-acccode']);
$reg_pw = mysqli_real_escape_string($con, $_POST['reg-pw']);
// Taking the current time //
date_default_timezone_set("Etc/GMT-8");
$now = date("Y-m-d H:i:s");
// Variable to store Error Message //
$error = '';
// Alphanumeric Generator //
function random_strings($length_of_string) {
// String of all alphanumeric character
$str_result = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
// Shufle the $str_result and returns substring
// of specified length
return substr(str_shuffle($str_result), 0, $length_of_string);
}
// Sorting out the query related to the function //
// Verify the user is an admin or not //
$VERFYADMIN = "SELECT * FROM user
WHERE status = 2 AND active = 1 AND account_code = '".md5($reg_acccode)."' AND password = '".md5($reg_pw)."'";
$VERFYADMINQ = mysqli_query($con, $VERFYADMIN);
//***BEGIN OF PROCESS***//
if (mysqli_num_rows($VERFYADMINQ) < 1) {
// if the admin is not verified, then inform the user and send him back to admin panel //
echo "<script>alert('ALERT: Information unable to be verified. Please try again.');";
echo "window.location.href='admin_panel.html';</script>";
exit(0);
} else {
// begin the process of registration //
while (list($key,$val) = #each ($regid)) {
// Now to verify the user's legitimacy //
// Take the user's vercode into variable first //
$USERVERCODE = "SELECT * FROM registration_list
WHERE registration_id = $val AND verified = 0";
$USERVERCODEQ = mysqli_query($con, $USERVERCODE);
if (mysqli_num_rows($USERVERCODEQ) < 1) {
// if we are unable to retrieve the data of the registering user then something must gone wrong //
echo "<script>alert('WARNING: Unable to retrieve the data. Please try again.');";
echo "</script>";
} else {
while ($row = mysqli_fetch_array($USERVERCODEQ)) {
$vercode = $row["verification_code"];
}
// since we got the value of the vercode then we start to define the query //
$VERCODE = "SELECT * FROM verification_code WHERE verification_code = $vercode AND code_active = 1";
$VERCODEQ = mysqli_query($con, $VERCODE);
if (mysqli_num_rows($VERCODEQ) < 1) {
// if we are unable to retrieve the data of the registering user then something must gone wrong //
echo "<script>alert('WARNING: Unable to retrieve the info of VERCODE. Please try again.');";
echo "</script>";
} else {
while ($row = mysqli_fetch_array($VERCODEQ)) {
$status = $row["code_status"];
}
// we will first insert the user main information into the database: i.e. password, username, etc. //
$account_code = random_strings(8);
$APPROVE = "INSERT INTO user (username, password, email, account_id, account_code, active, status, registered_date, verification_code)
SELECT username, password, email, account_id, '".md5($account_code)."', 1, $status, $now, verification_code
FROM registration_list
WHERE registration_id = ?";
$stmt = $con->prepare($APPROVE);
$stmt->bind_param("i", $val); // Problem around here //
$stmt->execute();
if (($stmt->error) == FALSE) {
I expect the process will be no issue at all as I've checked everything and nothing seems wrong to me.
Reformatting your code to make it more legible and easier to understand, we now have:
<?php
// we will only start the session with session_start() IF the session isn"t started yet //
if (session_status() == PHP_SESSION_NONE)
{
session_start();
}
?>
<?php
// including the conn.php to establish connection with database //
include "conn.php";
?>
<?php
// Begin of the function: Registration List's Verification Form: Registration //
// First we check the form has submitted or not //
if (isset($_POST['submit-list-reg']))
{
// If it is, then we will retreive data from the input forms //
$regid = $_POST["regid"];
$reg_acccode = mysqli_real_escape_string($con, $_POST['reg-acccode']);
$reg_pw = mysqli_real_escape_string($con, $_POST['reg-pw']);
// Taking the current time //
date_default_timezone_set("Etc/GMT-8");
$now = date("Y-m-d H:i:s");
// Variable to store Error Message //
$error = '';
// Alphanumeric Generator //
function random_strings($length_of_string)
{
// String of all alphanumeric character
$str_result = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
// Shufle the $str_result and returns substring
// of specified length
return substr(str_shuffle($str_result), 0, $length_of_string);
}
// Sorting out the query related to the function //
// Verify the user is an admin or not //
$VERFYADMIN = "SELECT * FROM user
WHERE status = 2 AND active = 1 AND account_code = '".md5($reg_acccode)."' AND password = '".md5($reg_pw)."'";
$VERFYADMINQ = mysqli_query($con, $VERFYADMIN);
//***BEGIN OF PROCESS***//
if (mysqli_num_rows($VERFYADMINQ) < 1)
{
// if the admin is not verified, then inform the user and send him back to admin panel //
echo "<script>alert('ALERT: Information unable to be verified. Please try again.');";
echo "window.location.href='admin_panel.html';</script>";
exit(0);
}
else
{
// begin the process of registration //
while(list($key,$val) = #each ($regid))
{
// Now to verify the user's legitimacy //
// Take the user's vercode into variable first //
$USERVERCODE = "SELECT * FROM registration_list WHERE registration_id = $val AND verified = 0";
$USERVERCODEQ = mysqli_query($con, $USERVERCODE);
if (mysqli_num_rows($USERVERCODEQ) < 1)
{
// if we are unable to retrieve the data of the registering user then something must gone wrong //
echo "<script>alert('WARNING: Unable to retrieve the data. Please try again.');";
echo "</script>";
}
else
{
while ($row = mysqli_fetch_array($USERVERCODEQ))
{
$vercode = $row["verification_code"];
}
// since we got the value of the vercode then we start to define the query //
$VERCODE = "SELECT * FROM verification_code WHERE verification_code = $vercode AND code_active = 1";
$VERCODEQ = mysqli_query($con, $VERCODE);
if (mysqli_num_rows($VERCODEQ) < 1)
{
// if we are unable to retrieve the data of the registering user then something must gone wrong //
echo "<script>alert('WARNING: Unable to retrieve the info of VERCODE. Please try again.');";
echo "</script>";
}
else
{
while ($row = mysqli_fetch_array($VERCODEQ))
{
$status = $row["code_status"];
}
// we will first insert the user main information into the database: i.e. password, username, etc. //
$account_code = random_strings(8);
$APPROVE = "INSERT INTO user (username, password, email, account_id, account_code, active, status, registered_date, verification_code)
SELECT username, password, email, account_id, '".md5($account_code)."', 1, $status, $now, verification_code
FROM registration_list
WHERE registration_id = ?";
$stmt = $con->prepare($APPROVE);
$stmt->bind_param("i", $val); // Problem around here //
$stmt->execute();
if (($stmt->error) == FALSE)
{
In here are several things that I wouldn't personally do. As has been mentioned, using variables supplied by user input, even MD5 ones, directly in SQL queries should be best avoided.
The line "while(list($key,$val) = #each ($regid))", which sets the $val variable has an ampersand to suppress any error messages, this in turn could be causing you issues further down. It's best not to suppress these messages, but to find out why they are occurring, this could be the cause of a non numeric value being passed to your "bind_param" function. I'd also use single quotes instead of double quotes with the function as well.
Solved after I changed the variables that contained string value with this format -> ' " . $variable . " ' .
I am using a simple script at the top of every page that will update a LastActive column in the database:
$username = $_SESSION['username'];
$userID = $_SESSION['user_id'];
if(isset($username, $userID)) {
if ($insert_stmt = $mysqli->prepare("UPDATE Users SET lastActive = DATE_ADD(Now(), interval 6 hour) WHERE username = ?")) {
$insert_stmt->bind_param('s', $username);
// Execute the prepared query.
if (! $insert_stmt->execute()) {
$insert_stmt->close();
header('Location: ../headers/error.php?err=Failed Upload');
}
}
$insert_stmt->close();
}
I always want to keep performance and security in mind. Would this lead to poor performance in the future with 000's of connections?
How does using cookies (not that I know how) differ from a simple script like this?
Thanks
edit:
$username = $_SESSION['username'];
$userID = $_SESSION['user_id'];
$loginTime = $_SESSION['timestamp'];
date_default_timezone_set("Europe/London");
$now = new DateTime();
$diff=$now->diff($loginTime);
$minutes = $diff->format(%i);
if(isset($username, $userID) && $minutes> 30) {
$_SESSION['timestamp'] = $now;
$online = true;
}
Couple of suggestions:
You could do this via AJAX, so that the LastVisited is updated asynchronously after the user's page loads. That way, there won't be any impact to the page load time for the user.
If, for any reason, your SQL query fails, you should fail silently. Since recording Last Visited is not business critical, you should not redirect the user to an error page. Maybe just log an error, and set up an alert so if there are multiple failures, you get alerted and can take a look at it.
All that you made with cookies will be data supplied by your users, then you cannot trust it.
In other hand, if you work with cookies, all of them will travel in each request header.
You should do it in server side and yes, a database is not performant.
You can try to persist this information with something like Redis, a in-memory data structure store, used as database, cache and message broker.
I thought I'd post the way I got around this for any one else looking for a User Online type method. Of course this might have been done much better but works in my situation.
I am using both database entries and session to test if a user is online.
On user login I update a column in my users table with a Now() timestamp and add this to their session data.
At the top of each page I am running a script to check if the user is logged in and get their timestamp from session data. if this data is 45 minutes old, the script will update the table setting the lastActive column of my users table to Now();
<?php
include_once 'functions.php';
if(isset($_SESSION['username'], $_SESSION['user_id'], $_SESSION['lastActive'])) {
date_default_timezone_set("Europe/London");
$now = new DateTime();
$lastActive = $_SESSION['lastActive'];
$diff=$now->diff($lastActive);
$hours = $diff->format('%h');
$mins = $diff->format('%i');
$day = $diff->format('%d');
$month = $diff->format('%m');
$year = $diff->format('%y');
if($mins > 45 || $hours >= 1 || $day >= 1 || $month >= 1 || $year >= 1) {
$_SESSION['lastActive'] = $now;
set_last_active($mysqli, $_SESSION['username']);
}
}
set_latst_action is simply just:
function set_last_active($mysqli, $username) {
if ($stmt = $mysqli->prepare("UPDATE Users SET lastActive = Now() WHERE username = ?")) {
$stmt->bind_param('s', $username);
$stmt->execute();
$stmt->close();
}
}
then when I want to see if a user is online for example on a profile page I call isOnline();
function isOnline($mysqli, $username) {
if ($stmt = $mysqli->prepare("SELECT lastActive FROM Users WHERE username = ? LIMIT 1")) {
$stmt->bind_param('s', $username);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows == 1) {
$stmt->bind_result($return);
$stmt->fetch();
$lastActive = $return;
} else {
// user does not exist
$lastActive = "";
return $lastActive;
$stmt->close();
}
} else {
// SELECT failed
$lastActive = "";
return $lastActive;
$stmt->close();
}
if (!empty($lastActive)) {
date_default_timezone_set("Europe/London");
$dateNow = new DateTime;
$lastActiveDate = new DateTime($lastActive);
$diff=$dateNow->diff($lastActiveDate);
$hours = $diff->format('%h');
$mins = $diff->format('%i');
$day = $diff->format('%d');
$month = $diff->format('%m');
$year = $diff->format('%y');
if ($mins > 45 || $hours >= 1 || $days >= 1 || $month >= 1 || $year >= 1) {
$return = "Offline";
return $return;
}
else {
$return = "Online";
return $return;
}
}
else {
$return = "Offline";
return $return;
}
}
I'm writing a simple password-recovery function for the website I'm developing and I was wondering about the expire time.
Getting to the point, I want to add an expire time of around 48h for the reset password link I'm gonna send. Do I have to create a new column to store the current time and check it out some time later to see if its still valid, or is there a simpler way?
That's my code so far:
public function forgotPass($email) {
$bd = new Bd();
$conn = $bd->connect();
$stt = $conn->prepare("SELECT * FROM Users where email=?");
$stt-> bind_param("s",$email);
$stt-> execute();
$result = $stt->get_result();
if (mysqli_num_rows($result) == 1) {
$stt = $conn->prepare("INSERT INTO Users(recovery) VALUES(?)");
$recovery = $this->randHash(8);
if (!$recovery)
return false;
$stt-> bind_param("s",$recovery);
$stt-> execute();
}
}
and here's my randHash code:
private static function randHash($lenght) {
if (!filter_var($lenght, FILTER_VALIDATE_INT)) {
return false;
}
$allowed = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$max = strlen($allowed) - 1;
for ($i=1;$i<=$lenght;$i++) {
$hash .= $allowed[mt_rand(0, $max)];
}
return $hash;
}
Just save the expiration time with the reset token in the database, and when the time has expired just don't accept the reset token anymore. This is by far the easiest and safest method.
Another way would be creating a reset hash, appending the time, and encrypting that with a secret key. Decrypt and check the timestamp when you check the hash. If the key leaked, however, this method becomes as weak as just putting it in plain text in the URL.
Storing the current date in database is one way to go. Then you can easily check if its less then 48 hours off. Another way to go is to include the time in the email.
public function forgotPass($email) {
$bd = new Bd();
$conn = $bd->connect();
$stt = $conn->prepare("SELECT * FROM Users where email=?");
$stt-> bind_param("s",$email);
$stt-> execute();
$result = $stt->get_result();
if (mysqli_num_rows($result) == 1) {
$hash1 = md5(microtime().$email.'xx'); //create a unique number for email
$ctime = time();
$hash2 = md5($hash1.$ctime); //create a unique hash based on hash1 and time
$stt = $conn->prepare("INSERT INTO Users(recovery) VALUES(?)");
$recovery = $hash2;
if (!$recovery)
return false;
$stt-> bind_param("s",$recovery);
$stt-> execute();
//send email with link
// http://www.example.com/resetpass.php?hash=$hash1&time=$ctime
}
}
//and then in resetpass.php
//NEEDS CHECKS FOR VALID QUERYVALUES
if (time()-$_GET['time'] <= 48 * 60 * 60) {
$checkhash = md5($_GET['hash'].$_GET['time']);
//check database for hash
}
EDIT: I received an email this morning regarding this answer and asking whether or not the hashing-part of my answer is just for making a really unique ID. The following is my response:
I went through and re-read the question on Stack Overflow. The code is not just for making a really unique ID (although it is important that there be no collisions). It is also to make it very hard for someone else to write their own password recovery link to gain access to a user account.
By creating a hash with a username and email (and without hashing the entire code), we would be able to include an additional validation on the user's identity. That is, merely having the link wouldn't be enough; a valid username and email address combination would also be needed to reset a password.
Strings are prepended to username, current time and email in a basic attempt to defeat rainbow tables. In retrospect, it would be better to replace the " " salts with base64_encode($row['username']) and base64_encode($email) respectively.
I would suggest creating two columns: recovery and recovery_expiry. Recovery_expiry holds when the link expires, and recovery holds the hash that must be compared. It consists of the username, a salt appended by the current time, and the current email address of the user.
function forgotPass($email)
{
$currTime = time();
$expiryTime = 60 * 60 * 24 * 2; // Two days
$bd = new Bd();
$conn = $bd->connect();
$stt = $conn->prepare("SELECT * FROM Users where email=?");
$stt->bind_param("s", $email);
$stt->execute();
$result = $stt->get_result();
if (mysqli_num_rows($result) == 1)
{
$row = mysqli_fetch_array();
$stt = $conn->prepare("INSERT INTO Users(recovery, recovery_expiry)"
. " VALUES(?,?)");
$hash = hash("sha256", " " . $row['username'])
. hash("sha256", "vivid" . $currTime)
. hash("sha256", " " . $email);
$stt->bind_param("s", $hash);
$stt->bind_param("i", $currTime + $expiryTime);
$stt->execute();
}
else
{
// Return that the given email address did not match any records
}
// Here would be the logic to send the forgotten password link to the user
}
function checkHash($hash)
{
$row = null;
$currTime = time();
$bd = new Bd();
$conn = $bd->connect();
$stt = $conn->prepare("SELECT * FROM Users WHERE recovery=? AND recovery_expiry < $currTime");
$stt->bind_param("s", $hash);
$stt->execute();
$result = $stt->get_result();
if (mysqli_num_rows($result) == 1)
{
$row = mysqli_fetch_array();
}
return $row;
}
I am having some problem with my apache server when handling big amount of traffic. after some optimizations I did. I still have the same problem. I check my log file and it turned out that I have a lot of php processing. The following code is getting processed about 800 times a minute (when I have high traffic) and casing my server to crash.
1) is there any parts of the code that I need to rewrite that would make it take less php processing ?
2) is it a good idea to have all of this code before the html starts ?
<?php
$ip = $_SERVER['REMOTE_ADDR'];
mysql_connect('', '', '');
mysql_select_db('');
if(empty($_GET['i']) == false){
$get_image = mysql_real_escape_string($_GET['i']);
$check_if_image = mysql_query("SELECT `id`, `image_name`, `image_type`, `image_caption`, `image_voteup`, `image_votedown`, `image_views`, `fb_userid` FROM images_new WHERE image_name = '$get_image'");
if(mysql_num_rows($check_if_image) == 1){
$result = mysql_fetch_assoc($check_if_image);
$image_id = $result['id'];
$image_name = $result['image_name'];
$image_type = $result['image_type'];
$image_caption = stripslashes($result['image_caption']);
$image_voteup = $result['image_voteup'];
$image_votedown = $result['image_votedown'];
//$image_date = $result['image_date'];
$image_views = $result['image_views'];
$fb_username = $result['fb_username'];
$fb_userid = $result['fb_userid'];
//next image
$next_image_id = $image_id + 1;
$check_next_image = mysql_query("SELECT `image_name` FROM images_new WHERE id = '$next_image_id'");
if(mysql_num_rows($check_next_image) == 1){
$next_image_result = mysql_fetch_assoc($check_next_image);
$next_image_name = $next_image_result['image_name'];
}
// pre image
$pre_image_id = $image_id - 1;
$check_pre_image = mysql_query("SELECT `image_name` FROM images_new WHERE id = '$pre_image_id'");
if(mysql_num_rows($check_pre_image) == 1){
$pre_image_result = mysql_fetch_assoc($check_pre_image);
$pre_image_name = $pre_image_result['image_name'];
}
//shares, comments, and likes
$fb_page_url = "http://www.xxx.com/images.php?i=".$get_image;
$fb_url = "http://api.facebook.com/restserver.php?method=links.getStats&urls=".urlencode($fb_page_url);
$fb_xml = file_get_contents($fb_url);
$fb_xml = simplexml_load_string($fb_xml);
$fb_shares = $fb_xml->link_stat->share_count;
$fb_likes = $fb_xml->link_stat->like_count;
$fb_likes_and_shares = $fb_likes + $fb_shares;
$fb_comments = $fb_xml->link_stat->commentsbox_count;
//facebook
require_once('scripts/facebook.php');
$config = array('appId' => '','secret' => '');
$params = array('scope'=>'user_likes,publish_actions,email,offline_access,user_birthday');
$facebook = new Facebook($config);
$user = $facebook->getUser();
if($user){
try{
$user_profile = $facebook->api('/me','GET');
$user_id = $user_profile['username'];
$expire_time = time() + 30758400;
//insert cookie id
if (!isset($_COOKIE['id'])){
$cookie_id = $user_profile['username'];
setcookie("id", $cookie_id, $expire_time, '/');
}
//insert cookie name
if (!isset($_COOKIE['name'])){
$user_name = $user_profile['first_name'];
setcookie("name", $user_name, $expire_time, '/');
}
//check if the user like the fan page
$isFan = $facebook->api(array(
"method" => "pages.isFan",
"page_id" => ''
));
}catch(FacebookApiException $e) {
error_log($e->getType());
error_log($e->getMessage());
}
}else{//if no user
if(isset($_COOKIE['name'])){
$user_name = $user_profile['first_name'];
setcookie("name", $user_name, time() - 30758400, '/');
}
}
//increase views
if($facebook->getUser()){
mysql_query("UPDATE images_main SET image_views = image_views + 1 WHERE image_name='$image_name'");
mysql_query("UPDATE images_new SET image_views = image_views + 1 WHERE image_name='$image_name'");
}
}else{//image was not found in the database.
header('Location: index.php');
}
}else{//redirect if get is empty
header('Location: index.php');
}
?>
I would say the key factor is your call to the Facebook API, such things are always expensive and easily cacheable, put that code in a separate page/include and cache it as you like.
Also as a side note, you should consider reducing the number of db queries and you may wish to update your db driver... as invariably everyone points out #Madara Uchiha
I see a few items right off the bat.
First query:
$check_if_image = mysql_query("SELECT `id`, `image_name`, `image_type`, `image_caption`, `image_voteup`, `image_votedown`, `image_views`, `fb_userid` FROM images_new WHERE image_name = '$get_image'");
If you only need one result back, put a 'LIMIT 1' at then end (unless this field has a UNIQUE index, in which case this shouldn't matter). Also make sure this field is indexed and preferably a VARCHAR field instead of TEXT or BLOB.
Next, you are running 2 queries to get the previous and next images. I would combine this into 1 query like this:
SELECT `image_name` FROM images_new WHERE id IN ('$next_image_id', '$pre_image_id')
Also, you can apply the first optimization I mentioned to these 2 queries:
if($facebook->getUser()){
mysql_query("UPDATE images_main SET image_views = image_views + 1 WHERE image_name='$image_name'");
mysql_query("UPDATE images_new SET image_views = image_views + 1 WHERE image_name='$image_name'");
}
Lastly, going through the Facebook API is going to add load time that you cannot do much about. Hopefully this gets you started down the right path.
i have already posted a similar question here, but failed to get a response that will fix my problem, also the problem has changed a bit so i'm re-posting and desperate to get some help!
link to previous question:
ajax long polling with mysql
CURRENT CODE:
JS(I run it from php):
$oldIDq = mysql_query("SELECT * FROM messages ORDER BY id DESC LIMIT 1");
while($oldrow = mysql_fetch_array($oldIDq)){
$oldID = $oldrow['id'];
}
$func = '
var oldID = '.$oldID.';
function wait() {
$.ajax({
type: "GET",
url: "../scripts/msg_scripts/msg.php?oldid=" + oldID,
async: true,
cache: false,
success: function (data){
if(data != \'1\'){
var json = eval(\'(\' + data + \')\');
if (json[\'msg_content\'] != "") {
alert("new meassage added");
}
oldID = json[\'oldID\'];
setTimeout(\'wait()\',1000); }
},
disconnect: function()
{
return false;
setTimeout(\'wait()\',1000);
},
error: function(XMLHttpRequest, textStatus, errorThrown){
alert("error: " + textStatus + "(" + errorThrown + ")");
setTimeout(\'wait()\',1000);
}
});
}
$(document).ready(function(){
wait();
});
';
SERVER:
$connect = mysql_connect ("localhost", "root", "")
or die ("couldnt connect");
mysql_select_db ("***") or die ("not found"); //if db was not found die
mysql_query("SET NAMES 'utf8'");
$oldID = $_GET['oldid'];
if($oldID == "") {
die('timeout');
}
else{
$result = mysql_query("SELECT id FROM messages ORDER BY id DESC LIMIT 1");
while($row = mysql_fetch_array($result))
{
$last_msg_id = $row['id'];
}
while($last_msg_id <= $oldID)
{
usleep(10000);
clearstatcache();
$result = mysql_query("SELECT id FROM messages ORDER BY id DESC LIMIT 1");
while($row = mysql_fetch_array($result))
{
$last_msg_id = $row['id'];
}
}
$response = array();
$response['msg_content'] = 'new';
$response['oldID'] = $last_msg_id;
echo json_encode($response);
}
now, i had a session running on the server side of the process and i removed it for now because i understood that long polling has a problem with sessions i also have sessions running on the page which sends the ajax request, since i removed the session my problem has improved in a way, what happens now is that i can basically click on a link on my website and exit the page and get an error, but if i do it more than 4-5 times, the browser freezes an every click on any link just reruns the ajax function and i get a different error. if i refresh the page of the request i imidetly get the second error and the browser freezes. also if that's helpful information if i close the browser and try to reopen any page of my site it doesn't load at all unless i rerun my server(working on localhost right now) also tried it with chrome and ff.
can some one please point me towards the solution?
-- Updated again to retrieve all new messages
Reading your code, you only wish to return the last message, if so then the while loops are in this case pretty useless. Do keep in mind that there might be more 'new' messages between the oldID and the last ID inserted into your database which you skip, so does this code I provide
$connect = mysql_connect ("localhost", "root", "")
or die ("couldnt connect");
mysql_select_db ("***") or die ("not found"); //if db was not found die
mysql_query("SET NAMES 'utf8'");
$oldID = trim($_GET['oldid']);
// empty response, you may fill it with a default msg
$response = array(
'msg_content' => 'none',
'oldID' => $oldID
);
// this if statement will prevent to return a valid
// JSON string to the ajax request
if(empty($oldID)) {
die('timeout');
}
else {
$result = mysql_query("SELECT id FROM messages WHERE id > ".addslashes($oldID)." ORDER BY id DESC");
$index = 1;
// check if results have new messages
if(($num_rows = mysql_num_rows($result) > 0) {
$response['msg_content'] = 'new';
while($row = mysql_fetch_array($result)) {
$response['new_msgs'][] = $row['id']
if($index == $num_rows)
$response['oldID'] = $row['id']; // set oldID to last record
}
}
echo json_encode($response);
--
To your comment on how to use session_write_close properly.
session_start();
$var_id = $_SESSION['id'];
$var_fn = $_SESSION['firstname'];
$var_ln = $_SESSION['lastname'];
$var_mail = $_SESSION['email'];
// close write to session here to release it for other sources
session_write_close();
if (!loggedin()){
header ("Location: index.php");}
if ($_GET['id']) {
$id = mysql_real_escape_string($_GET['id']);}
// you are using session here, use the localized $var_id
else if (isset($var_id)) {
$id = mysql_real_escape_string($var_id);
}
When session_start() is called, the session at that point is locked for writing to any other source, except the current scope (.php file of execution) it's in. This is to make sure that no values can be changed during the readout of session values.
From the documentation
Session data is usually stored after your script terminated without the need to call session_write_close(), but as session data is locked to prevent concurrent writes only one script may operate on a session at any time. When using framesets together with sessions you will experience the frames loading one by one due to this locking. You can reduce the time needed to load all the frames by ending the session as soon as all changes to session variables are done.
To the getting stuck problem, I think the while loop is endless, request the page from your browser http://example.com/pathto/scripts/msg_scripts/msg.php and see what happens
$counter = 0;
while($last_msg_id <= $oldID)
{
usleep(10); // changing to be a bit faster
clearstatcache();
$result = mysql_query("SELECT id FROM messages ORDER BY id DESC LIMIT 1");
$row = mysql_fetch_array($result);
$last_msg_id = $row['id'];
$counter++;
if($counter > 100)
break;
}
echo "counted: {$counter}";
exit();