mySQLi prepared statements and binding - php

When preparing a mySQLi statement do I need to bind parameters?
public function query($sql){
$sql = mysqli_prepare($this-> connect, $sql);
$array= array();
$query=mysqli_query($this->connect, $sql);
if ($query > 0){
$c=mysqli_num_rows ($query);
if($c > 1){
while($fetch = mysqli_fetch_row($query)){
array_push($array, $fetch);
}
}
return $array;
}else ...
exit();
}
the ... is just some more conditions to follow through. It just shows what my question is. The way I prepare is that ok or should I do this better. I've never used the prepare method and i am new to this. Also is it best practice to exit the query at the end?

Related

Converting a normal OOP SQLi statement to a Prepared Statement

I have the following normal User class statement that I'm trying to convert to a prepared statement.
public function didReceiveRequest($user_from) {
$user_to = $this->user['username'];
$check_request_query = mysqli_query($this->con, "SELECT * FROM friend_requests WHERE user_to='$user_to' AND user_from='$user_from'");
if (mysqli_num_rows($check_request_query) > 0) {
return true;
}
else {
return false;
}
}
I'm new to prepared statements and have been doing pretty well throughout the User class, but am still having trouble with a few. Being new, I don't follow the logic as well, so please go easy. Here is what I have so far:
public function didReceiveRequest($user_from){
$user_to = $this->user['username'];
$check_request = $this->con->stmt_init();
$check_request->prepare('SELECT * FROM friend_requests WHERE user_to=? AND user_from=?');
$check_request->bind_param('ss', $user_to, $user_from);
$check_request->execute();
$result = check_request->get_result();
$data = $result->fetch_assoc();
$check_request->free_result();
$check_request->close();
if($data > 0){
return true;
}else{
return false;}
}
So a couple things abt this: 1)I know there is probably a better and more efficient way to do this. And 2) Will what I have return the same result as what was there (with normal statement) previously. I don't want to mix up calls from my dependent pages.
Here is how you would use MySqli prepared statements for your case example.
public function didReceiveRequest($user_from) {
$query = "SELECT *
FROM friend_requests
WHERE user_to = ? AND user_from = ?";
$user_to = $this->user['username'];
$stmt = $this->con->prepare($query);
$stmt->bind_param(ss, $user_to, $user_from); //set your bindings
$stmt->execute();
//$results = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);//Use if you want to see results.
if($stmt->num_rows !== 0) {
return TRUE;
}else{
return FALSE;
}
}
Here is a really good link to read that teaches you how to use prepared statements with MySqli. I would bookmark this link and refer to it often. MySqli prepared statements
Using prepared statements does not affect how you receive the results from you query.
The difference I see in what was provided as an answer is $result = $stmt->get_result(); Again being new to prepared statements I'm not 100% that this is the reason above code throws an error, but this code works.
public function didReceiveRequest($user_from) {
$user_to = $this->user['username'];
$stmt = $this->con->stmt_init();
$stmt->prepare('SELECT * FROM friend_requests WHERE user_to=? AND user_from=?');
$stmt->bind_param('ss', $user_to, $user_from);
$stmt->execute();
$result = $stmt->get_result();
$qty = $result->num_rows;
$stmt->free_result();
$stmt->close();
if($qty > 0) {
return true;
}else{
return false;
}
}

Sometimes no query result with mysqli

I've wrote this function to read from my MySQL database. This works in most cases, but if it comes to a row with umlauts or characters like "" it returns "0 results".
function leseAntwort($FrageID, $AntwortID){
$sql = "SELECT antwort_text FROM antwort WHERE frage_id=$FrageID AND id=$AntwortID";
$result = connect()->query($sql);
if ($result->num_rows > 0) {
$antwort = $result->fetch_row();
connect()->close();
return $antwort[0];
} else {
connect()->close();
return "0 results";
}
}
[UPDATE]
I tried this, but there is no difference between the results.
function leseAntwort($FrageID, $AntwortID){
$frage=$FrageID;
$antwort=$AntwortID;
global $mysqli;
if ($stmt = $mysqli->prepare("SELECT antwort_text FROM antwort WHERE frage_id=? AND id=?")){
$stmt->bind_param("ii", $frage, $antwort);
$stmt->execute();
$stmt->bind_result($d);
$stmt->fetch();
return $d;
$stmt->close();
$mysqli->close();
} else {
echo "Error";
}
}
you have an SQL Injection in your SQL Statement.
PLZ use Prepared Statements http://php.net/manual/de/mysqli.quickstart.prepared-statements.php
After that you can use utf-8 Chars in your Params.

