mysqli prepared statement without bind_param - php

I have this code for selecting fname from the latest record on the user table.
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
$sdt=$mysqli->('SELECT fname FROM user ORDER BY id DESC LIMIT 1');
$sdt->bind_result($code);
$sdt->fetch();
echo $code ;
I used prepared statement with bind_param earlier, but for now in the above code for first time I want to use prepared statement without binding parameters and I do not know how to select from table without using bind_param(). How to do that?

If, like in your case, there is nothing to bind, then just use query()
$res = $mysqli->query('SELECT fname FROM user ORDER BY id DESC LIMIT 1');
$fname = $res->fetch_row()[0] ?? false;
But if even a single variable is going to be used in the query, then you must substitute it with a placeholder and therefore prepare your query.
However, in 2022 and beyond, (starting PHP 8.1) you can indeed skip bind_param even for a prepared query, sending variables directly to execute(), in the form of array:
$query = "SELECT * FROM `customers` WHERE `Customer_ID`=?";
$stmt = $db->prepare($query);
$stmt->execute([$_POST['ID']]);
$result = $stmt->get_result();
$row = $result->fetch_assoc();

The answer ticked is open to SQL injection. What is the point of using a prepared statement and not correctly preparing the data. You should never just put a string in the query line. The point of a prepared statement is that it is prepared. Here is one example
$query = "SELECT `Customer_ID`,`CompanyName` FROM `customers` WHERE `Customer_ID`=?";
$stmt = $db->prepare($query);
$stmt->bind_param('i',$_POST['ID']);
$stmt->execute();
$stmt->bind_result($id,$CompanyName);
In Raffi's code you should do this
$bla = $_POST['something'];
$mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
$stmt = $mysqli->prepare("SELECT `fname` FROM `user` WHERE `bla` = ? ORDER BY `id` DESC LIMIT 1");
$stmt->bind_param('s',$_POST['something']);
$stmt->execute();
$stmt->bind_result($code);
$stmt->fetch();
echo $code;
Please be aware I don't know if your post data is a string or an integer. If it was an integer you would put
$stmt->bind_param('i',$_POST['something']);
instead. I know you were saying without bind param, but trust me that is really really bad if you are taking in input from a page, and not preparing it correctly first.

Related

Using PHP variable in SQL query

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.

PDO DELETE also creates a new INSERT

I have a strange problem where every time I do a simple DELETE query to delete WHERE email =. For some reason after deletion it also does a new INSERT with the same email? There is no INSERT anywhere and there are no triggers... Does anybody know why this happens? The table has a email and a nr with auto_increment.
$check_email = $_POST['email'];
$query = "SELECT `email` FROM `newsletter` WHERE email = '$check_email';";
$sth = $dbh->prepare($query);
$sth->execute();
$row = $sth->fetch(PDO::FETCH_ASSOC);
$check_users_email = $row['email'];
if($check_users_email != ''){
$query_update = "DELETE FROM `newsletter` WHERE email = '$check_users_email';";
}
$sth = $dbh->prepare($query_update);
$sth->execute();
Before deletion: email=test#email.com | nr=1
After deletion: email=test#email.com | nr=2
it might be in your sql string, since you're using prepared statements.
in PDO you should use named or unnamed placeholders. then after preparing the query, you pass the prams as an array when you execute the statement.
If you're using PDO, no need to use single quotes. just the column name and for the search value just use placeholders and then pass on the values on execution as an array.
NOTE: i renamed the PDO object $sth inside the 'if' statement, just to avoid name clash. also i moved the last 2 lines inside the 'if' statement, because you need the value of the sql string '$query_update' which will not be available if that statement returned false.
also to check if the variable $check_users_email is empty, you can use empty() or strlen().
try this:
$check_email = $_POST['email'];
$query = "SELECT email FROM newsletter WHERE email = :check_email";
$sth = $dbh->prepare($query);
$sth->execute(array(':check_email' => $check_email));
$row = $sth->fetch(PDO::FETCH_ASSOC);
$check_users_email = $row['email'];
if($check_users_email != ''){
$query_update = "DELETE FROM newsletter WHERE email = :check_users_email";
$sth2 = $dbh->prepare($query_update);
$sth2->execute(array(':check_users_email' => $check_users_email));
}

Best way to avoid preparing the same PDO statement more than once?

