Mysqli prepared statement : several WHERE clauses and WHERE IN (Array) [duplicate] - php

This question already has answers here:
How can I bind an array of strings with a mysqli prepared statement?
(7 answers)
Closed 11 months ago.
I need to run the query below. It asks me a lot of trouble. In fact, I have several "WHERE" conditions, one that requires the decomposition of an Array.
This issue helped me but it doesn't have several conditions "WHERE" .
$array = (1,2,3,4,5,6,7,8,9,10);
$clause = implode(',', array_fill(0, count($array), '?'));
if($request = $this->getConnexion()->prepare('SELECT col1, col2 FROM table WHERE col1 IN ('.$clause.') AND col2>=?') or die(mysqli_error($this->getConnexion()))) {
// The problem starts here
call_user_func_array(array($request, 'bind_param'), $array);
$request->bind_param('i', $this->getTime());
// Until here
$request->execute();
$request->bind_result($col1, $col2);
$request->store_result();
// Following the code
}

The important thing here is that you are calling bind_param() just once, with an array containing all of the parameters you'll need to bind, so your solution will be to just add the additional WHERE clause parameter onto your $array of values to bind. The IN() clause isn't a special case requiring call_user_func_array() separated from other parameters. You call it on all of them.
Something is missing though - bind_param()'s first parameter is a string of data types. All your types are i, so you'll need to use str_repeat() to create that.
// Eventually, this array will contain the other params too
$array = (1,2,3,4,5,6,7,8,9,10);
// This creates a string of ?,?,?,?... for the IN () clause
$clause = implode(',', array_fill(0, count($array), '?'));
// Add the additional value onto the $array array
// so the last param is bound with the others.
$array[] = $this->getTime();
$types = str_repeat('i', count($array));
// The params passed to call_user_func_array() must have as references, each subsequent value. Add those in a loop, but start with the $types string
$params = array($types);
foreach ($array as $key => $value) {
$params[] = &$array[$key];
}
if($request = $this->getConnexion()->prepare('SELECT col1, col2 FROM table WHERE col1 IN ('.$clause.') AND col2>=?') or die(mysqli_error($this->getConnexion()))) {
// Then bind_param() is called on all parameters
// using the $params array which includes types and param references
call_user_func_array(array($request, 'bind_param'), $params);
// Execute & fetch.
$request->execute();
$request->bind_result($col1, $col2);
$request->store_result();
// Following the code
}

Related

Dynamic select mysqli query with dynamic parameters returns error doesn't match number of bind variables [duplicate]

