I have function in PHP, which should bind in MySQL IN statement so many variables, that is in array.
My problem is that variable and key is changing but function bind only last value n times.
I don't have idea where is the problem...
Here is my class method:
public function getOtListByOtNumbers($conditions){
$data_array = $conditions[SEARCH_OT];
# To find last key (remove coma)
$quantity = count($data_array);
$marks = '';
# Bind name string && rewriting value as integer
foreach ($data_array as $key => $value){
$i = $key+1;
if ($i == $quantity){
$marks .= ':key'.$i;
}
else {
$marks .= ':key'.$i.', ';
}
}
# Query
$sql="
SELECT
c_int_id,
c_ot,
c_tickets_amount,
c_basket_value,
c_person,
c_company,
c_city,
c_package_number,
c_delivery_method,
c_ot_date,
c_email,
c_phone,
c_comment,
c_send
FROM ws_orders
WHERE c_ot IN (".$marks.")
ORDER BY c_int_id DESC
LIMIT :first, :last ";
$stmt = $this->PDO->prepare($sql);
# Bind n values
// Here is a problem
var_dump($data_array); // var dump 1
foreach ($data_array as $key => $param){
$key_number = $key +1;
$key_name = 'key'.$key_number;
$stmt->bindParam($key_name, $param, PDO::PARAM_INT);
var_dump($key_name); // var dump 2
var_dump($param); // var dump 3
}
# Bind limit values
$stmt->bindParam('first', $conditions[OT_SEARCH_FIRST_ROW], PDO::PARAM_INT);
$stmt->bindParam('last', $conditions[OT_SEARCH_ROW_LIMIT], PDO::PARAM_INT);
# If executed return result
if ($stmt->execute() != FALSE) {
$stmt_result = $stmt->fetchAll();
$stmt->closeCursor();
var_dump($stmt_result); // var dump 4
# If not executed print debug and return FALSE
} else {
var_dump($stmt->errorInfo());
$this->debugQuery($stmt);
$stmt_result = FALSE;
}
return $stmt_result;
}
Here are var dupms:
var dump 1
array (size=2)
0 => string '2761531'
1 => string '2760650'
var dump 2 & 3
string 'key1' (length=4)
string '2761531'
string 'key2' (length=4)
string '2760650'
SQL query from this execution
SELECT
c_int_id,
c_ot,
c_tickets_amount,
c_basket_value,
c_person,
c_company,
c_city,
c_package_number,
c_delivery_method,
c_ot_date,
c_email,
c_phone,
c_comment,
c_send
FROM ws_orders
WHERE c_ot IN ('2760650', '2760650')
ORDER BY c_int_id DESC
LIMIT 0, 30
So what I am doing wrong?
EDIT
So i did it ;)
The problem was with foreach
Instead of:
foreach ($data_array as $key => $param){
$key_number = $key +1;
$key_name = 'key'.$key_number;
$stmt->bindParam($key_name, $param, PDO::PARAM_INT);
var_dump($key_name); // var dump 2
var_dump($param); // var dump 3
}
I give:
for ($key_number = 0; $key_number < $quantity + 1; $key_number++) {
$key_name = 'key'.$key_number;
$stmt->bindParam($key_name, $data_array[$key_number], PDO::PARAM_INT);
}
And it works, but i still don't know what the problem was with earlier foreach...
There is the difference between PDO::bindParam() and PDO::bindValue(). PDO::bindParam binds reference, not value. When foreach process ends, $param will reference to the last array value. In the time of execute call all binded references will be evaluated to same value.
Official PDO::bindParam documentation says:
Unlike PDOStatement::bindValue(), the variable is bound as a reference
and will only be evaluated at the time that PDOStatement::execute() is
called.
If you want bind values in foreach use PDO::bindValue.
If you pass the variable as reference it would work fine for value, but won't work for key.
Example:
foreach ($data_array as $key => &$param) {
$key_number = $key + 1; //this won't work
$key_name = 'key' . $key_number;
$stmt->bindParam($key_name, $param, PDO::PARAM_INT);
var_dump($key_name); // var dump 2
var_dump($param); // var dump 3
}
The problem is that BindParam Passes the second value by reference. PHP reuses (or appears to in this case) the address of the $param, and not the actual value.
Your foreach could have used:
$stmt->bindParam($key, $data_array[$key]);
This has the effect of binding the address location of the array at that key location, so when your sql executes, it gets the right value.
You probably wanted:
$stmt->bindValue($key, $param);
which should evaluate in the foreach loop instead of at the execute statement, and is a passed value instead of an address location.
Related
I have a looped query to do inserts into the MySQL database it works perfectly to do what I need it to do as in it takes all the users inputs in array and then loops them and inputs each into their own row in the database.
$sql_insert_race_history = "INSERT INTO inf_race_history
(`inf_id`,`race_history`, `results`)
VALUES ";
if ($vracehistory != '') {
foreach ($vracehistory as $kay => $value) {
// $sql .= '' | $sql = $sql . '';
$sql_insert_race_history .= "('$inserted_id','{$value}','{$results[$kay]}'),";
}
} else {
$vracehistory = '';
}
// remove last `,` into query;
$sql_insert_race_history = rtrim($sql_insert_race_history, ',');
$countRow = count($_POST['racehist']);
//INSERT INTO THE DATABASE VIA QUERY
$results_racehistory = mysqli_query($vconn, $sql_insert_race_history);
This code works and inserts everything as i need it However i have been told that it is vulnerable to SQL injections attacks, so i have been trying to prevent that by using prepared statements every version I try only so far loops the dont work and it only uploads the very last item in the array
$stmtrace = $conn->prepare("INSERT INTO inf_race_history
(`inf_id`,`race_history`, `results`)
VALUES (?,?,?)");
if ($vracehistory != '') {
foreach ($vracehistory as $kay => $value) {
$stmtrace->bind_param("sss", $inserted_id,$value,$results[$kay]);
}
} else {
$vracehistory = '';
}
// remove last `,` into query;
$sql_insert_race_history = rtrim($stmtrace, ',');
$countRow = count($_POST['racehist']);
//INSERT INTO THE DATABASE VIA QUERY
$stmtrace->execute();
I think it may have something to do with changing it from .= in the foreach loop to just ->bind_param as maybe that is taking away the opportunity to loop it ? tho im not too sure and also how would i echo that i try to echo $stmtrace tho it says method _tostring is not implemented
foreach ($vracehistory as $kay => $value) {
$stmtrace->bind_param("sss", $inserted_id, $value, $results[$kay]);
$stmtrace->execute();
}
You should place execute() inside loop.
bind the params outside the foreach loop, and assign and execute the query when you assign the variables inside the foreach loop. For example
$stmtrace->bind_param("sss", $insertId, $insertValue, $insertKey);
foreach ($vracehistory as $kay => $value) {
$insertId = inserted_id;
$insertValue = $value;
$insertKey = $kay;
$stmtrace->execute();
}
Another note, if you bind an integer, the value of the bind_param method should be 'i'.
I have function in PHP, which should bind in MySQL IN statement so many variables, that is in array.
My problem is that variable and key is changing but function bind only last value n times.
I don't have idea where is the problem...
Here is my class method:
public function getOtListByOtNumbers($conditions){
$data_array = $conditions[SEARCH_OT];
# To find last key (remove coma)
$quantity = count($data_array);
$marks = '';
# Bind name string && rewriting value as integer
foreach ($data_array as $key => $value){
$i = $key+1;
if ($i == $quantity){
$marks .= ':key'.$i;
}
else {
$marks .= ':key'.$i.', ';
}
}
# Query
$sql="
SELECT
c_int_id,
c_ot,
c_tickets_amount,
c_basket_value,
c_person,
c_company,
c_city,
c_package_number,
c_delivery_method,
c_ot_date,
c_email,
c_phone,
c_comment,
c_send
FROM ws_orders
WHERE c_ot IN (".$marks.")
ORDER BY c_int_id DESC
LIMIT :first, :last ";
$stmt = $this->PDO->prepare($sql);
# Bind n values
// Here is a problem
var_dump($data_array); // var dump 1
foreach ($data_array as $key => $param){
$key_number = $key +1;
$key_name = 'key'.$key_number;
$stmt->bindParam($key_name, $param, PDO::PARAM_INT);
var_dump($key_name); // var dump 2
var_dump($param); // var dump 3
}
# Bind limit values
$stmt->bindParam('first', $conditions[OT_SEARCH_FIRST_ROW], PDO::PARAM_INT);
$stmt->bindParam('last', $conditions[OT_SEARCH_ROW_LIMIT], PDO::PARAM_INT);
# If executed return result
if ($stmt->execute() != FALSE) {
$stmt_result = $stmt->fetchAll();
$stmt->closeCursor();
var_dump($stmt_result); // var dump 4
# If not executed print debug and return FALSE
} else {
var_dump($stmt->errorInfo());
$this->debugQuery($stmt);
$stmt_result = FALSE;
}
return $stmt_result;
}
Here are var dupms:
var dump 1
array (size=2)
0 => string '2761531'
1 => string '2760650'
var dump 2 & 3
string 'key1' (length=4)
string '2761531'
string 'key2' (length=4)
string '2760650'
SQL query from this execution
SELECT
c_int_id,
c_ot,
c_tickets_amount,
c_basket_value,
c_person,
c_company,
c_city,
c_package_number,
c_delivery_method,
c_ot_date,
c_email,
c_phone,
c_comment,
c_send
FROM ws_orders
WHERE c_ot IN ('2760650', '2760650')
ORDER BY c_int_id DESC
LIMIT 0, 30
So what I am doing wrong?
EDIT
So i did it ;)
The problem was with foreach
Instead of:
foreach ($data_array as $key => $param){
$key_number = $key +1;
$key_name = 'key'.$key_number;
$stmt->bindParam($key_name, $param, PDO::PARAM_INT);
var_dump($key_name); // var dump 2
var_dump($param); // var dump 3
}
I give:
for ($key_number = 0; $key_number < $quantity + 1; $key_number++) {
$key_name = 'key'.$key_number;
$stmt->bindParam($key_name, $data_array[$key_number], PDO::PARAM_INT);
}
And it works, but i still don't know what the problem was with earlier foreach...
There is the difference between PDO::bindParam() and PDO::bindValue(). PDO::bindParam binds reference, not value. When foreach process ends, $param will reference to the last array value. In the time of execute call all binded references will be evaluated to same value.
Official PDO::bindParam documentation says:
Unlike PDOStatement::bindValue(), the variable is bound as a reference
and will only be evaluated at the time that PDOStatement::execute() is
called.
If you want bind values in foreach use PDO::bindValue.
If you pass the variable as reference it would work fine for value, but won't work for key.
Example:
foreach ($data_array as $key => &$param) {
$key_number = $key + 1; //this won't work
$key_name = 'key' . $key_number;
$stmt->bindParam($key_name, $param, PDO::PARAM_INT);
var_dump($key_name); // var dump 2
var_dump($param); // var dump 3
}
The problem is that BindParam Passes the second value by reference. PHP reuses (or appears to in this case) the address of the $param, and not the actual value.
Your foreach could have used:
$stmt->bindParam($key, $data_array[$key]);
This has the effect of binding the address location of the array at that key location, so when your sql executes, it gets the right value.
You probably wanted:
$stmt->bindValue($key, $param);
which should evaluate in the foreach loop instead of at the execute statement, and is a passed value instead of an address location.
I have an SQL $query like this;
UPDATE `tags` SET `tags.name` = :name,
`tags.slug` = :slug,
`tags.date_published` = :date_published,
`tags.meta_title` = :meta_title,
`tags.meta_description` = :meta_description,
`tags.meta_keywords` = :meta_keywords
WHERE `tags.id` = :id
And my $values array;
Array
(
[:name] => iphone
[:slug] => iphone
[:date_published] => 2016-03-27
[:meta_title] => iphone Yazıları
[:meta_description] => iphone hakkında son gelişmeler ve faydalı bilgiler.
[:meta_keywords] => iphone yazıları, iphone hakkında bilgiler, iphone haberleri
[:id] => 24
)
I'm trying to update one row with PDO's prepare function;
$sth = $this->db->prepare($query);
$sth->execute($values);
echo "affected rows: ".$sth->rowCount(); // prints 0
return $sth->rowCount() ? true : false;
And I get no errors but rows are not affected after execute the query. Where is my mistake?
mysql tags table
edit:
I'm creating values array like this;
$params = array(
"id", "name", "slug", "date_published",
"meta_title", "meta_description", "meta_keywords");
$values = array();
foreach ($params as $key) {
#$values[$key] = $_POST[$key];
}
And this is how I create the query;
$query = "UPDATE `$table` SET";
$values = array();
/* Add field names and placeholders to query. */
foreach ($params as $key => $value) {
$query .= " `{$table}.{$key}` = :" . $key . ",";
$values[":" . $key] = $value;
}
/* Remove last comma. */
$query = substr($query, 0, -1);
/* Build where condition. */
if ($cond) {
$query .= " WHERE ";
foreach ($cond as $key => $value) {
$query .= " `{$table}.{$key}` = :" . $key . ",";
$values[":" . $key] = $value;
}
/* Remove last comma. */
$query = substr($query, 0, -1);
}
You're escaping the column names incorrectly. For example, the back ticks should not enclose the whole of tags.name, but rather the table and column names separately:
`tags`.`name`
Checking the output of PDO::errorInfo() confirms this (emulated prepares must be set to false in order to see this, however).
General notes:
You don't need to specify the colon prefix for the keys of the array being bound (likewise when explicitly binding columns via bindParam() or bindValue()).
You should avoid using back ticks in your PDO queries. They're a MySQL-specific feature, and PDO is not a SQL abstraction layer (thereby preventing one of the major benefits of PDO - portability).
I'm trying to build a custom class to manage operations with database. I'm a beginner with OOP so if you have any suggests please tell me. By the way, i have an update method which took as parameter the name of the table, the fields to update, the values with which i want to update the fields and the fields and values to put in where clause of query.
At this time, i have two distinct arrays, one for the set part and one for the where part.
I build the query string like so PDOStatement Object ( [queryString] => UPDATE Ordini SET Nome=':Nome', Cognome=': Cognome', Telefono=': Telefono' WHERE ID=':ID' )
Now i would like to bind the params to the variables and execute the query and that's the problem. I try this way but the query don't update the fields.
- In $values i have the values i want to bind to variables in the SET part
- In $wv i have the values i want to bind to variables in the WHERE part
- In $fieldsQuery i have the placeholders for the SET part(":Nome" for example)
- In $fieldsWhere i have the placeholders for the WHERE part
How can i bind in the right way the placeholders with the variables?
public function update($table=NULL, $fieldsQuery=NULL, $fieldsValues = NULL, $whereFields=NULL, $whereValues=NULL, $whereOperators = NULL)
{
if($fieldsQuery != NULL)
$fields = explode(",", $fieldsQuery);
if($fieldsValues != NULL)
$values = explode(",", $fieldsValues);
if($whereFields != NULL)
$wf = explode(",", $whereFields);
if($whereValues != NULL)
$wv = explode(",", $whereValues);
$fieldsQuery = array_map(function($field) { return ":$field";}, $fields);
$bindValuesSet = array_combine($fieldsQuery, $values);
//return an array in which every field is => Fieldname=':Fieldname'
$bindSetInitial = array_combine($fields, $fieldsQuery);
//transform every item in array from field to :field
$fieldsWhere = array_map(function($field) { return ":$field";}, $wf);
$bindValuesWhere = array_combine($fieldsWhere, $wv);
$bindWhereInitial = array_combine($wf, $fieldsWhere);
//implode an array mantaining both key and value
$fieldsValues = implode(', ', array_map(function ($v, $k) { return sprintf("%s='%s'", $k, $v); }, $bindSetInitial, array_keys($bindSetInitial)));
$fieldsWhere = implode(', ', array_map(function ($v, $k) { return sprintf("%s='%s'", $k, $v); }, $bindWhereInitial, array_keys($bindWhereInitial)));
$query = $this->db->prepare('UPDATE ' . $table . ' SET ' . $fieldsValues . ' WHERE ' . $fieldsWhere);
$query->bindParam(':Nome', $values[0]);
$query->bindParam(':Cognome', $values[1]);
$query->bindParam(':Telefono', $values[2]);
$query->bindParam(':ID', $wv[0], PDO::PARAM_INT);
$query->execute();
print_r($query->debugDumpParams());
}
':Nome' is not a placeholder for a prepared statement. It's just a string ':Nome'
Placeholder is :Nome (without `) and without any spaces, tabs etc.
I.e. : Nome is not a placeholder too.
So, you query should be:
UPDATE Ordini SET Nome=:Nome, Cognome=:Cognome, Telefono=:Telefono WHERE ID=:ID
And thanks to #Fred-ii- - read error handling section of PDO
UPDATE:
I get array values from $_POST['changed'].
The array structure looks like this:
Array
(
[0] => Array
(
[recid] => 1
[nachname] => Müller7777
)
[1] => Array
(
[recid] => 3
[vorname] => Maria123
)
)
I get on line #3 this error: Fatal error: Function name must be a string
$uarr=array();
foreach ($_POST['changed'] as $a) {
list($x,$k)=array_keys($a);
list($y,$v)=array_values($a);
$uarr[$y][]="$k='$v'";
}
foreach ($uarr as $k=>$v) {
$sql = "";
$sql .="UPDATE tbl SET ".join(",",$v)." WHERE recid=$k";
// send UPDATE ...
}
file_put_contents('filename2.txt', $sql);
Before I do the final database UPDATE I want to check if the created array does its job. Thats why I want to write the $sql variable first into a txt-file.
------------------------------------------------
SOLUTION:
checking if $_POST['changed'] == null is the final answer for this question.
if ($_POST['changed'] == null) {
} else {
$uarr=array();
$b = $_POST['changed'];
foreach ($b as $a) {
list($x,$k)=array_keys($a);
list($y,$v)=array_values($a);
// $x contains the `recid` key
// $y ... value
$uarr[$y][]="$k='$v'";
}
foreach ($uarr as $k=>$v) {
$sql = "";
$sql .="UPDATE tbl SET ".join(",",$v)." WHERE recid=$k";
// send UPDATE ...
}
file_put_contents('filename2.txt', $sql);
}
Before you run the individual UPDATE statements - yes, for each recid value you should send one statement - you could first collect all the affected values for each recid in an associative array like
$uarr=array();
foreach ($_POST['changed'] as $a) {
list($x,$k)=array_keys($a);
list($y,$v)=array_values($a);
// $x contains the `recid` key
// $y ... value
$uarr[$y][]="$k='$v'";
}
and then do another loop like
foreach ($uarr as $k=>$v) {
$sql="UPDATE tbl SET ".join(",",$v)." WHERE recid=$k";
// send UPDATE ...
}
But, of course, this will only work correctly if the $_POST('changed') array adheres to the described format and order. And, finally, so far there is nothing in this code to protect you from SQL injection.
Try to do it like this:
$BigArray = $_POST['changed'];
$LengthOfArray = sizeof($BigArray);
for ($i = 0; $i < $LengthOfArray ; $i++) {
$SubArray = $BigArray[$i];
// Call the update/insert here
// $SubArray['recid'] is the ID
// $SubArray['vorname'] is the name
}