Nice way to pass parameters to PDO - php

Positional parameters become a nightmare when dealing with more than 3 or 4 parameters. Named parameters are verbose. I'm thinking of doing this:
query("SELECT * FROM users WHERE username = ", $username, " AND password = ", $password)
With dynamic parameters (using func_get_args()), every second one being transformed into a positional parameter.
I've never seen this before and wanted to know if anyone has done this before and why/why not?

Named parameters don't have to be verbose, at least not compared to positional parameters. You could use shortened versions that are still obvious:
$st = $dbh->prepare('SELECT * FROM users WHERE username = :u AND password = :p');
$st->bindValue(':u', $username);
$st->bindValue(':p', $password);
$st->execute();

It's a clever idea. The only problem I see is how to distinguish between SQL and passed-in variables. Unless you make an assumption that every second arg is a variable. I just think that assumption is fragile, and obfuscates things more than makes them clear.
Better way would probably be to use interpolation:
query("SELECT foo FROM bar WHERE id = #{id}", array("id" => "23"));
Then write logic to interpolate these.

I don't think positional parameters are so bad... this is my favorite method:
function mysql_safe_string($value) {
if(is_numeric($value)) return $value;
elseif(empty($value)) return 'NULL';
elseif(is_string($value)) return "'".mysql_real_escape_string($value)."'";
elseif(is_array($value)) return implode(',',array_map('mysql_safe_string',$value));
}
function mysql_safe_query($format) {
$args = array_slice(func_get_args(),1);
$args = array_map('mysql_safe_string',$args);
$query = vsprintf($format,$args);
$result = mysql_query($query);
if($result === false) echo '<div class="mysql-error"><strong>Error: </strong>',mysql_error(),'<br/><strong>Query: </strong>',$query,'</div>';
return $result;
}
// example
$result = mysql_safe_query('SELECT * FROM users WHERE username=%s', $username);

Related

How to check if column equals a value and do somthing if true? [duplicate]

