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)
}
Related
This question already has an answer here:
Write a prepared statement with nullable values in conditions
(1 answer)
Closed 2 years ago.
I have a quite long mysql query, selecting data according to status field. I'm calling it for different statuses and it works well, but I have a scenario when I should get all records where status is null ONLY. Is there a way to do this without having to write 2 different sql queries?
Looks like I can't insert 'IS NULL' or '=' without it being rendered as a string.
I want to achieve this:
$sql = "SELECT name, surname FROM ...
...
WHERE status ?;";
$stmt = $pdo->prepare($sql);
$stmt->execute(array($status === 'undefined' ? 'IS NULL' : " = '$status'"));
After all, here's what I did:
$sql = "SELECT name, surname FROM ...
...
WHERE status <=> ?;";
$stmt = $pdo->prepare($sql);
$stmt->execute(array($status === 'unfinished' ? null : $status));
Using parameterised queries, as indicated, is a safer way of introducing user input into your SQL statements. However, it has the effect of treating all input as a parameter, and therefore will surroung any string literals with quotes - giving rise to the problem you have.
To deal with this issue, why not just modify the logic of the code:
$sql = "SELECT name, surname FROM ...
...
WHERE status";
if ($status === 'undefined') {
$sql .= " IS NULL";
$stmt = $pdo->prepare($sql);
$stmt->execute();
} else {
$sql .= " = ?"
$stmt = $pdo->prepare($sql);
$stmt->execute(array($status));
}
Edit
Updated to move the execution into the relevant part of the if statement becuase the parameters must not be specified if there is no placeholder in the SQL statement.
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.
I need to execute a sql query where I need to use a apostrophe because I search for an email address. When I now type the '' in the query they become a variable pointer but they should just tell mysql that this is a string.
How can I tell the program to use them in a different way? I already tried pdo->quote.
$statement = $pdo->prepare("SELECT user_id FROM tbl_server WHERE user_id = (SELECT user_id from tbl_user where Email = ':email') "); // here i need the normal apostrophe
$result = $statement->execute(array('email' => $email));
$user = $statement->fetch();
You must not quote :email in the query; the database driver will determine how to handle the parameter because it knows the passed type and the column's type in the database. But you need to prepend the : to email when executing the parameterized query.
$statement = $pdo->prepare("SELECT user_id FROM tbl_server WHERE user_id = (SELECT user_id from tbl_user where Email = :email) "); // here i need the normal apostrophe
$result = $statement->execute(array(':email' => $email));
$user = $statement->fetch();
By the way you should check whether $result is true or false, also your query might deliver an empty result set. So you should check whether $user === false (i.e. no user found) after fetching.
I'm having some trouble using a variable declared in PHP with an SQL query. I have used the resources at How to include a PHP variable inside a MySQL insert statement but have had no luck with them. I realize this is prone to SQL injection and if someone wants to show me how to protect against that, I will gladly implement that. (I think by using mysql_real_escape_string but that may be deprecated?)
<?php
$q = 'Hospital_Name';
$query = "SELECT * FROM database.table WHERE field_name = 'hospital_name' AND value = '$q'";
$query_result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($query_result)) {
echo $row['value'];
}
?>
I have tried switching '$q' with $q and that doesn't work. If I substitute the hospital name directly into the query, the SQL query and PHP output code works so I know that's not the problem unless for some reason it uses different logic with a variable when connecting to the database and executing the query.
Thank you in advance.
Edit: I'll go ahead and post more of my actual code instead of just the problem areas since unfortunately none of the answers provided have worked. I am trying to print out a "Case ID" that is the primary key tied to a patient. I am using a REDCap clinical database and their table structure is a little different than normal relational databases. My code is as follows:
<?php
$q = 'Hospital_Name';
$query = "SELECT * FROM database.table WHERE field_name = 'case_id' AND record in (SELECT distinct record FROM database.table WHERE field_name = 'hospital_name' AND value = '$q')";
$query_result = mysqli_query($conn, $query);
while ($row = mysqli_fetch_assoc($query_result)) {
echo $row['value'];
}
?>
I have tried substituting $q with '$q' and '".$q."' and none of those print out the case_id that I need. I also tried using the mysqli_stmt_* functions but they printed nothing but blank as well. Our server uses PHP version 5.3.3 if that is helpful.
Thanks again.
Do it like so
<?php
$q = 'mercy_west';
$query = "SELECT col1,col2,col3,col4 FROM database.table WHERE field_name = 'hospital_name' AND value = ?";
if($stmt = $db->query($query)){
$stmt->bind_param("s",$q); // s is for string, i for integer, number of these must match your ? marks in query. Then variable you're binding is the $q, Must match number of ? as well
$stmt->execute();
$stmt->bind_result($col1,$col2,$col3,$col4); // Can initialize these above with $col1 = "", but these bind what you're selecting. If you select 5 times, must have 5 variables, and they go in in order. select id,name, bind_result($id,name)
$stmt->store_result();
while($stmt->fetch()){ // fetch the results
echo $col1;
}
$stmt->close();
}
?>
Yes mysql_real_escape_string() is deprecated.
One solution, as hinted by answers like this one in that post you included a link to, is to use prepared statements. MySQLi and PDO both support binding parameters with prepared statements.
To continue using the mysqli_* functions, use:
mysqli_prepare() to get a prepared statement
mysqli_stmt_bind_param() to bind the parameter (e.g. for the WHERE condition value='$q')
mysqli_stmt_execute() to execute the statement
mysqli_stmt_bind_result() to send the output to a variable.
<?php
$q = 'Hospital_Name';
$query = "SELECT value FROM database.table WHERE field_name = 'hospital_name' AND value = ?";
$statement = mysqli_prepare($conn, $query);
//Bind parameter for $q; substituted for first ? in $query
//first parameter: 's' -> string
mysqli_stmt_bind_param($statement, 's', $q);
//execute the statement
mysqli_stmt_execute($statement);
//bind an output variable
mysqli_stmt_bind_result($stmt, $value);
while ( mysqli_stmt_fetch($stmt)) {
echo $value; //print the value from each returned row
}
If you consider using PDO, look at bindparam(). You will need to determine the parameters for the PDO constructor but then can use it to get prepared statements with the prepare() method.
I'm just migrating my code from mysql_query style commands to PDO style and I ran into a problem. THe old code looked like this :
$query_list_menu = "SELECT ".$_GET['section_name']." from myl_menu_hide_show WHERE id='".$_GET['id']."'";
And the updated code looks like below. Apparently it's not working. I store in $_GET['section_name'] a string that represents a field name from the database. But I think there is a problem when I pass it as a variable. Is the below code valid ? Thanks.
$query_list_menu = "SELECT :section_name from myl_menu_hide_show WHERE id=:id";
$result_list_menu = $db->prepare($query_list_menu);
$result_list_menu->bindValue(':section_name', $_GET['section_name'] , PDO::PARAM_STR);
$result_list_menu->bindValue(':id', $_GET['id'] , PDO::PARAM_INT);
$result_list_menu->execute();
If $_GET['section_name'] contains a column name, your query should be:
$query_list_menu = "SELECT " . $_GET['section_name'] . " from myl_menu_hide_show WHERE id=:id";
Giving:
$query_list_menu = "SELECT :section_name from myl_menu_hide_show WHERE id=:id";
$result_list_menu = $db->prepare($query_list_menu);
$result_list_menu->bindValue(':id', $_GET['id'] , PDO::PARAM_INT);
$result_list_menu->execute();
The reason is that you want the actual name of the column to be in the query - you'd changed it to be a parameter, which doesn't really make much sense.
I'll also add that using $_GET['section_name'] directly like this is a massive security risk as it allows for SQL injection. I suggest that you validate the value of $_GET['section_name'] by checking it against a list of columns before building and executing the query.
There is no good and safe way to select just one field from the record based on the user's choice. The most sensible solution would be to select the whole row and then return the only field requested
$sql = "SELECT * from myl_menu_hide_show WHERE id=?";
$stmt = $db->prepare($query_list_menu);
$stmt->execute([$_GET['id']]);
$row = $stmt->fetch();
return $row[$_GET['section_name']] ?? false;