PDO and SQL IN statements [duplicate] - php

This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 9 years ago.
Im using a sequel for search like this using PDOs
$states = "'SC','SD'";
$sql = "select * from mytable where states in (:states)";
$params = array(':states'=>$states);
and I use my function
$result = $this->selectArrayAssoc($sql, $params);
where my selectArrayAssoc function as following
public function selectArrayAssoc($sql, $params = array())
{
try {
$sth = $this->db->prepare($sql);
$sth->execute($params);
$result = $sth->setFetchMode(PDO::FETCH_ASSOC);
return $sth->fetchAll();
} catch(PDOException $e) {
print $e->getMessage();
//Log this to a file later when in production
exit;
}
}
it does not take the quoted variables, I think it is suppressing, in such cases how to deal with this.

When using prepared statement placeholders (parameter binding) in general, each occurrence of a placeholder holds exactly one variable.
You're trying to pass several. What's happening is basically that your parameters are escaped: Your :states is replaced with '''SC'',''SD''' or '\'SC\',\'SD\'' internally, rather than with just the raw 'SC','SD' that you want.

pinkgothic is absolute correct. But I think you got the problem, that you have an array of 'states' and want work with this array. You've to prepare the Placeholder for each value in the query.
$states = array('SC','SD');
$phArray = array();
$valArray = array();
foreach($ids AS $key=>$value){
array_push($phArray, ':PH' . $key);
$valArray[':PH' . $key] = $value;
}
$sql = 'select * from mytable where states in (' . implode(',', $phArray) . ')';
$params = array($valArray);
$result = $this->selectArrayAssoc($sql, $params);

Related

Need a 2023 answer to PDO BIND IN clause [duplicate]