This question already has answers here:
Single result from database using mysqli
(6 answers)
Closed 2 years ago.
I am trying to write a function that will check for a single value in the db using mysqli without having to place it in an array. What else can I do besides what I am already doing here?
function getval($query){
$mysqli = new mysqli();
$mysqli->connect(HOST, USER, PASS, DB);
$result = $mysqli->query($query);
$value = $mysqli->fetch_array;
$mysqli->close();
return $value;
}
How about
$name = $mysqli->query("SELECT name FROM contacts WHERE id = 5")->fetch_object()->name;
The mysql extension could do this using mysql_result, but mysqli has no equivalent function as of today, afaik. It always returns an array.
If I didn't just create the record, I do it this way:
$getID = mysqli_fetch_assoc(mysqli_query($link, "SELECT userID FROM users WHERE something = 'unique'"));
$userID = $getID['userID'];
Or if I did just create the record and the userID column is AI, I do:
$userID = mysqli_insert_id($link);
Always best to create the connection once at the beginning and close at the end. Here's how I would implement your function.
$mysqli = new mysqli();
$mysqli->connect(HOSTNAME, USERNAME, PASSWORD, DATABASE);
$value_1 = get_value($mysqli,"SELECT ID FROM Table1 LIMIT 1");
$value_2 = get_value($mysqli,"SELECT ID FROM Table2 LIMIT 1");
$mysqli->close();
function get_value($mysqli, $sql) {
$result = $mysqli->query($sql);
$value = $result->fetch_array(MYSQLI_NUM);
return is_array($value) ? $value[0] : "";
}
Here's what I ended up with:
function get_col($sql){
global $db;
if(strpos(strtoupper($sql), 'LIMIT') === false) {
$sql .= " LIMIT 1";
}
$query = mysqli_query($db, $sql);
$row = mysqli_fetch_array($query);
return $row[0];
}
This way, if you forget to include LIMIT 1 in your query (we've all done it), the function will append it.
Example usage:
$first_name = get_col("SELECT `first_name` FROM `people` WHERE `id`='123'");
Even this is an old topic, I don't see here pretty simple way I used to use for such assignment:
list($value) = $mysqli->fetch_array;
you can assign directly more variables, not just one and so you can avoid using arrays completely. See the php function list() for details.
This doesn't completely avoid the array but dispenses with it in one line.
function getval($query) {
$mysqli = new mysqli();
$mysqli->connect(HOST, USER, PASS, DB);
return $mysqli->query($query)->fetch_row()[0];
}
First and foremost,
Such a function should support prepared statements
Otherwise it will be horribly insecure.
Also, such a function should never connect on its own, but accept an existing connection variable as a parameter.
Given all the above, only acceptable way to call such a function would be be like
$name = getVal($mysqli, $query, [$param1, $param2]);
allowing $query to contain only placeholders, while the actual data has to be added separately. Any other variant, including all other answers posted here, should never be used.
function getVal($mysqli, $sql, $values = array())
{
$stm = $mysqli->prepare($sql);
if ($values)
{
$types = str_repeat("s", count($values));
$stm->bind_param($types, ...$values);
}
$stm->execute();
$stm->bind_result($ret);
$stm->fetch();
return $ret;
}
Which is used like this
$name = getVal("SELECT name FROM users WHERE id = ?", [$id]);
and it's the only proper and safe way to call such a function, while all other variants lack security and, often, readability.
Try something like this:
$last = $mysqli->query("SELECT max(id) as last FROM table")->fetch_object()->last;
Cheers

call_user_func_array for dynamic mysqli_bind_param [duplicate]

I am attempting to create a database query function which can take multiple parameters and be reused elsewhere, however tried a number of methods online similar to my approach and they are not working as expected.
function query($query, $bindings, $type)
I want to be able to run queries on the go with this single function, this type of function is a lot easier with the PDO driver as you could simply enter the binding inside ->execute($binding); however in this case I am forced to use MySQLi as the application currrently relies on it but wanting to upgrade it to use prepared statements.
An example of how I need to be able to use the function to ensure it is reusable and flexible:
$engine->query("SELECT * FROM accounts WHERE email = :mail AND id = :id",array(':mail' => $_POST['mail'], ':id' => 2))->fetch_assoc();
Lets split each of them up. First is the statement, then the array which is used to bind the parameters used, then the types of the parameters, and finally the type of fetch_ to utilize on the query (ASSOC, OBJECT, ARRAY) etc.
"SELECT * FROM accounts WHERE email = :mail AND id = :id",
array(':mail' => $_POST['mail'], ':id' => 2),
"si"
->fetch_assoc();
though to implement named parameters would be quite a task, the rest is pretty doable.
A PHP >= 5.6 variant, implementing splat operator:
function query($query, $params = NULL, $types = NULL)
{
if (!$params)
{
return $mysqli->query($query);
}
$statement = $this->mysqli->prepare($select);
$types = $types ?: str_repeat('s', count($params));
$statement->bind_param($types, ...$params);
$statement->execute();
return $statement->get_result();
}
used like this
$sql = "SELECT * FROM accounts WHERE email = ? AND id = ?";
$row = $engine->query($sql, [$_POST['mail'], 2])->fetch_assoc();
or, if you want to set types explicitly
$row = $engine->query($sql, [$_POST['mail'], 2], "si")->fetch_assoc();

MySQLi dynamic prepared function with array binding

I am attempting to create a database query function which can take multiple parameters and be reused elsewhere, however tried a number of methods online similar to my approach and they are not working as expected.
function query($query, $bindings, $type)
I want to be able to run queries on the go with this single function, this type of function is a lot easier with the PDO driver as you could simply enter the binding inside ->execute($binding); however in this case I am forced to use MySQLi as the application currrently relies on it but wanting to upgrade it to use prepared statements.
An example of how I need to be able to use the function to ensure it is reusable and flexible:
$engine->query("SELECT * FROM accounts WHERE email = :mail AND id = :id",array(':mail' => $_POST['mail'], ':id' => 2))->fetch_assoc();
Lets split each of them up. First is the statement, then the array which is used to bind the parameters used, then the types of the parameters, and finally the type of fetch_ to utilize on the query (ASSOC, OBJECT, ARRAY) etc.
"SELECT * FROM accounts WHERE email = :mail AND id = :id",
array(':mail' => $_POST['mail'], ':id' => 2),
"si"
->fetch_assoc();
though to implement named parameters would be quite a task, the rest is pretty doable.
A PHP >= 5.6 variant, implementing splat operator:
function query($query, $params = NULL, $types = NULL)
{
if (!$params)
{
return $mysqli->query($query);
}
$statement = $this->mysqli->prepare($select);
$types = $types ?: str_repeat('s', count($params));
$statement->bind_param($types, ...$params);
$statement->execute();
return $statement->get_result();
}
used like this
$sql = "SELECT * FROM accounts WHERE email = ? AND id = ?";
$row = $engine->query($sql, [$_POST['mail'], 2])->fetch_assoc();
or, if you want to set types explicitly
$row = $engine->query($sql, [$_POST['mail'], 2], "si")->fetch_assoc();

Use bind_result & fetch () or store_result() instead get_result

How do I change this function to another function. I don't want to use the get_result
I searched online but could not find an answer that could help me.
public function Select($Table_Name, $Conditions='' ,$Array_Conditions_Limit=NULL , $OrderBy='', $Limit='', $Selected_Fields='*')
{
$Query = "SELECT ".$Selected_Fields." FROM ".$Table_Name;
if(!empty($Conditions))
$Query .= " WHERE ".$Conditions;
if(!empty($OrderBy))
$Query .= " ORDER BY ".$OrderBy;
if(!empty($Limit))
$Query .= " LIMIT ".$Limit;
$Statment = $this->ConnectionResult->prepare($Query);
if(isset($Array_Conditions_Limit) )
{
$Statment = $this->DynamicBindVariables($Statment, $Array_Conditions_Limit);
$Statment->execute();
return $Statment->get_result();
}
else
$Statment->execute();
return $Statment->get_result();
}
This also functions dynamic bind variables
private function DynamicBindVariables($Statment, $Params)
{
if (is_array($Params) && $Params != NULL)
{
// Generate the Type String (eg: 'issisd')
$Types = '';
foreach($Params as $Param)
{
$Types .= $this->GetType($Param);
}
// Add the Type String as the first Parameter
$Bind_names[] = $Types;
// Loop thru the given Parameters
for ($i=0; $i<count($Params);$i++)
{
$Bind_name = 'bind' . $i;
// Add the Parameter to the variable
$$Bind_name = $Params[$i];
// Associate the Variable as an Element in the Array
$Bind_names[] = &$$Bind_name;
}
// Call the Function bind_param with dynamic Parameters
call_user_func_array(array($Statment,'bind_param'), $Bind_names);
}
elseif(isset($Params) && !empty($Params))
{
$Types = '';
$Types .= $this->GetType($Params);
$Statment->bind_param($Types ,$Params);
}
return $Statment;
}
I using the return value as follows:
$myresult =Select('post','post_category=?' ,2 );
$row = $myresul2->fetch_object()
First of all, I find this approach utterly useless. What are you actually doing is dismembering fine SQL sentence into some anonymous parts.
"SELECT * FROM post WHERE post_category=?"
looks WAY better than your anonymous parameters of which noone have an idea.
'post','post_category=?'
One can tell at glance what does first statement to do. and have no idea on the second. Not to mention it's extreme:
'post','post_category=?',NULL, NULL, 'username, password'
So, instead of this kindergarten query builder I would rather suggest a function that accepts only two parameters - a query itself and array with bound data:
$myresult = Select("SELECT * FROM post WHERE post_category=?", [2]);
To make it more useful, I wouild make separate functions to get different result types, making your second line with fetch_object() obsolete (however, speaking of objects, they are totally useless to represent a table row). Example:
$row = $db->selectRow("SELECT * FROM post WHERE post_category=?", [2]);
Look: it's concise yet readable!
As a further step you may wish to implement more placeholder types, to allow fields for ORDER BY clause be parameterized as well:
$data = $db->getAll('id','SELECT * FROM t WHERE id IN (?a) ORDER BY ?n', [1,2],'f');
you can see how it works, as well as other functions and use cases in my safeMysql library

Parameterized query with several optional search terms

I have a web application with lots of data, and a search/filter function with several fields, such as name, status, date, and so on. I have been using parameterized queries like this for the regular (non-search) queries:
$id = $_POST['itemID'];
$db = mysqli_connect($host, $username, $password, $database);
$sql_query = "SELECT * FROM tbl_data WHERE ID = ?";
$stmt_query = mysqli_prepare($db, $sql_query);
mysqli_stmt_bind_params($stmt_query, "i", $id);
mysqli_stmt_execute($stmt_query);
//and so on..
How would I protect agains SQL-injection with multiple, optional parameters? There can be up to 10 separate parameters that might or might not be set.
Edit as the question seems unclear:
My example was with one parameter, which is not optional. I know this protects against sql-injection. How would I go about doing this with 10 parameters, where one or several could be set at the same time? E.g. a query such as this:
SELECT * FROM tbl_data
WHERE NAME = ?
AND STATUS = ?
AND DATE = ?
AND PARAM4 = ?
AND PARAM5 = ?
where the user only wants to search on name and date. How would I do the binding? It's not a good idea to check for each of the 100 possible combinations of search terms.
You are already protected against sql injection, as you are using the mysqli_stmt_bind_params which will escape properly for you.
Edit.
You could switch to some database framework to have a clean and beautiful code.
Otherwise... this is so old spaghetti style... but I love it :D
It's quite easy to expand your code to work with an unknown number of parameters. You should just loop on your parameters and at the same time 1. build your query string with the question mark notation, and add your parameters to an array, which you will be passing to maxdb_stmt_bind_param ( resource $stmt , string $types , array &$var ).
So it would look like this. It assumes at least ONE parameter is there (but it's trivial to avoid this).
$sql = "SELECT * FROM tbl_data WHERE ";
$and = '';
$types = '';
$parameters = array();
foreach($_POST as $k => $v) {
// check that $k is on your whitelist, if not, skip to the next item
$sql .= "$and $k = ?";
$and = " AND ";
$parameters[] = $v;
$types .= 's';
}
$stmt_query = mysqli_prepare($db, $sql);
mysqli_stmt_bind_params($stmt_query, $types, $parameters);
I recommend switching to PDO. It's built into PHP like the mysqli extension, but has a cleaner syntax and allows you to pass in your parameter values as an array, which you can easily construct dynamically with as many elements as needed.

Categories