Refactoring prepared statements queries code PHP MySAL - php

Using prepared statements to query the database in PHP is making me repeat a lot of code.
For instance, if I want to fetch a particular user from the database in my users.php file, I do it like this:
$sql = "SELECT * FROM users WHERE id=?";
// prepare sql statement
$stmt = $conn->prepare($sql);
// bind query parameters
$stmt->bind_param('i', $user_id);
// execute
$stmt->execute();
// get result object
$result = $stmt->get_result();
// fetch user from result as associative array
$user = $result->fetch_assoc();
To me, this is already a lot of code just to fetch a user. But no big deal, it is more secure so it is worth it.
The problem arises when I want to insert a post into the database in my post.php file. I do it like this:
$sql = "INSERT INTO posts SET user_id=?, title=?, body=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param('iss', $user_id, $title, $body);
$result = $stmt->execute();
$stmt->close();
I am already seeing code repetition here. It means in my application if I want to query the database 100 times, I will have to repeat some lines of code such as the prepare(), bind_param(), execute() steps especially since the bind_param() parameters are always changing.
I have thought of refactoring this in one or two functions say in my database.php file so that I can just call the function passing the query and the parameters and have that function do all the prepare() and bind_param() and execute() functions.
Is this possible? If so is it good practice?
Thanks for any suggestions.

Related

When is the right time to close a prepared statement?

Let's say I have prepared a mysqli statement by doing $stmt = $connection->prepare("SELECT name, id, password_hash FROM person WHERE id = ?") and that I use this statement in a function:
function get_person_info($person_id) {
global $stmt;
$stmt->bind_param("s", $person_id);
$stmt->execute();
return $stmt->get_result()->fetch_array();
}
If I don't know how many times this function will be called, when should I close $stmt if I want optimal performance?
More specifically: what exactly happens when closing $stmt? I have read the documentation at php.net, but it was unclear to me whether or not I am supposed to close a statement if I plan on reusing it.
Don't close it. It will be closed at the script's end.

How to bind parameters in mysql query?

I have search for bind parameters. But it just getting me confused. I'm really a beginner in php and mysql.
here is code:
$query ="UPDATE table_user_skills SET rating='" . $_POST["rating"] . "' where rating_id='".$_POST['id']."'";
$result = $conn->query($query);
I wonder if how can i apply the bind parameters method in this sample query. Thanks for you response.
Thanks for all the responses. My code works
update.php
$sql = "UPDATE table_user_skills SET rating=? WHERE rating_id=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param('sd', $myrate, $myrateid);
$stmt->execute();
if ($stmt->errno) {
echo "Error" . $stmt->error;
}
else print 'Your rate is accepted.';
$stmt->close();
When you write the query, leave the values (the $_POST variables) out of the SQL code and in their place use a placeholder. Depending on which interface you're using in PHP to talk to your MySQL database (there's MySQLi and PDO), you can use named or unnamed place holders in their stead.
Here's an example using PDO
$query = "UPDATE table_user_skills SET rating= :ratings where rating_id= :id";
$stmt = $conn->prepare($query);
$stmt->execute($_POST);
What we've done here is send the SQL code to MySQL (using the PDO::prepare method) to get back a PDOStatement object (denoted by $stmt in the above example). We can then send the data (your $_POST variables) to MySQL down a separate path using PDOStatement::execute. Notice how the placeholders in the SQL query are named as you expect your $_POST variables. So this way the SQL code can never be confused with data and there is no chance of SQL injection.
Please see the manuals for more detailed information on using prepared statements.

Understanding PDO Prepared Statements and Binding Parameters