This question already has answers here:
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Closed 11 months ago.
I'm trying to create a select query with dynamic where clause and dynamic parameters but I always get error :
Warning: mysqli_stmt::bind_param(): Number of elements in type
definition string doesn't match number of bind variables
Which I sincerely do not understand since it seems the count is alright. So this is what the code really looks like in its rude format. I can't see what I'm doing wrong.
//get variables
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';
//convert string to array
$socialArray = explode(',', $mediaArray)
//declare some variables to be used later
$andwhere = '';
$bp = '';
$socialmarray = ''
//get every value from array of social media
foreach($socialArray as $socialmedia){
$socialmarray .=$socialmedia.',';
$andwhere .= " AND socialmedianame=?";
$bp .='s';
}
//test strings
echo $wheres = $andwhere;//AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?
echo $bip = $bp.'s';//ssss
echo $validarayy = rtrim($socialmarray,',');//Facebook,Twitter,Twitch
//select query
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$selectquery->execute();
$resultquery = $selectquery->get_result();
Because:
You are using user-supplied data, you must assume that your query is vulnerable to a malicious injection attack and
the amount of data that is to be built into the query is variable/indefinite and
you are only writing conditional checks on a single table column
You should use a prepared statement and merge all of the WHERE clause logic into a single IN statement.
Building this dynamic prepared statement is more convoluted (in terms of syntax) than using pdo, but it doesn't mean that you need to abandon mysqli simply because of this task.
$mediaArray ='Facebook,Twitter,Twitch,';
$otherMedia = 'House';
$media = array_unique(explode(',', $mediaArray . $otherMedia));
$count = count($media);
$conn = new mysqli("localhost", "root", "", "myDB");
$sql = "SELECT * FROM mediaservices";
if ($count) {
$stmt = $conn->prepare("$sql WHERE socialmedianame IN (" . implode(',', array_fill(0, $count, '?')) . ")");
$stmt->bind_param(str_repeat('s', $count), ...$media);
$stmt->execute();
$result = $stmt->get_result();
} else {
$result = $conn->query($sql);
}
foreach ($result as $row) {
// access values like $row['socialmedianame']
}
For anyone looking for similar dynamic querying techniques:
SELECT with dynamic number of LIKE conditions
INSERT dynamic number of rows with one execute() call
In your query:
$selectquery = $conn->prepare("select * from mediaservices where socialmedianame=? $wheres");
The ? represents one parameter to pass in, and the evaluation of $wheres adds another three, giving you four total parameters.
bind_param() should take a string representing the types of the variables to insert as the first parameter, and the variables themselves as the subsequent parameters.
In your bind:
$selectquery->bind_param("$bip",$otherMedia,$validarayy);
$bip evaluates to ssss and $otherMedia is a single string ("House"). You might expect $validarayy to be three strings, but rtrim() returns a string. Thus, it is only one string ("Facebook,Twitter,Twitch"). You pass through two variables when the query is expecting four:
$conn->prepare("select * from mediaservices where socialmedianame=House AND socialmedianame=Facebook,Twitter,Twitch AND socialmedianame=? AND socialmedianame=? AND socialmedianame=?"
To correct this, you'll want to convert $validarayy back to an array, and use the index for the various inputs:
$socialmarray2 = explode(',', $validarayy);
$selectquery->bind_param("$bip", $otherMedia, $socialmarray2[0], $socialmarray2[1], $socialmarray2[2]);
Also note that your sample code has a few missing semicolons; you'll need to fix these in order for your code to work correctly.
This can be seen working here.
Finally, note that even if you were to split the three strings out correctly, the selection of ... AND socialmedianame=Facebook AND socialmedianame=Twitter AND socialmedianame=Twitch will never match any results; socialmedianame can only contain one value. You're probably looking to substitute your AND statements with OR statements.

Using mysql_real_escape_string() instead prepared statement in IN operator [duplicate]

This question already has answers here:
How can I bind an array of strings with a mysqli prepared statement?
(7 answers)
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Closed 2 years ago.
I have an array full of random content item ids. I need to run a mysql query (id in the array goes in the WHERE clause), using each ID that's in the array, in the order that they appear in the said array. How would I do this?
This will be an UPDATE query, for each individual ID in the array.
As with nearly all "How do I do SQL from within PHP" questions - You really should use prepared statements. It's not that hard:
$ids = array(2, 4, 6, 8);
// prepare an SQL statement with a single parameter placeholder
$sql = "UPDATE MyTable SET LastUpdated = GETDATE() WHERE id = ?";
$stmt = $mysqli->prepare($sql);
// bind a different value to the placeholder with each execution
for ($i = 0; $i < count($ids); $i++)
{
$stmt->bind_param("i", $ids[$i]);
$stmt->execute();
echo "Updated record ID: $id\n";
}
// done
$stmt->close();
Alternatively, you can do it like this:
$ids = array(2, 4, 6, 8);
// prepare an SQL statement with multiple parameter placeholders
$params = implode(",", array_fill(0, count($ids), "?"));
$sql = "UPDATE MyTable SET LastUpdated = GETDATE() WHERE id IN ($params)";
$stmt = $mysqli->prepare($sql);
// dynamic call of mysqli_stmt::bind_param hard-coded eqivalent
$types = str_repeat("i", count($ids)); // "iiii"
$args = array_merge(array($types), $ids); // ["iiii", 2, 4, 6, 8]
call_user_func_array(array($stmt, 'bind_param'), ref($args)); // $stmt->bind_param("iiii", 2, 4, 6, 8)
// execute the query for all input values in one step
$stmt->execute();
// done
$stmt->close();
echo "Updated record IDs: " . implode("," $ids) ."\n";
// ----------------------------------------------------------------------------------
// helper function to turn an array of values into an array of value references
// necessary because mysqli_stmt::bind_param needs value refereces for no good reason
function ref($arr) {
$refs = array();
foreach ($arr as $key => $val) $refs[$key] = &$arr[$key];
return $refs;
}
Add more parameter placeholders for other fields as you need them.
Which one to pick?
The first variant works with a variable number of records iteratively, hitting the database multiple times. This is most useful for UPDATE and INSERT operations.
The second variant works with a variable number of records too, but it hits the database only once. This is much more efficient than the iterative approach, obviously you can only do the same thing to all affected records. This is most useful for SELECT and DELETE operations, or when you want to UPDATE multiple records with the same data.
Why prepared statements?
Prepared statements are a lot safer because they make SQL injection attacks impossible. This is the primary reason to use prepared statements, even if it is more work to write them. A sensible habit to get into is: Always use prepared statements, even if you think it's "not really necessary." Neglect will come and bite you (or your customers).
Re-using the same prepared statement multiple times with different parameter values is more efficient than sending multiple full SQL strings to the database, because the database only needs to compile the statement once and can re-use it as well.
Only parameter values are sent to the database on execute(), so less data needs to go over the wire when used repeatedly.
In longer loops the execution time difference between using a prepared statement and sending plain SQL will become noticeable.
Using the "IN" Clause
Might be what you're after
$ids = array(2,4,6,8);
$ids = implode($ids);
$sql="SELECT * FROM my_table WHERE id IN($ids);";
mysql_query($sql);
otherwise, what's wrong with
$ids = array(2,4,6,8);
foreach($ids as $id) {
$sql="SELECT * FROM my_table WHERE ID = $id;";
mysql_query($sql);
}
Amen to Tomalak's comment on statements.
However, if you do not wish to use mysqli, you can always use intval() to prevent injection:
$ids = array(2, 4, 6, 8);
for ($i = 0; $i < count($ids); $i++)
{
mysql_query("UPDATE MyTable SET LastUpdated = GETDATE() WHERE id = " . intval($ids[$i]));
}
$values_filtered = array_filter('is_int', $values);
if (count($values_filtered) == count($values)) {
$sql = 'update table set attrib = 'something' where someid in (' . implode(',', $values_filtered) . ');';
//execute
} else {
//do something
}
You could do something like the following, however you need to be VERY careful that the array only contains integers otherwise you could end up with SQL injection.
You really don't want to be doing multiple queries to get the content out if you can help it. Something like this might be what you are after.
foreach ($array as $key = $var) {
if ((int) $var <= 0) {
unset($array[$key]);
}
}
$query = "SELECT *
from content
WHERE contentid IN ('".implode("','", $array)."')";
$result = mysql_query($query);

Insert data with associative array using PDO

Before you duplicate your question, I read all answers that it's has a relation with my question. I'm trying to insert data with associative array for example
<?php
$data = array(
'fname'=>'joe',
'lname'=>'sina'
);
foreach ($data as $key=>$value) {
}
?>
I want to display data like this
/*insert into tblname($key)values($value);
finally the query will appear correctly format */
insert into tblname('fname','lname') values('joe','sina');
You don't need to use foreach here. If you just prepare and bind the query, you can pass $data to the execute() and get the keys by implode() on the keys.
$data = array(
'fname'=>'joe',
'lname'=>'sina'
);
$stmt = $pdo->prepare("INSERT INTO tblname (".implode(', ', array_keys($data)).") VALUES (:".implode(', :', array_keys($data)).")");
$stmt->execute($data);
The keys in the array must match the placeholders in the query (the ones with a colon in front of it). You also had a syntax error in the query, as columns cannot be quoted by singlequotes.

PHP: "Value Expected To Be A Reference" with Call_User_Func and Bind Params [duplicate]

This question already has answers here:
mysqli bind_param() expected to be a reference, value given
(3 answers)
Closed 11 months ago.
I have been trying to bind an array with a prepared statement for days, I have managed to create the number of ?,? and the i,i these are represented in my code as $params and $type. The problem comes when using call_user_func_array with my code since I haven't managed to make it work, I have tried multiple answers from this site but I haven't got it working.
The full query code is:
$params = implode(',', array_fill(0, count($greaterThan), '?'));
$stmt11 = $mysqli->prepare("SELECT nombre FROM usuarios WHERE id IN (".$params.")");
$type = implode('', array_fill(0, count($greaterThan), 'i'));
$param = implode(",", $greaterThan);
call_user_func_array(array($stmt11, "bind_param"), array_merge(array($type), $greaterThan));
print_r(error_get_last());
$stmt11->execute();
$stmt11->store_result();
$stmt11->bind_result($nombresmayores);
$arraynombresmayores = array();
$stmt11->store_result();
while($stmt11->fetch()){
$arraynombresmayores[] = $nombresmayores;
}
Where $param are the values separated by comas (just in case you need it). The array I'm trying to bind is $greaterThan, the query works perfectly because I have made some debugging. The error the program outputs is:
Parameter 2 to mysqli_stmt::bind_param() expected to be a reference, value given
Finally, the content of the array is:
array(2) {
[0]=>
int(2)
[1]=>
int(4)
}
if your php version is not outdated you can make it much simper
$params = implode(',', array_fill(0, count($greaterThan), '?'));
$stmt = $mysqli->prepare("SELECT nombre FROM usuarios WHERE id IN ($params)");
$types = str_repeat('i',count($greaterThan));
$stmt->bind_param($types, ...$greaterThan);
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($nombresmayores);
$arraynombresmayores = array();
$stmt->store_result();
while($stmt->fetch()){
$arraynombresmayores[] = $nombresmayores;
}
but better yet use PDO:
$params = implode(',', array_fill(0, count($greaterThan), '?'));
$stmt = $pdo->prepare("SELECT nombre FROM usuarios WHERE id IN ($params)");
$stmt->execute($greaterThan);
$arraynombresmayores = $stmt->fetchAll(PDO::FETCH_COLUMN);
just compare this neat code with that awful mess you have now.
I have method i use in an class i made to work mysqli in a fashinable way. The key reside in the foreach loop: Basicaly you create a copy of your array with another one by reference and then bind the referenced array. Hope this helps.
UPDATED :
in your case you need to create an array with your string $type and the array of greateThan. From the temp to the bind array, make sure the keys stay the sames and don't unset your data before having called the bond_param
$type = implode('', array_fill(0, count($greaterThan), 'i'));
//$param = implode(",", $greaterThan); //Not needed
//merge the 'type' as an array and the variables to pass
$temp_params= array_merge([$type],$greaterThan);
//Create a referenced array but dont'reference the 'type' argument
foreach($temp_params as $k=>$v){
if($k===0){
$bind_params[$k]=$temp_params[$k];
}else{
$bind_params[$k]=&$temp_params[$k];
}
}
//use the referenced array
call_user_func_array(array($stmt11, "bind_param"), array_merge(array($type), $bind_params));
Just try to assign that expression to a variable, and bind a variable. Expressions cannot be used as references, so you have to pass a variable.

I have an array of integers, how do I use each one in a mysql query (in php)? [duplicate]

This question already has answers here:
How can I bind an array of strings with a mysqli prepared statement?
(7 answers)
Use an array in a mysqli prepared statement: `WHERE .. IN(..)` query [duplicate]
(8 answers)
Closed 2 years ago.
I have an array full of random content item ids. I need to run a mysql query (id in the array goes in the WHERE clause), using each ID that's in the array, in the order that they appear in the said array. How would I do this?
This will be an UPDATE query, for each individual ID in the array.
As with nearly all "How do I do SQL from within PHP" questions - You really should use prepared statements. It's not that hard:
$ids = array(2, 4, 6, 8);
// prepare an SQL statement with a single parameter placeholder
$sql = "UPDATE MyTable SET LastUpdated = GETDATE() WHERE id = ?";
$stmt = $mysqli->prepare($sql);
// bind a different value to the placeholder with each execution
for ($i = 0; $i < count($ids); $i++)
{
$stmt->bind_param("i", $ids[$i]);
$stmt->execute();
echo "Updated record ID: $id\n";
}
// done
$stmt->close();
Alternatively, you can do it like this:
$ids = array(2, 4, 6, 8);
// prepare an SQL statement with multiple parameter placeholders
$params = implode(",", array_fill(0, count($ids), "?"));
$sql = "UPDATE MyTable SET LastUpdated = GETDATE() WHERE id IN ($params)";
$stmt = $mysqli->prepare($sql);
// dynamic call of mysqli_stmt::bind_param hard-coded eqivalent
$types = str_repeat("i", count($ids)); // "iiii"
$args = array_merge(array($types), $ids); // ["iiii", 2, 4, 6, 8]
call_user_func_array(array($stmt, 'bind_param'), ref($args)); // $stmt->bind_param("iiii", 2, 4, 6, 8)
// execute the query for all input values in one step
$stmt->execute();
// done
$stmt->close();
echo "Updated record IDs: " . implode("," $ids) ."\n";
// ----------------------------------------------------------------------------------
// helper function to turn an array of values into an array of value references
// necessary because mysqli_stmt::bind_param needs value refereces for no good reason
function ref($arr) {
$refs = array();
foreach ($arr as $key => $val) $refs[$key] = &$arr[$key];
return $refs;
}
Add more parameter placeholders for other fields as you need them.
Which one to pick?
The first variant works with a variable number of records iteratively, hitting the database multiple times. This is most useful for UPDATE and INSERT operations.
The second variant works with a variable number of records too, but it hits the database only once. This is much more efficient than the iterative approach, obviously you can only do the same thing to all affected records. This is most useful for SELECT and DELETE operations, or when you want to UPDATE multiple records with the same data.
Why prepared statements?
Prepared statements are a lot safer because they make SQL injection attacks impossible. This is the primary reason to use prepared statements, even if it is more work to write them. A sensible habit to get into is: Always use prepared statements, even if you think it's "not really necessary." Neglect will come and bite you (or your customers).
Re-using the same prepared statement multiple times with different parameter values is more efficient than sending multiple full SQL strings to the database, because the database only needs to compile the statement once and can re-use it as well.
Only parameter values are sent to the database on execute(), so less data needs to go over the wire when used repeatedly.
In longer loops the execution time difference between using a prepared statement and sending plain SQL will become noticeable.
Using the "IN" Clause
Might be what you're after
$ids = array(2,4,6,8);
$ids = implode($ids);
$sql="SELECT * FROM my_table WHERE id IN($ids);";
mysql_query($sql);
otherwise, what's wrong with
$ids = array(2,4,6,8);
foreach($ids as $id) {
$sql="SELECT * FROM my_table WHERE ID = $id;";
mysql_query($sql);
}
Amen to Tomalak's comment on statements.
However, if you do not wish to use mysqli, you can always use intval() to prevent injection:
$ids = array(2, 4, 6, 8);
for ($i = 0; $i < count($ids); $i++)
{
mysql_query("UPDATE MyTable SET LastUpdated = GETDATE() WHERE id = " . intval($ids[$i]));
}
$values_filtered = array_filter('is_int', $values);
if (count($values_filtered) == count($values)) {
$sql = 'update table set attrib = 'something' where someid in (' . implode(',', $values_filtered) . ');';
//execute
} else {
//do something
}
You could do something like the following, however you need to be VERY careful that the array only contains integers otherwise you could end up with SQL injection.
You really don't want to be doing multiple queries to get the content out if you can help it. Something like this might be what you are after.
foreach ($array as $key = $var) {
if ((int) $var <= 0) {
unset($array[$key]);
}
}
$query = "SELECT *
from content
WHERE contentid IN ('".implode("','", $array)."')";
$result = mysql_query($query);

Categories