Currently I save prepared statements into a private variable, because I ignore how they really work in the deepness, and do it just in case.
So the question is really simple, if I iterate over the same $PDO->prepare(), will it prepare again the same query?
foreach( $arr as $docid ) {
if( $this->dbLink === null ) { // PDO resource, saved in the object.
throw new Exception( 'Must first connect to DB' );
}
if( $this->queryCheckAccess === null ) {
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
}
else {
$result = $this->queryCheckAccess->execute(array(':id'=>$docid));
}
}
Will it matter ? Or the DB Engine / PHP is smart enough to know that it is the same prepared statement?
Thanks a lot.
----------------- EDIT --------------
I think I was misunderstood.
What I ask is what happens if I do:
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
And what happens if I do:
if( $this->queryCheckAccess === null ) {
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
}
Will the engine prepare the query 4 times in the first example? Or will notice it is the same query and just "jump" that?
Your code only prepares the query once, because after the first loop iteration, it's not NULL so it the conditional block won't run. But it's a waste of time to check the condition every time through the loop.
But to answer your question, if you prepare() the same query, it does do redundant work, even if the query is identical to the one you prepared before. So you should avoid that.
But you don't need to prepare inside the loop at all. Prepare once before you start the loop, and bind a variable to the parameter. You don't need to bind every time in the loop, just change the value of that variable.
if( $this->dbLink === null ) { // PDO resource, saved in the object.
throw new Exception( 'Must first connect to DB' );
}
$query = 'SELECT * from something where id = :id';
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->bindParam(':id' => $docidparam);
foreach( $arr as $docid ) {
$docidparam = $docid;
$result = $this->queryCheckAccess->execute();
}
I'm not sure if you can bind the variable and also use it as the loop variable, there might be a scope conflict.
Another suggestion for this query: why not just run one query to search for a list of values?
$list = implode(",", array_fill(1, count($arr), "?"));
$query = "SELECT * FROM something WHERE id IN ( $list )";
$this->queryCheckAccess = $this->dbLink->prepare($query);
$this->queryCheckAccess->execute($arr);
PS: Also you should check for errors. If you enable PDO error mode EXCEPTION, then errors will automatically throw exceptions. If you don't enable that mode, you need to check the return value of prepare() and execute(), which return false if there's an error.
I just RUN a code similar to your example, and enabled MySQL Query LOG I found that all prepare requests are sent to MySQL Server
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Prepare SELECT * FROM test_table WHERE username = ?
Close stmt
Test code:
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth = $dbh->prepare($sql);
$sth->bindParam(1, $user);
$sth->execute();
Then, the best way is to prepare once, and Bind different values and then execute.
$sth = $dbh->prepare($sql);
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();
$user = "test2";
$sth->bindParam(1, $user);
$sth->execute();
$user = "test";
$sth->bindParam(1, $user);
$sth->execute();
No, that's one of the main features of prepared statements. If you're going to run the same query multiple times but with different variables then preparing the query will give you a speed increase. Especially if you make use of transactions (requires InnoDB storage engine).
To answer the question from the title (which is quite different from questions in the body), the best way to avoid preparing the same statement more than once, apparently would be to avoid running multiple similar queries at all.
To answer the question from the question body - no, DB Engine / PHP is not "smart" enough to know that it is the same query were prepared again. With every new prepare() call another statement is created. And I would be first to hate such a "smart" behavior. The "smarter" your tool, the more unpredictable results you get.
To answer the real problem in the code, a smart developer would use a right tool to save himself a trouble.
With safeMysql whole mess will be reduced to one query and one line of code
$data = $this->dbLink->getAll('SELECT * from somth where id IN (?a)', $arr);
S0 - no multiple queries, no multiple preparations, no multiple questions.
By the way, you are losing first id with your code.
Yet you're losing all of them but last one if you don't use the result in place.

Multiple prepared statements (SELECT)

I'm trying to change my SQL queries with prepared statements.
The idea: I'm getting multiple records out of the database with a while loop and then some additional data from the database in the loop.
This is my old SQL code (simplified):
$qry = "SELECT postId,title,userid from post WHERE id='$id'";
$rst01 = mysqli_query($mysqli, $qry01);
// loop trough mutiple results/records
while (list($postId,$title,$userid) = mysqli_fetch_array($rst01)) {
// second query to select additional data
$query05 = "SELECT name FROM users WHERE id='$userid";
$result05 = mysqli_query($mysqli, $query05);
$row05 = mysqli_fetch_array($result05);
$name = $row05[name ];
echo "Name: ".$name;
// do more stuff
// end of while loop
}
Now I want to rewrite this with prepared statements.
My question: is it possible to run a prepared statement in the fetch of another prepared statement ? I still need the name like in the old SQL code I do for the $name.
This is what've written so far.
$stmt0 = $mysqli->stmt_init();
$stmt0->prepare("SELECT postId,title,userid from post WHERE id=?");
$stmt0->bind_param('i', $id);
$stmt0->execute();
$stmt0->bind_result($postId,$title,$userid);
// prepare second statement
$stmt1 = $mysqli->stmt_init();
$stmt1->prepare("SELECT name FROM users WHERE id= ?");
while($stmt0->fetch()) {
$stmt1->bind_param('i', $userid);
$stmt1->execute();
$res1 = $stmt1->get_result();
$row1 = $res1->fetch_assoc();
echo "Name: ".$row1['name'] ;
}
It returns an error for the second statement in the loop:
Warning: mysqli_stmt::bind_param(): invalid object or resource mysqli_stmt in ...
If I use the old method for the loop and just the prepared statement to fetch the $name it works.
You can actually do this with a single JOINed query:
SELECT p.postId, p.title, p.userid, u.name AS username
FROM post p
JOIN users u ON u.id = p.userid
WHERE p.id = ?
In general, if you are running a query in a loop, there is probably a better way of doing it.

mySQLi Prepared Statement Select with Escape Characters

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

Categories