From experience and also having been told constantly the benefits of using prepared statements and binding my parameters, I have constantly used those two techniques in my code, however I would like to understand exactly the purpose of each of those two techiques:
From my understanding of prepared statements:
$sql = "SELECT * FROM myTable WHERE id = ".$id;
$stmt = $conn->prepare($sql);
$stmt->execute();
The previous code should create a sort of a buffer in the database with the query I proposed. Now FROM MY UNDERSTANDING (and I could be very wrong), the previous code is insecure, because the string $sql could be anything depending on what $id actually is, and if $id = 1; DROP TABLE myTable;--, I would be inserting a malicious query even though I have a prepared statement.
FROM MY UNDERSTANDING this is where binding my parameters com in. If I do the following instead:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
The database should know exactly all the parts of the sql statement before hand:
SELECT these columns: * FROM myTable and WHERE id = "a variable that was input by the user", and if "a variable that was input by the user" != a variable, the query fails.
I have been told by some my understanding is correct, and by others that it is false, could someone please let me know if I am wrong, correct, or missing something? And elaborate as much as you want, all feedback is greatly appreciated!
You're correct that the first case is insecure. It's important to understand though, that preparing a statement only has value if you are using variable data, and/or executing the same query repeatedly. If you are executing plain statements with no variables, you could simply do this:
$sql = "SELECT * from myTable WHERE this_column IS NOT NULL";
$result = $conn->query($sql);
And end up with a PDOStatement object to work with, just like when you use PDO::exec().
For your second case, again, you're largely correct. What's happening is the variable passed to the database is escaped and quoted (unless you specify otherwise with the third argument to PDOStatement::bindParam(), it's sent as a string which is fine for most cases.) So, the query won't "fail" if bad data is sent. It behaves exactly as if you had passed a valid number that didn't exist as an ID in the database. There are, of course, some edge cases where you are still vulnerable even with a correctly prepared statement.
Also, to make life easier, you can use prepared statements like this, to do implicit binding:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->execute([":id"=>$id]);
Or even like this, with un-named parameters:
$sql = "SELECT * FROM myTable WHERE id = ?";
$stmt = $conn->prepare($sql);
$stmt->execute([$id]);
Naturally, most of this has been explained in the comments while I was typing up the answer!

MySQLi prepared statement fails where identical regular query succeeds

I have a conventional query that works just fine that looks like this:
$result = $mysqli->query("SELECT value FROM activities WHERE name = 'Drywall'");
This succeeds in returning a row. However, for the purposes of diagnosing the problem I'm having with a prepared statement, I tried an identical query as a prepared statement like so:
$stmt = $mysqli->prepare("SELECT value FROM activities WHERE name = 'Drywall'");
$stmt->execute();
Despite the fact these are identical query strings, $stmt->num_rows is always 0. Why would the conventional query work, but the prepared statement not when they are the same exact query? Also, I realize including 'Drywall' in the prepared query string runs counter to the purpose of prepared statements, but I was just trying to eliminate the possibility that bind_param() was the culprit. So I was using bind_param() to fill in placeholders and that wasn't working either, despite my double-checking at runtime that the variable I was binding contained the correct value.
I think you want to use
$stmt->store_result();
before the call
$stmt->num_rows();
see last line of the descripton in the manual for $stmt->num_rows() (http://www.php.net/manual/en/mysqli-stmt.num-rows.php).
Check for proper use of the mysqli->prepare. The function depends on a parameter to be passed. It is different from passing the values ​​directly in the query but can use with another way.
Verify the manual:
http://www.php.net/manual/pt_BR/mysqli.prepare.php
Did you try something like this:
$stmt = $mysqli->prepare("SELECT value FROM activities WHERE name = 'Drywall'");
$stmt->execute();
$res = $stmt->get_result();
$row = $res->fetch_assoc();
PS:
Prepared statements are Good. I would urge you to ALWAYS consider using them.
But in this case, a simple query would be much more efficient (would incur fewer round trips) than a prepared statement.

Cannot pass parameter by reference PHP prepared statement problem

I am trying to make a query with a prepared statement to retrieve some information and make another query with the information that was received from the first query, but i am receiving the error:
Cannot pass parameter by reference
is there a way around this?
this is my code:
$DBH = getDBH();
$stmt = $DBH->prepare("SELECT small FROM info WHERE user = ?");
$stmt->bind_param("s",$userid);
$stmt->execute();
$stmt->bind_result($small);
$stmt->fetch();
$stmt->close();
$stmt = $DBH->prepare("INSERT INTO method(small) VALUES(?)");
$stmt->bind_param("s",$small);
$stmt->execute();
$stmt->close();
I think i may have got it to work by adding
return $small;
after
$stmt->fetch();
although i have not had time to test it with any actual values, I am not recieving any errors, but i am unsure if the code stops at
return $small;
or if everything continues to execute, i may be able to just rewrite it into a function and return the value.
Edit: Never mind the below! I was thinking of PDO
Try changing the following lines:
$stmt = $DBH->prepare("SELECT small FROM info WHERE user = :s");
and
$stmt = $DBH->prepare("INSERT INTO method(small) VALUES(:s)");
It's because you're trying to bind a nonexistent parameter. If you want to use question marks, you have to find it like so If I remember correctly.
$stmt->bind_param(0,$userid);

Categories