MySQL Prepared statement confusion

Ok, so I am having a lot of trouble with Prepared statements. I've done hours of research and still can't seem to fully understand everything...
I really feel like I need to understand Prepared statements because I was just about to release a few new free APIs on my website (which require API Key to execute API) but I recently realized how insecure everything is.... I can simply use SQL injection to bypass API Key check, e.g. 'OR'1'='1
Here is how I validate API Key:
$apikey = $_GET['key'];
$sql = "SELECT * FROM `table` WHERE `key` = '$apikey'";
$query = mysqli_query($con, $sql);
if($query)
{
$fetchrow = mysqli_fetch_row($query);
if(isset($fetchrow[0]))
{
echo "API Key is valid!";
}
else
{
echo "API KEY is invalid";
}
}
And like mentioned above this can easily be bypassed by executing my API like this
http://website.com/api.php?key='OR'1'='1
This really scared me at first, but then I did some research and learned a good way to prevent any form of SQL injection is to use prepared statement, so I did a lot of research and it just seems quite complicated to me :/
So I guess my question is, how can I take my above code, and make it function the same way using prepared statements?
Probably everything you need:
class Database {
private static $mysqli;
Connect to the DB:
public static function connect(){
if (isset(self::$mysqli)){
return self::$mysqli;
}
self::$mysqli = new mysqli("DB_HOST", "DB_USER", "DB_PASS", "DB_NAME");
if (mysqli_connect_errno()) {
/*Log error here, return 500 code (db connection error) or something... Details in $mysqli->error*/
}
self::$mysqli->query("SET NAMES utf8");
return self::$mysqli;
}
Execute statement and get results:
public static function execute($stmt){
$stmt->execute();
if ($mysqli->error) {
/*Log it or throw 500 code (sql error)*/
}
return self::getResults($stmt);
}
Bind results to the pure array:
private static function getResults($stmt){
$stmt->store_result();
$meta = $stmt->result_metadata();
if (is_object($meta)){
$variables = array();
$data = array();
while($field = $meta->fetch_field()) {
$variables[] = &$data[$field->name];
}
call_user_func_array(array($stmt, "bind_result"), $variables);
$i = 0;
while($stmt->fetch()) {
$array[$i] = array();
foreach($data as $k=>$v)
$array[$i][$k] = $v;
$i++;
}
$stmt->close();
return $array;
} else {
return $meta;
}
}
Class end :)
}
Example of usage:
public function getSomething($something, $somethingOther){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE something = ? AND somethingOther = ?");
$stmt->bind_param("si", $something, $somethingOther); // s means string, i means number
$resultsArray = Database::execute($stmt);
$someData = $resultsArray[0]["someColumn"];
}
Resolving your problem:
public function isKeyValid($key){
$mysqli = Database::connect();
$stmt = $mysqli->prepare("SELECT * FROM table WHERE key = ? LIMIT 1");
$stmt->bind_param("s", $key);
$results = Database::execute($stmt);
return count($results > 0);
}
PHP automatically closes DB connection so no worries about it.
$sql = "SELECT * FROM `table` WHERE `key` = ?";
if(stmt = $mysqli->prepare($sql)) {
$stmt->bind_param("i", $apikey);
$stmt->execute();
$stmt->bind_result($res);
$stmt->fetch();
$stmt->close();
}
See more - http://php.net/manual/en/mysqli.prepare.php

Returning array and num rows mysqli prepared

