I'm trying to use SELECT COUNT(*) in a prepared statement.
Below is my PHP:
if(!($stmt = $link->prepare("SELECT COUNT(*) AS failed FROM LoginAttempts WHERE (email = ? OR IP = ?) AND LastLogin BETWEEN now() and subdate(now(),INTERVAL 5 MINUTE)"))){
}
$stmt->bind_param('ss', $email, $ip);
$stmt->execute();
$stmt->bind_result($failed);
$stmt->close();
What I'm trying to achieve from this, is when a user tries to login to their account (and provides incorrect login information) their IP, and the entered email address is logged in a table.
When an attempt is tried to login, using their email or IP, I count how many records match either their IP or email address.
The issue I'm having, is when accessing $failed the result is NULL even though there are records in the database within the last 5 minutes.
Where exactly am I going wrong? There are no errors in my apache error log, or with:
error_reporting(E_ALL);
ini_set('display_errors',1);
Thanks for your time
Edit: I needed to use $stmt->fetch(); - not sure how I missed that. Thanks to Saty for your comment!
The issue is with between clause
SELECT COUNT(*) AS failed FROM LoginAttempts
WHERE (email = ? OR IP = ?) AND LastLogin
BETWEEN subdate(now(),INTERVAL 5 MINUTE) and now()
try this and let me know
Few mistake in your code
1) Not looking for errors
2) Close if condition at the end
3) Forget to fetch data form query result
You code would be
if (!($stmt = $link->prepare("SELECT COUNT(*) AS failed FROM LoginAttempts WHERE (email = ? OR IP = ?) AND LastLogin BETWEEN now() and subdate(now(),INTERVAL 5 MINUTE)"))) {
/* bind parameters for markers */
$stmt->bind_param('ss', $email, $ip);
/* execute query */
$stmt->execute();
/* bind result variables */
$stmt->bind_result($failed);
/* fetch value */
while ($stmt->fetch()) {
printf("%s", $failed);
}
/* close statement */
$stmt->close();
}
A few errors in here, but mainly you forgot to fetch the result after binding it to a variable. bind_param basically indicates the variable to place the values from the result set it does not actually do the retrieval of a row from the result set, you need some sort of ->fetch() to do that
Its a good idea to test all the status's of almost all mysqli_ api calls and output the error somewhere, they are normally very useful and fairly precise. A great aid in debugging a complex query
$stmt = $link->prepare("SELECT COUNT(*) AS failed
FROM LoginAttempts
WHERE (email = ? OR IP = ?)
AND LastLogin BETWEEN now() and subdate(now(),INTERVAL 5 MINUTE)");
if ( $stmt === FALSE ) {
echo $link->error;
exit;
}
$stmt->bind_param('ss', $email, $ip);
$result = $stmt->execute();
if ( $result === false ) {
echo $link->error;
exit;
}
$stmt->bind_result($failed);
$stmt->fetch(); // this actually get the value out of the result set into $failed
$stmt->close();
Related
I'm following an API tutorial (user authentication), but i'm fairly new to SQL, so there are some stuff that i dont understand.
I've been looking for an answer and as far as i know, the LIMIT clause has an offset (0 in this case) and a count (1 in this case).
This is the code (inside the user class):
function emailExists(){
// query to check if email exists
$query = "SELECT id, firstname, lastname, password
FROM " . $this->table_name . "
WHERE email = ?
LIMIT 0,1";
// prepare the query
$stmt = $this->conn->prepare( $query );
// sanitize
$this->email=htmlspecialchars(strip_tags($this->email));
// bind given email value
$stmt->bindParam(1, $this->email);
// execute the query
$stmt->execute();
// get number of rows
$num = $stmt->rowCount();
// if email exists, assign values to object properties for easy access and use for php sessions
if($num>0){
// get record details / values
$row = $stmt->fetch(PDO::FETCH_ASSOC);
// assign values to object properties
$this->id = $row['id'];
$this->firstname = $row['firstname'];
$this->lastname = $row['lastname'];
$this->password = $row['password'];
// return true because email exists in the database
return true;
}
// return false if email does not exist in the database
return false;
}
What i understand is that the query starts looking for a similar email from the start (row 0), but then i don't understand why they bind the 1 to the email.
Is it necessary to use a LIMIT here? why can't just use the clause WHERE email = :email (and bind the :email to the email sent by the user?)
Query checks whether email exists only. If there is more records with the same email it takes more resources to execute statement. If there is at least one record it means email exists. No need to check more.
This case doesn't show it clearly but imagine you have table with millions of records and you want to check whether one specific value exists which can appear in multiple records. You can freeze database if query is too complicated /too many tables are joint etc. You need only to check, so you limit it to 1. This is nice practice to this type of queries.
Here is some background information on what I'm trying to do here. I'm try to create a registration form for my website (successful so far until this point). Data can be entered into the DB. The only thing I'm trying to do now is prevent duplicate email/usernames from being entered into the db. Through much stackoverflow research, I have found and tested the following code:
$query = "SELECT COUNT(*) AS num_rows FROM users WHERE email = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $email);
if ($stmt->execute()) {
return $stmt->num_rows;
}
What I then do is following:
if(user_exists($user_email) > 0) {
echo "Email already exists!";
}
But is passes by this if statement as if the email does exist in the database!
The email I'm trying to enter, for my tests is testemail#testemailweb.com which is already in the database! Would someone possibly point out where I have messed up in my code? Is there a silly mistake that I could have possibly done when trying to perform this?
The fix for your particular problem here is by not using COUNT(*) as mentioned by John and not depend on mysqli_stmt->num_rows by using a buffered result set:
$query = "SELECT * FROM users WHERE email = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $email);
return $stmt->execute() && $stmt->store_result() && $stmt->num_rows > 0;
Addendum
The only thing I'm trying to do now is prevent duplicate email/usernames from being entered into the db
You will want to use table constraints to prevent this (not just from the app, but anything else that can access the database). This has the added benefit of guarding against race conditions.
ALTER TABLE users ADD UNIQUE(email);
This will raise an error if you attempt to insert a row with an email value that already exists. You can check for this error on the application side and do whatever you want with it.
Your query will always return 1 row. COUNT(*) will return a result set even if only to report no rows match your query. As a result user_exists() always returns 1.
Change your query to:
$query = "SELECT * FROM users WHERE email = ?";
Now if no rows match your query $stmt->num_rows will be 0 so user_exists() will return 0.
change:
if ($stmt->execute()) {
return $stmt->num_rows;
}
What I then do is following:
if(user_exists($user_email) > 0) {
echo "Email already exists!";
}
to
$j=0;
if ($stmt->execute()) {
$j= $stmt->num_rows;
} else {
echo "Email already exists!";
}
This question already has answers here:
PHP MYSQLI number of rows doesnt work no errors
(3 answers)
Closed 6 years ago.
I don't understand why $amountOfUsers is showing as 0?
This used to work before I moved to the bind_param function... I was only using query() instad of prepare. But this is a lot safer, I just have trouble understand why this doesn't work, and how to fix it.
$stmt = $mysqli->prepare("SELECT id, expire, status, username FROM username WHERE username= ?");
$stmt->bind_param('s', $username);
$stmt->execute();
//Counting results. 0 = Invalid, 1 = Valid
$amountOfUsers = $stmt->num_rows;
The error I am getting is: $amountOfUsers isn't counting the number of results properly.
$stmt = $mysqli->prepare("SELECT id, expire, status, username FROM username WHERE username= ?");
$stmt->bind_param('s', $username);
$stmt->execute();
// Store the result (so you can get the properties, like num_rows)
$stmt->store_result();
// Get the number of rows
$amountOfRows = $stmt->num_rows;
// Bind the result to variables
$stmt->bind_result($id, $expire, $status, $db_username);
// Process the variables
while($stmt->fetch()) {
printf("%d %s %s %s\n", $id, $expire, $status, $db_username);
}
Sometimes things don't go according to plan. Checking result codes and errors available in your library is usually more efficient for troubleshooting than asking strangers, but hopefully this stranger can help... choose one of these patterns:
A:
$result = $stmt->execute();
if (!$result) { /* handle errors */ }
B:
$stmt->execute();
if ($stmt->errno != 0) { /* handle errors */ }
C (for development troubleshooting only, not code you would leave around):
$stmt->execute();
print_r($stmt->error_list);
More info here and associated pages:
http://www.php.net/manual/en/mysqli-stmt.errno.php
I would never in my life understand why php users are so inclined to the number of rows returned.
Especially if used only as a flag... if any data returned!
Why not to take the very returned data and see?
$sql ="SELECT id, expire, status, username FROM username WHERE username= ?s";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('s', $username);
$stmt->execute();
$res = $stmt->get_result();
$row = $res->fetch_assoc();
if ($row)
{
// do whatever
}
I would never understand an inclination to long and windy codes as well.
Why not to get yourself an abstraction library and get everything in one single line?
$sql = "SELECT id, expire, status, username FROM username WHERE username= ?";
if ($row = $db->getRow($sql))
{
// do whatever
}
I have a need to evaluate if a user logged in to a system after a specific date. To do this, there are three tables in a MySQL database, users, survey and logins. Survey holds the date of a point in time that needs compared against the users last log in. Here's the question.
When I used the "?" placeholder, the resulting num_rows count was always 0. But when I assign the values before handing the query statement to $mysqli->prepare(), the process works as expected. Somehow, store_result() was not picking up the column. Here is my code:
if (isset($userId)){
//get survey release date
$res3 = $mysqli->query("SELECT sur_date,sur_url FROM survey ORDER BY sur_id DESC limit 1");
$array = $res3->fetch_assoc();
$theta_date = $array['sur_date'];
//$theta_date = "2013-01-18 01:00:00";
//this didn't generate errors, but didn't output the correct result either.
//$query = "SELECT login_id FROM logins WHERE login_user=? AND login_date>=?";
//if ($stmt = $mysqli->prepare($query)){
// $stmt->bind_param('ss',$userID,$theda_date);
// $stmt->execute();
//this works
$query = "SELECT login_id FROM logins WHERE login_user='$userId' AND login_date>='$theta_date'";
if ($stmt = $mysqli->prepare($query)){
$stmt->execute() or die("The query did not work");
//if number is greater than 0 do something
$stmt->store_result();
printf("The number of login ids after theta are %d",$stmt->num_rows);
$stmt->close();
}else{
echo "The query did not execute.";
}
}else{
echo "The User ID was not valid.";
exit();
}
$mysqli->close();
Any insight would be helpful,
The prepared statement seems to be having an issue with the $theta_date datetime format. $theta_date is stored in the survey table as '2013-01-18 01:00:00'. bind_param() was trying to parse $theta_date as a reference. Here is the solution:
//convert datetime to Unix timestamp with strtotime()
$theta_date = $array['sur_date'];
$timestamp = strtotime($theta_date);
//In the prepared statement, use MySQL FROM_UNIXTIME function to convert timestamp
$query = "SELECT login_id FROM logins WHERE login_user=? AND login_date>=FROM_UNIXTIME(?)";
//changed the parameter types to integer and bound $timestamp to second placeholder
if ($stmt = $mysqli->prepare($query)){
$stmt->bind_param('ii',$userId,$timestamp);
$stmt->execute();
//the rest is the same
$stmt->store_result();
printf("The number of login ids after theta are %d",$stmt->num_rows);
$stmt->close();
}
That was a pain.
I am trying to select from a mySQL table using prepared statements. The select critera is user form input, so I am binding this variable and using prepared statements. Below is the code:
$sql_query = "SELECT first_name_id from first_names WHERE first_name = ?";
$stmt = $_SESSION['mysqli']->prepare($sql_query);
$stmt->bind_param('s', $_SESSION['first_name']);
$stmt->execute();
$stmt->store_result();
if ($stmt->num_rows == '1') {
$stmt->bind_result($_SESSION['first_name_id']);
$stmt->fetch();
} else {
$stmt->close();
$sql_query = "INSERT INTO first_names (first_name) VALUES (?)";
$stmt = $_SESSION['mysqli']->prepare($sql_query);
$stmt->bind_param('s', $_SESSION['first_name']);
$stmt->execute();
$_SESSION['first_name_id'] = $_SESSION['mysqli']->insert_id;
}
$stmt->close();
Obviously my code is just determining whether or not the first_name already exists in the first_names table. If it does, it returns the corresponding ID (first_name_id). Otherwise, the code inserts the new first_name into the first_names table and gets the insert_id.
The problem is when a user enters a name with an escape character ('Henry's). Not really likely with first names but certainly employers. When this occurs, the code does not execute (no select or insert activity in the log files). So it seems like mySQL is ignoring the code due to an escape character in the variable.
How can I fix this issue? Is my code above efficient and correct for the task?
Issue #2. The code then continues with another insert or update, as shown in the code below:
if (empty($_SESSION['personal_id'])) {
$sql_query = "INSERT INTO personal_info (first_name_id, start_timestamp) VALUES (?, NOW())";
} else {
$sql_query = "UPDATE personal_info SET first_name_id = ? WHERE personal_info = '$_SESSION[personal_id]'";
}
$stmt = $_SESSION['mysqli']->prepare($sql_query);
$stmt->bind_param('i', $_SESSION['first_name_id']);
$stmt->execute();
if (empty($_SESSION['personal_id'])) {
$_SESSION['personal_id'] = $_SESSION['mysqli']->insert_id;
}
$stmt->close();
The issue with the code above is that I cannot get it to work at all. I am not sure if there is some conflict with the first part of the script, but I have tried everything to get it to work. There are no PHP errors and there are no inserts or updates showing in the mySQL log files from this code. It appears that the bind_param line in the code may be where the script is dying...
Any help would be very much appreciated.
you should validate/escape user input before sending it to the db.
checkout this mysql-real-escape-string()