Prepared statement and null value when using? - php

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.

Related

Using mysqli query object as a set in subsequent query

I am working on a reservation system for a local resort which is accessed via reasonably secure LAN only, as such I am not immediately concerned with SQL injection. My main concern is the functionality to lookup up reservations based upon the PK(s) returned by a query on individual guests. To do this, I have been using the mysqli query object returned by a query of a table containing guest information:
$get_guest_id = "SELECT id FROM guests WHERE fname = '$fname' OR lname = '$lname' OR phone = '$phone' OR email = '$email'";
$guest_id_result = mysqli_query($con, $get_guest_id);
I have tried using this query object as I would a set within mySQL:
$search_by_id = "SELECT * FROM reservation WHERE guest_id IN '$guest_id_result'";
But this does not return as expected:
Returns false on failure. For successful queries which produce a result set, such as SELECT, SHOW, DESCRIBE or EXPLAIN, mysqli_query() will return a mysqli_result object. For other successful queries, mysqli_query() will return true.
Rather than returning true, false, or a result set, the PHP script stops executing at this statement.
Is there a different way to pass the data contained within a mysqli query object to another query, in a manner similar to a set?
The problem is that you're trying to cast an object into a string for your query...
The best solution would probably be to turn the two queries into a singular one, additionally updating the code to use a prepared statement.
Query
SELECT * FROM reservations
WHERE guest_id IN (SELECT id
FROM guests
WHERE fname = ?
OR lname = ?
OR phone = ?
OR email = ?
);
Code
$sql = "
SELECT * FROM reservations
WHERE guest_id IN (SELECT id
FROM guests
WHERE fname = ?
OR lname = ?
OR phone = ?
OR email = ?
);
";
$query = $con->prepare($sql);
$query->bind_param("ssss", $fname, $lname, $phone, $email);
$query->execute();
$result = $query->get_result();
while ($row = $result->fetch_assoc()) {
echo $row["guest_id"], PHP_EOL; // Example output printing the guest_id of guests with reservations (followed by a newline)
}

What is the purpose of 'LIMIT 0,1' on this query?

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.

unable to properly use the resqult of an sql query in php

What I'm trying to do is take the number of rows that meet a certain requirement, and depending on the number that do, the script will do different things.
my code:
$sql2 = "SELECT COUNT(email)FROM subscribers WHERE id= ?";
$stmt2 = $conn->prepare($sql2);
$stmt2->bind_param("s", $id);
$stmt2->execute();
$callbacks = $stmt2->get_result();
while($rows = $callbacks->fetch_assoc()){
$results = $rows;
}
$stmt2->close();
if ($results <2001){
$email = trim($_POST["email"]);
}else{
$email_err = "This user has reached the limmit to what their plan can allow .";
}
The problem I'm having is that I'm unsure of how to actually use the number of rows.
In this configuration, it doesn't care what the number of results is, it just posts them anyways.
If I do something like this id_err = $results; to see what the query result is it gives me an array to string conversion error.
As hardware intensive as it sounds for this kind of operation, if you think should query the rows directly and count them with PHP please show me how.
I have also tried doing some variation on $results = $rows['count'] with different names such as 'total', and 'result' but they still post. when I try to see what the query result with id_err = $results; it flat out ignores it and posts anyways.
to avoid any confusion for on what $id_err is, it stops the script from posting if it ever gets used.
and to clarify this SQL query is within a conditional statement.
thank you in advance.
The count function returns all records as one row with the count.. so remove the while and compare the appropriate index.
$sql2 = "SELECT COUNT(email) as the_count FROM subscribers WHERE id= ?";
$stmt2 = $conn->prepare($sql2);
$stmt2->bind_param("s", $id);
$stmt2->execute();
$callbacks = $stmt2->get_result();
$rows = $callbacks->fetch_assoc();
$stmt2->close();
if ($rows['the_count'] < 2001){
$email = trim($_POST["email"]);
}else{
$email_err = "This user has reached the limmit to what their plan can allow .";
}

SELECT count(*) with OR operator

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();

how to do change this query into a prepared statement?

Right, so i'm still getting my head around prepared statements and every time i think, yeah i've got it, a new query comes along and i'm thinking Hmmmm i would i set that up?
So here we go, i have a query that pulls records from a database based on a date and orders them by said date. The records it finds are based on a year and month value and the query looks like this:
$getresults = mysql_query(" SELECT * FROM `results` WHERE `date` LIKE '2012-$monthid%' ORDER BY date ");
I already have basic prepared statement for getting a users record from my database:
$query = "SELECT *
FROM results
WHERE date = ?
LIMIT 1";
if($stmt = $this->conn->prepare($query))
{
$stmt->bind_param('s', $date);
$stmt->execute();
if($stmt->fetch())
{
$stmt->close();
return true;
}
else
return false;
}
How would i change this to make it more like the first query?
Thanks for the help.
One idea would be to filter by year and month in separate parts of the WHERE clause:
$query = "SELECT * FROM results WHERE YEAR(date) = 2012 AND MONTH(date) = ? ORDER BY date";
if ($stmt = $this->conn->prepare($query)) {
$stmt->bind_param('i', $monthid);
...
}

Categories