I have a script that sends more than 10 queries of all CRUD types on 5 tables (some of the SELECTs with JOINs). Originally, I used mysqli_* functions for all those. Now, to improve security, I am porting the code to use prepared statements.
I have no previous experience using prepared statements and I have some doubts about what can and cannot be done. For instance lets say I start by a SELECT query, then have an UPDATE and finally an INSERT.
My question is this:
Should I repeat the mysqli_stmt_init and mysqli_stmt_close for each query, or could I initiate once before the first query, prepare a statement for each query and use it and finally close it after all queries are done with? In other words, is method 2 OK, or should I stick to method 1?
Method 1 - No reuse
// SELECT
$stmt = mysqli_stmt_init($link);
mysqli_stmt_prepare($stmt, "SELECT on table 1 and table 2");
...
mysqli_stmt_close($stmt);
// UPDATE
$stmt = mysqli_stmt_init($link);
mysqli_stmt_prepare($stmt, "UPDATE on table 3");
...
mysqli_stmt_close($stmt);
// INSERT
$stmt = mysqli_stmt_init($link);
mysqli_stmt_prepare($stmt, "INSERT INTO table 4");
...
mysqli_stmt_close($stmt);
Method 2 - Reusing statement
// INIT
$stmt = mysqli_stmt_init($link);
// SELECT
mysqli_stmt_prepare($stmt, "SELECT on table 1 and table 2");
...
// UPDATE
mysqli_stmt_prepare($stmt, "UPDATE on table 3");
...
// INSERT
mysqli_stmt_prepare($stmt, "INSERT INTO table 4");
...
// CLOSE
mysqli_stmt_close($stmt);
There is no need for stmt_init() or stmt_close(), mysqli_prepare() returns a stmt object and PHP handles the cleaning, the correct order is mysqli_stmt::prepare => mysqli_stmt::bind_param => mysqli_stmt::execute.
$mysqli = new mysqli( "host", "user", "pass", "db" );
$stmt = $mysqli->prepare( "SELECT * FROM table WHERE id = ?" );
$stmt->bind_param( "i", $id );
$id = 5;
$stmt->execute();
You can execute the same statement multiple times with different params, for example:
$stmt = $mysqli->prepare( "SELECT * FROM table WHERE id = ?" );
$stmt->bind_param( "i", $id );
$id = 5;
$stmt->execute(); // executed: SELECT * FROM table WHERE id = 5
$id = 3;
$stmt->execute(); // executed: SELECT * FROM table WHERE id = 3
But in case of different statements you must prepare each statement separately.
Yes, method 2 is ok.
There is no need to finally close it either, as it will be closed when your script ends.
Related
I have a site in which I need to rewrite all the SQL to be prepared statements in the MySQLi Prepared format.
Similar to the below
$sql = "SELECT * FROM jobs WHERE Job_Id = ?";
// Prepare statement
$stmt = $dbcon->prepare($sql);
// Bind parameters
$stmt->bind_param('i', $Job_Id);
// Execute statement
$stmt->execute();
// Bind result
$result = $stmt->get_result();
if($result->num_rows >= 1){
while($row = $result->fetch_assoc()){
}
}
I came across this line:
UPDATE jobs SET jobTitle = IF('$jobTitle' = '', jobTitle, '$jobTitle'),
How would this query line be represented in a prepared statement? Surely all the variables would be replaced with ?, but then do I have to re-use the same variable and have more placeholders?
i have to get data from one table from my database, BUT after getting the data, i have to access another table to get more data using the id found in the first query.
Here is my code:
$query = "SELECT id,name,datetime FROM table1 WHERE id=?";
if($stmt=mysqli_prepare($mysqli,$query)){
mysqli_stmt_bind_param($stmt,"i",$_SESSION['id']);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_result($stmt,$id,$name,$datetime);
while(mysqli_stmt_fetch($stmt)){
$query2 = "SELECT id FROM table2 WHERE id=?";
if($stmt2=mysqli_prepare($mysqli,$query2)){
mysqli_stmt_bind_param($stmt2,"s",$id2);
mysqli_stmt_execute($stmt2);
mysqli_stmt_store_result($stmt2);
$num = mysqli_stmt_num_rows($stmt2);
}
The code does not work, i know i can't do that. I'm new with mysqli, in MySQL it works, but in MySQLi don't.
Is this query right? Can i pass array key like this?
mysqli_query("UPDATE subjects SET has_notes = 1 WHERE sub_id = '$notes_data['sub']'");
If not please help me with a solution.
Thanks in advance
Im surprised no one told you to prepare that query:
/* create a prepared statement */
if ($stmt = mysqli_prepare($link, "UPDATE subjects SET has_notes = 1 WHERE sub_id = ?")) {
/* bind parameters for markers */
mysqli_stmt_bind_param($stmt, "i", $notes_data['sub']);
/* execute query */
mysqli_stmt_execute($stmt);
}
or the object oriented way:
/* create a prepared statement */
if ($stmt = $mysqli->prepare("UPDATE subjects SET has_notes = 1 WHERE sub_id = ?")) {
/* bind parameters for markers */
$stmt->bind_param("i", $notes_data['sub']);
/* execute query */
$stmt->execute();
}
Please read up on mysqli::prepare/mysqli_prepare
mysqli_query("UPDATE subjects SET has_notes = 1 WHERE sub_id = '".$notes_data['sub']."'");
A simply solution can be this:
mysqli_query('UPDATE subjects SET has_notes = 1 WHERE sub_id = ' . $notes_data['sub']);
Note: this solution is correct if sub_id is an integer field and $notes_data['sub'] is an integer too
otherwise:
mysqli_query("UPDATE subjects SET has_notes = 1 WHERE sub_id = '" . $notes_data['sub'] ."'");
You need to use { and } to enclose the value you're including:
mysqli_query("UPDATE subjects SET has_notes = 1 WHERE sub_id = '{$notes_data['sub']}'");
I'd encourage you, however, to seriously consider writing using MySQLi's prepared statements rather than building up the query like this - if the $notes_data['sub'] variable has come from the web then you'll be at serious risk of an SQL injection vulnerability.
Why not do something like this:
$stmt = mysqli_prepare($link, 'UPDATE subjects SET has_notes = 1 WHERE sub_id = ?');
mysqli_stmt_bind_param($stmt, 's', $notes_data['sub']);
mysqli_stmt_execute($stmt);
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.
I'm just trying to figure out how to determine the number of rows and then make that number display in the HTML.
My prepared statement looks like this:
if($stmt = $mysqli -> prepare("SELECT field1, field2, field3 FROM table WHERE id= ?ORDER BY id ASC"))
{
/* Bind parameters, s - string, b - blob, i - int, etc */
$stmt -> bind_param("i", $id);
$stmt -> execute();
/* Bind results */
$stmt -> bind_result($testfield1, $testfield2, $testfield3);
/* Fetch the value */
$stmt -> fetch();
/* Close statement */
$stmt -> close();
}
I understand that I'm supposed to first save the results, then use num_rows, like this:
$stmt->store_result();
$stmt->num_rows;
However, I'm running, and issue with the page bugging out when I put that code in there. I haven't even been able to get to the next step of how to display the number of rows
What am I missing in terms of calculating the number of rows inside the prepared statement, then how would I display it with a <?php echo '# rows: '.$WHATGOESHERE;?>
num_rows returns the number, you have to store it in a variable.
/*.....other code...*/
$numberofrows = $stmt->num_rows;
/*.....other code...*/
echo '# rows: '.$numberofrows;
So full code should be something like this:
$stmt = $mysqli -> prepare("SELECT field1, field2, field3 FROM table WHERE id= ? ORDER BY id ASC");
/* Bind parameters, s - string, b - blob, i - int, etc */
$stmt -> bind_param("i", $id);
$stmt -> execute();
$stmt -> store_result();
/* Bind results */
$stmt -> bind_result($testfield1, $testfield2, $testfield3);
/* Fetch the value */
$stmt -> fetch();
$numberofrows = $stmt->num_rows;
/* Close statement */
$stmt -> close();
echo '# rows: '.$numberofrows;
If you are only interested in the row count instead of the actual rows of data, here is a complete query block with a COUNT(*) call in the SELECT clause.
$conn = new mysqli("host", "user", "pass", "db");
$stmt = $conn->prepare("SELECT COUNT(*) FROM `table` WHERE id= ?");
$stmt->bind_param("s", $id);
$stmt->execute();
$stmt->bind_result($num_rows);
$stmt->fetch();
echo $num_rows;
Or if you want to know the row count before iterating/processing the rows, one way is to lump the entire resultset (multi-dimensional array) into a variable and call count() before iterating.
$conn = new mysqli("host", "user", "pass", "db");
$sql = "SELECT field1, field2, field3
FROM table
WHERE id= ?
ORDER BY id";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result();
$resultset = $result->fetch_all(MYSQLI_ASSOC);
echo "<div>Num: " , count($resultset) , "</div>";
foreach ($resultset as $row) {
echo "<div>Row: {$row['field1']} & {$row['field2']} & {$row['field3']}</div>";
}
*I have tested both of the above snippets to be successful on my localhost.
This works as of Feb 2020:
$number_of_records = $stmt->rowCount();
echo $number_of_records;
From php.net manual:
PDOStatement::rowCount() returns the number of rows affected by a
DELETE, INSERT, or UPDATE statement.
Here is an example from their website:
<?php
/* Delete all rows from the FRUIT table */
$del = $dbh->prepare('DELETE FROM fruit');
$del->execute();
/* Return number of rows that were deleted */
print("Return number of rows that were deleted:\n");
$count = $del->rowCount();
print("Deleted $count rows.\n");
?>
The above example will output:
Return number of rows that were deleted:
Deleted 9 rows.
Check out the example #2 here:
PHP.net
Use PDO::query() to issue a SELECT COUNT(*) statement with the same predicates as your intended SELECT statement, then use PDOStatement::fetchColumn() to retrieve the number of rows that will be returned. Your application can then perform the correct action.