I'm a bit new to the mysqli prepared statement and I would like to use fetch_array to return the results AND also return num_rows as an array value.
I have something like this
function getCategories($dbh, $catId)
{
$data = array();
$s = "SELECT id, title FROM categories WHERE parent_id = ?";
if ($stmt = mysqli_prepare($dbh, $s)) {
mysqli_stmt_bind_param($stmt, "i", $catId);
mysqli_stmt_execute($stmt);
if (mysqli_stmt_errno($stmt)) {
exit(mysqli_stmt_error($stmt));
}
mysqli_stmt_store_result($stmt);
$count = mysqli_stmt_num_rows($stmt)) {
if ($count) {
$data['count'] = $count;
$result = mysqli_stmt_get_result($stmt);
while ($r = mysqli_fetch_assoc($result)) {
$data[] = $r;
}
}
return $data;
} else {
exit(mysqli_error($dbh));
}
}
It seems that I cannot use mysqli_stmt_store_result and mysqli_stmt_get_result().
The store_result function seems to give a boolean and then I get this error: "mysqli_fetch_assoc() expects parameter 1 to be mysqli_result, boolean given"
Hope this makes sense. Any help would be really appreciated.
Updated:
function getCategories($dbh, $catId)
{
$data = array();
$s = "SELECT id, title FROM categories WHERE parent_id = ?";
if ($stmt = mysqli_prepare($dbh, $s)) {
mysqli_stmt_bind_param($stmt, "i", $catId);
mysqli_stmt_execute($stmt);
if (mysqli_stmt_errno($stmt)) {
exit(mysqli_stmt_error($stmt));
}
$result = mysqli_stmt_get_result($stmt);
$data['count'] = $result->num_rows;
while ($r = mysqli_fetch_assoc($result)) {
$data[] = $r;
}
return $data;
} else {
exit(mysqli_error($dbh));
}
}
According to the PHP docs, mysqli_stmt_get_result returns FALSE on error:
Returns a resultset for successful SELECT queries, or FALSE for other DML queries or on failure. The mysqli_errno() function can be used to distinguish between the two types of failure.
You're then passing that into mysqli_fetch_assoc which complains because you're giving it a bool instead of the resultset it expects.
Do a little more erroring checking at that point and you'll be fine. There's probably something wrong with your SQL query. Call mysqli_errorno to determine if there's an error, as the docs state above.
EDIT:
Use the mysqli_error function to get a description of the mysql error. It would be best to use this everywhere you're checking for failure as having an error message will make debugging much easier than simply failing silently.

Printing an entire SQL result from a prepared statement in PHP

I'm trying to build a function that allows me to abstract the use of the mysqli functions in my code. Currently I have a working version for using standard SQL (no prepared statements).
function mySqlQueryToArray($con, $sql){
// Process SQL query
$result = mysqli_query($con, $sql);
if(mysqli_error($con)){
out($sql . "\n");
exit("ERROR: MySQL Error: " . mysqli_error($con));
}
// Put result into 2D array
$output = array();
while($row = mysqli_fetch_assoc($result)) {
array_push($output, $row);
}
return $output;
}
Now I'm trying to translate that code into something that can use prepared statements but my research has revealed that when using prepared statements you need to bind each column to a different variable using mysqli_stmt::bind_result which causes a problem because the point of the function is to work for an arbitrary SQL Query.
My main question is this: Is there a way to print out the entire output from a SQL query in a 2D array same as the function above does using prepared statements?
Here's my current code for using prepared statements, it has everything but the bind_result in there.
//Creates a MySQL Query, gets the result and then returns a 2D array with the results of the query
function mySqlQueryToArray($con, $sql, $params){
// Prepare SQL query
$stmt = $con->prepare($sql);
if(mysqli_error($con)){
echo "$sql=>\n" . print_r($params, true) . "\n";
exit("$sql=>\n" . print_r($params, true) . "\nERROR: MySQL Error: " . mysqli_error($con));
}
// Bind parameters
foreach($params as $param){
$type = "s";
if(gettype($param) == 'integer'){
$type = "i";
}
$stmt->bind_param($type, $param);
}
//execute query
$stmt->execute();
// Put result into 2D array
$output = array();
// ??? need to bind results and get them into an array somehow
//close prepared statement
$stmt->close();
return $output;
}
PDO turns out to be the answer. I feel like I should post the code I created to solve my problem. Thanks to #Don'tPanic for the tip.
function pdoQuery($sql, $params){
$db = new PDO('mysql:host=localhost;dbname=My_Database;charset=utf8', 'username', 'password', array(PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
try {
$stmt = $db->prepare($sql);
$stmt->execute($params);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
}catch(PDOException $ex){
echo "ERROR: SQL Error: $sql";
// logError("ERROR: SQL Error: $sql");
}
}

Categories