This question already has answers here:
PHP - Using PDO with IN clause array
(9 answers)
Closed 24 days ago.
I did have this code working but it was not secure from SQL injection so I tried to update it. The user submit filter requirements which come from check boxes, I take them from post, and replace them with ?, I then implode, and bind them back together for the IN clause.
I feel like I am a couple of lines of code away from getting this. What am I doing wrong? I have echoed out after implode and it shows the right amount of ? corresponding to the selections. Is the problem in my execute statement?
if(isset($_POST['songgenre'])){
$songgenre = $_POST['songgenre'];
$placeholderssonggenre = array_fill(1, count($songgenre), '?');
$songgenrefilter = implode(',', $placeholderssonggenre);
}else{
$songgenre ='';
$genreempty = '';
}
$sql = "SELECT * FROM music_db WHERE songgenre IN ($songgenrefilter)";
$stmt = $conn->prepare($sql);
$res = $stmt->execute($songgenre);
if ($res !== FALSE) {
$results = $res->rowCount();
echo($results);
} else {
echo "Code Failed";
}
I know really how frustrating it could be. Well, it seems that following things are missing there. Try it:
When you're using the IN clause, you need to pass an array of values to the execute() method, not a single variable. This means that you should be passing $songgenre directly to the execute() method, instead of using it to create the $placeholderssonggenre variable.
You should bind the parameters to the statement before executing it, not after. You can use the bindValue() method to bind the values to the placeholders in the query.
When you execute the statement, you should use the fetchAll() method to retrieve the rows, instead of rowCount().
Here's the correct code:
if(isset($_POST['songgenre'])){
$songgenre = $_POST['songgenre'];
$placeholders = array_fill(0, count($songgenre), '?');
$placeholders = implode(',', $placeholders);
} else {
$songgenre ='';
$placeholders = '';
}
$sql = "SELECT * FROM music_db WHERE songgenre IN ($placeholders)";
$stmt = $conn->prepare($sql);
foreach ($songgenre as $i => $value) {
$stmt->bindValue($i + 1, $value);
}
$stmt->execute();
$results = $stmt->fetchAll();
Another option more elegant
if(isset($_POST['songgenre'])){
$postfilter = [
'songgenre' => ['filter' => FILTER_SANITIZE_STRING] // always sanitize
];
$filter_post_array = filter_var_array($_POST, $postfilter);
$filtered = $filter_post_array['songgenre'];
} else {
$filtered = [];
}
$sql = "SELECT * FROM music_db WHERE songgenre IN (:songgenrefilter)";
$stmt = $conn->prepare($sql);
$stmt->bindValue(':songgenrefilter',implode(',',$filtered);
$stmt->execute();
$result = $stmt->fetchAll();

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

PDO results are empty

I decided to flip from mysqli/mysqlnd to PDO, however I am encountering a problem I had the last time I did this. I am trying this again as it seems that PDO allegedly supports passing a variable that contains an array of values to the execute() param for binding to the query without having to use things like call_user_func_array.
The code I have for demonstration is :
$bind_arguments[] = "dogs";
$bind_arguments[] = "cats";
$bind_arguments[] = "birds";
$db = new PDO('mysql:dbname=' . SQL_DATA . ';host=' . SQL_SERVER, SQL_USERNAME, SQL_PASSWORD, array (
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
));
$sql = 'SELECT `name` FROM `pets` WHERE `type` = ? OR `type` = ? OR `type` = ?';
$result = Array();
try {
if($stmt = $db->prepare($sql)) {
$stmt->execute($bind_arguments);
$result = $stmt->fetchAll();
}
} catch(PDOException $e) {
echo 'Wrong SQL: ' . $sql . ' Error: ' . $e->getMessage(); exit;
}
$db = null;
var_export($result); // null
I don't get any exceptions, however $result is null. If I do the regular query using Navicat (or using mysqli) It works!
See Example #5, which shows I should be able to do this (posting example from there here for reference) :
<?php
/* Execute a prepared statement using an array of values for an IN clause */
$params = array(1, 21, 63, 171);
/* Create a string for the parameter placeholders filled to the number of params */
$place_holders = implode(',', array_fill(0, count($params), '?'));
/*
This prepares the statement with enough unnamed placeholders for every value
in our $params array. The values of the $params array are then bound to the
placeholders in the prepared statement when the statement is executed.
This is not the same thing as using PDOStatement::bindParam() since this
requires a reference to the variable. PDOStatement::execute() only binds
by value instead.
*/
$sth = $dbh->prepare("SELECT id, name FROM contacts WHERE id IN ($place_holders)");
$sth->execute($params);
?>
Also see Example #1 posted below for convenience :
<?php
$sth = $dbh->prepare("SELECT name, colour FROM fruit");
$sth->execute();
/* Fetch all of the remaining rows in the result set */
print("Fetch all of the remaining rows in the result set:\n");
$result = $sth->fetchAll();
print_r($result);
?>
So why is this not working ? What am I doing wrong ?
Update
I made some typos when posting my code (which was stripped out of a larger class) for StackOverflow's MVCE requirements. These typos were not present in the original class. I have updated them in the code above. - sorry for any confusion this may have caused.
You are assigning values to $bind_array and $bnid_array but are sending in $bind_arguments to execute(). Try changing $bnid_array to $bind_array and use $stmt->execute($bind_array);

How to use php array in a Prepared Statement for SQL IN Operator using SQLi? [duplicate]

This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 8 years ago.
This is my code:
if(isset($_POST['abc']))
{
$things['abc'] = mysqli_real_escape_string($connect, implode("','", $_POST['abc']));
$result = mysqli_query($connect, "SELECT * FROM this_list WHERE abc_column IN ('{$things['abc']}')");
if (!$result)
{
echo "Error fetching results: " . mysqli_error();
}
else
{
while ($row = mysqli_fetch_array($result))
{
$abc[] = $row['description'];
}
}
}
The above code uses mysqli_real_escape_string(), and $things is an array with checkbox values that is received via POST. This array contains the list of strings separated by comma that I am using in the query.
When I was searching on the net, I noticed that some people say mysqli_real_escape_string() may prevent sql injection, I was thinking maybe prepared statement for checkbox values might be more safer against sql injection.
I have used prepared statement with separate parameters to prevent sql injection. But I am stuck on this one and I dont know how to change the above code to a prepare() statement since it uses an array $things['abc']. I tried searching and everytime I search array in prepared statement, I am getting info on Java, etc.. Can someone enlighten me on how I can do this with php please?
EDIT:
After the help from onetrickpony code below, this is what I have now:
if(isset($_POST['abc']))
{
$ph = rtrim(str_repeat('?,', count($_POST['abc'])), ',');
$query = sprintf("SELECT col1 FROM abc_table WHERE col2 IN (%s)", $ph);
$stmt = mysqli_prepare($connect, $query);
// bind variables
$params = array();
foreach($_POST['abc'] as $v)
$params[] = &$v;
array_unshift($params, $stmt, str_repeat('s', count($_POST['abc']))); // s = string type
call_user_func_array('mysqli_stmt_bind_param', $params);
mysqli_stmt_execute($stmt);
// Get the data result from the query.
mysqli_stmt_bind_result($stmt, $col1);
/* fetch values and store them to each variables */
while (mysqli_stmt_fetch($stmt)) {
$name[] = $col1;
echo $name;
}
//loop to echo and see whats stored in the array above
foreach($name as $v) {
echo $v;
}
// Close the prepared statement.
$stmt->close();
}
In the above code, the sqli method for prepare statement seems to work which is great. However, when I use the mysqli_stmt_bind_result(), the $name[] array inside the while loop only seems to print the last row.
UPDATE:
onetrickpony's code with the mysqli method for using php array in a Prepared Statement worked fine and it was a very good approach he had suggested. However, I have been having nightmare with the second half of the code which is trying to get the fetched array results to work. After trying for more than a day, I have given up on that and I have made the switch to PDO. Again onetrickpony's advise below was totally worth it. Making the switch to PDO made the code so much easier and simpler and couldnt believe it.
Try this:
// build placeholder string (?,?...)
$ph = rtrim(str_repeat('?,', count($_POST['abc'])), ',');
$query = sprintf("SELECT * FROM this_list WHERE abc_column IN (%s)", $ph);
$stm = mysqli_prepare($connect, $query);
// bind variables (see my notes below)
$params = array();
foreach($_POST['abc'] as $v)
$params[] = &$v;
// s = string type
array_unshift($params, $stm, str_repeat('s', count($_POST['abc'])));
call_user_func_array('mysqli_stmt_bind_param', $params);
mysqli_stmt_execute($stm);
It appears that mysqli_stmt_bind_param cannot be called multiple times to bind multiple variables. And even worse, it requires referenced variables. I'd recommend you switch to PDO, just because of these limitations that force you to write ugly code :)

Is it possible to fetch_object while using bind_param? (PHP/MySQLi)

I have a question for you guys. I'm trying to make the way that I run MySQL as secure as I can. I'm currently wondering if it's possible to fetch an object with MySQLi after I have prepared the statement, binded the parameters, and executed the statement.
Example:
$sql = $mysqli->prepare('SELECT * FROM users WHERE username = ?;');
$sql->bind_param('s', $username);
$username = 'RastaLulz';
$sql->execute();
$object = $sql->fetch_object();
echo $object->mail;
I get the following error:
Fatal error: Call to a member function fetch_object() on a non-object in C:\xampp\htdocs\ProCMS\DevBestCMS\inc\global\class.mysql.php on line 23
However, when I add "$sql->result_metadata();" I don't get an error, but it doesn't return a result (it's just NULL).
$sql = $mysqli->prepare('SELECT * FROM users WHERE username = ?;');
$sql->bind_param('s', $username);
$username = 'RastaLulz';
$sql->execute();
$result = $sql->result_metadata();
$object = $result->fetch_object();
echo $object->mail;
This is how you'd do it without binding the parameters:
$sql = $mysqli->query("SELECT * FROM users WHERE username = 'RastaLulz';");
$object = $sql->fetch_object();
echo $object->mail;
Here's my current MySQL class - just need to get the execute function working.
http://uploadir.com/u/lp74z4
Any help is and will be appreciated!
I had the same question. I found out that I could do the following:
# prepare statement
$stmt = $conn->prepare($sql)
# bind params
$stmt->bind_param("s", $param);
# execute query
$stmt->execute();
# get result
$result = $stmt->get_result();
# fetch object
$object = $result->fetch_object();
I hope that works for you, too.
I just dug around in my Database class and this is how I do it. Honestly I don't remember why I needed to do it this way and there might be a much better way. But if it helps you here is the code. I do vaguely remember being irritated about there not being a simple way to get your results as an object.
// returns an array of objects
public function stmtFetchObject(){
$rows=array(); //init
// bind results to named array
$meta = $this->stmt->result_metadata();
$fields = $meta->fetch_fields();
foreach($fields as $field) {
$result[$field->name] = "";
$resultArray[$field->name] = &$result[$field->name];
}
call_user_func_array(array($this->stmt, 'bind_result'), $resultArray);
// create object of results and array of objects
while($this->stmt->fetch()) {
$resultObject = new stdClass();
foreach ($resultArray as $key => $value) {
$resultObject->$key = $value;
}
$rows[] = $resultObject;
}
return $rows;
}
What is the ';' at the end of your statement? You are giving mysqli an invalid query and so it is not creating an object for you.
The problem is not the fetch_object, but the prepare statement.
Remove the ';' and try again. It should work like a charm.
I've never seen a query end like that.
Try instantiating the variable before binding.
I think its just good practice but use double quotes instead of single quotes.

Categories