mysqli_real_escape_string foreach function db_array_update - php

Someone wrote a PHP program many times ago for me, and now I got this error when I run the code :
mysqli_real_escape_string() expects parameter 2 to be string, array
given in....
I cannot fix this here is the code :
function db_array_update($table, $a, $where)
{
$q = "update $table set ";
$b = NULL;
foreach($a as $key => $value)
{
if (is_int($key))
continue;
$con = mysqli_connect("localhost", MYSQLUSER , MYSQLPASS, MYSQLDB);
$b[] = "$key='".mysqli_real_escape_string($con, $value)."'";
}
$q .= implode(",", $b);
$q .= " where ".$where;
db_query($q);
}
and I use it like this :
db_array_update("all_data",array('last_fetched' =>date("Y/m/d H:i:s"),'name'=>$name, 'creation'=>$creat, 'expiration' =>$expire,"id=".$res['id']);
Can someone help me how should I fix this ?
Tried many things but not work...

What's being passed into your function as $a must have one or more array values that are arrays themselves. You will get this error if $value is an array, ie. you have a multi-dimensional array instead of simple key/string pairs.
Do var_dump($a) inside your function to see which one of your array values is an array. Also, you have some goofs in the data you pass in:
db_array_update("all_data",array(
'last_fetched' => date("Y/m/d H:i:s"),
'name'=>$name, // May be an array?
'creation'=>$creat, // May be an array?
'expiration' =>$expire, // May be an array?
), // Need this closing ) to end the array.
"id=".$res['id'] // This one should be outside the array!
);
Also you need to close the array before the 'id=' bit that you're passing in for the $where condition, you have unclosed parentheses there.
...Really though, your code is full of funnies outside this issue. Study the answers here. If this is a representative specimen of the code, someone should rewrite your database functionality.

The use of the mysqli is done wrong. The whole benefit of mysqli has been reduced to about zero. See http://php.net/manual/en/book.mysqli.php for examples.
To correct a part of the code:
Connection was opened many times: Really inefficient.
escape is not required when adding data into the database the correct way: No use of variable binding=> Very weak protection against SQL injection.
The code after these possible corrections:
function db_array_update($table, $a, $where)
{
$q = "update $table set";
$b = NULL;
$c=null;
$con = mysqli_connect("localhost", MYSQLUSER , MYSQLPASS, MYSQLDB);
foreach($a as $key => $value)
{
if (is_int($key))
continue;
$b[]="$key=?";
}
$q .= implode(",", $b);
$q .= " where ".$where;
$stmt = $mysqli->prepare($q);
foreach($a as $key => $value)
{
if (is_int($key))
continue;
$stmt->bind_param($key, $value);
}
$stmt->execute();
$stmt->close();
}
This is still not compliant:
$where is an unknown string, should be with parameter binding.
$table is assumed an internal value, but should just be a direct table name to prevent any abuse there.

Related

How can I pass many columns to bind_result() with more flexibility?

I have some long MySQL table whose design is not totally fixed yet. So, occasionally, I need to add/delete some columns. But, every time I alter the table, I must re-write all the line dealing with bind_result(). I am looking for a solution which makes this change easy.
Assume I currently have a table with columns like col_a, col_b, col_c, ..., col_z. So, I use the bind_result() to store result values as the manual says.
$res = $stmt->bind_result($a, $b, $c,..., $z);
But, if I change the table design, I must change parameters of all the lines dealing with this bind_result() to match the new MySQL table.
Is there any technique like following?
// Some php file defining constants
define("_SQL_ALL_COLUMNS", "\$a, \$b, \$c, \$d, ... \$z");
// Some SQL process in in other php files
stmt->execute();
$res = $stmt->bind_result(_SQL_ALL_COLUMNS);
So, I don't need to worry about a change of the number of the parameters in other files as long as I once define them correctly somewhere. Of course, I already found that my attempt in the previous example was not a right way.
Is there any good solution for this type of situation?
Use call_user_func_array() to dynamically set the number of parameters:
function execSQL($con, $sql, $params = null)
$statement = $con->prepare($sql);
if (!$statement){
// throw error
die("SQL ERROR: $sql\n $con->error");
}
$type = "";
$arg = array();
if ($params && is_array($params)){
foreach($params as $param){
if (is_numeric($param)){
$type .= 'd';
continue;
}
$type .= 's';
}
$arg[] = $type;
foreach($params as $param){
$arg[] = $param;
}
call_user_func_array(array($statement,'bind_param'), refValues($arg)); // php 7
}
$res = $statement->execute();
if (!$res){
die("Looks like the Execute Query failed.\n\nError:\n{$statement->error}\n\nQuery:\n{$sql}\n\nParams:\n{".implode(",", $arg)."}");
}
return $con->insert_id;
}
function refValues($arr){
if (strnatcmp(phpversion(),'5.3') >= 0) { //Reference is required for PHP 5.3+
$refs = array();
foreach($arr as $key => $value){
$refs[$key] = &$arr[$key];
}
return $refs;
}
return $arr;
}
You can use it by calling the function execSQL with an array of parameters:
$result = execSQL($connection,$sql,["a","b","c","..."]);
What this does is check the data type of the parameters and appends to the $type variable, which will then be passed to the bind method as first parameter.

associative array to string conversion issue

First off when answering please try and explain simply as possible as I am fairly new to php. Anyway my problem is is that I do not understand why my associative array to string conversion is not working. I am basically using the same model as described here: PHP 5 arrays Scroll down to see example for associative arrays. Anyway the output I always get is this when I submit "Adam" into the textbox:
Notice: Undefined index:
queryStr in C:\xampp\htdocs\practice\src\fetchigndatausingpdo.php on line 24
PID = 80 = 8 AND FirstName = adam AND 1 = adam AND LastName = preston AND 2 = preston AND Age = 17 AND 3 = 17 AND
Below is the code if you have any suggestions please notify me, thankyou :). Also $user and $pass have been deliberately blanked for security reasons.
<form action="fetchigndatausingpdo.php" method="post">
<input type="text" name="name">
<input type="submit" name="submit" value="submit">
</form>
<?php
$user = "adam";
$pass = "**********";
if(isset($_POST['name'])){
try{
$dbh = new PDO('mysql:host=localhost;dbname=my_db', $user, $pass, array(PDO::ATTR_PERSISTENT=>true));
$stmt = $dbh->prepare("SELECT * FROM persons WHERE FirstName LIKE ?");
$stmt->execute(array($_POST['name']));
if($stmt->rowCount() > 0){
$result = $stmt->fetchAll();
$terms = count($result);
foreach($result as $person){
foreach ($person AS $field => $value){
$terms--;
$GLOBALS['queryStr'].= $field.' = '.$value;
if($terms){
$GLOBALS['queryStr'].=' AND ';
}
}
}
echo $queryStr;
}
}catch(PDOException $e){
echo $e->getMessage();
}
}
?>
If you will be only receiving exactly one row from your query, you can change your $result = $stmt->fetchAll(); row to use the fetch() method of your database handler instead. That will give you exactly one array for your $result variable. That way, you'll like not have to make any further changes.
Change is required, because fetchAll() will put arrays into your $result variable (even if there's only one result). If you don't change the fetchAll() to fetch(), then you could try changing your foreach($result as $field => $value){ and below part to something like this:
..
foreach($result as $person){
foreach ($person AS $field => $value){
$terms--;
$GLOBALS['queryStr'].= $field.' = '.$value;
if($terms){
$GLOBALS['queryStr'].=' AND ';
}
echo $queryStr;
}
}
..
Please elaborate if this is the desired effect.
Some additional comments on the code:
You are already using the PDO library and its prepare method for efficient and safe use of queries. As you are using parameterbinding, it is not necessary to do manual sanitization of the received data, as PDO will handle that for you. Talking about this line here:
$name = mysql_real_escape_string($_POST['name']);
and this one:
$stmt->execute(array($name))
You could change the execute part to
$stmt->execute(array($_POST['name']))
instead. Also, regular way of checking if any results were returned when using PDO would be the rowCount() method of the statement you're executing. PDO would return true even if there weren't any matches in your query resultset. The above all combined, you would have that part of your code look like this:
..
if(isset($_POST['name'])){
try{
$dbh = new PDO('mysql:host=localhost;dbname=my_db', $user, $pass, array(PDO::ATTR_PERSISTENT=>true));
$stmt = $dbh->prepare("SELECT * FROM persons WHERE FirstName LIKE ?");
$stmt->execute(array($_POST['name']));
$resultsSize = $stmt->rowCount();
if($resultsSize > 0){
$queryStr = '';
$result = $stmt->fetchAll();
foreach($result as $person){
$terms = sizeof(array_keys($person));
foreach ($person AS $field => $value){
$terms--;
$queryStr .= $field.' = '.$value;
if($terms){
$queryStr .=' AND ';
}
$queryStr .= '<br/>'; // easier HTML visual evaluation
}
}
echo $queryStr;
}
}catch(PDOException $e){
echo $e->getMessage();
}
}
With the above refactoring (you are first checking if the $_POST['name'] is set) you are avoiding the extra resources allocated to the database connection (even if you are not using it). Putting your connection initialization along with your other executed code is not a problem in this case, you're good with one catch block handling your errors (as in this case they would be of similar nature, either problematic connection to the database.
Changes made to the query: when comparing string values, the equals sign (=) would result in a byte-by-byte comparison for exact value, while the LIKE operator would give the advantage of not stumbling upon any encoding fancyness - and using LIKE is the normally accepted way of comparing string values (also, with LIKE you could make use of wildcards (% character, etc.)).
**Edit:
I've updated the above code based on your comment - I've also moved the output of the string after the outer foreach loop (so it will print when all of the returned rows have been processed). I've changed your variable from the $GLOBALS superglobal variable to a local one (current context doesn't imply you would need access to it outside this file or page generation, if so, look into the possibility of using the $_SESSION variable).

create PHP function for PDO insert query

I have tried to create a function for an SQL/PDO Insert query:
function InsertQuery ($table,$cols,$values) {
global $pdo_conn;
foreach($values as $values2) {
$values2 = $values2;
}
$stmt='INSERT into $table (';
foreach($cols as $cols2) {
$stmt.=" ".$cols2.", ";
}
$stmt.=" ) VALUES ( ";
foreach($cols as $cols2) {
$stmt.=" :".$cols2." ";
}
$stmt.=" ) ";
$stmt2 = $pdo_conn->prepare($stmt);
foreach($cols as $cols2) {
$stmt2->bindParam(':$cols2', $cols2);
}
}
but i am getting the error:
Catchable fatal error: Object of class PDOStatement could not be converted to string in /home/integra/public_html/admin/includes/functions.php on line 30
please be patient with me as i am new to PDO and just used to using MySQL
have i put the prepared statement wrong or my foreach loops?
I believe the statement should look like:
$stmt2 = $pdo_conn->prepare('INSERT into $table (col1) values (:val1)');
$stmt2->bindParam(':$val1', $val);
here is how i called my function:
$col=array('col1');
$val=array('val1');
InsertQuery ("table1",$col,$val);
UPDATE:
Ok here is my new code:
global $pdo_conn;
foreach($values as $values2) {
$values2 = $values2;
}
$stmt='INSERT into '.$table.' (';
foreach($cols as $cols2) {
$stmt.=" ".implode(",", $cols2)." ";
}
$stmt.=" ) VALUES ( ";
foreach($cols as $cols2) {
$stmt.=" :".implode(",", $cols2)." ";
}
$stmt.=" ) ";
$stmt2 = $pdo_conn->prepare($stmt);
foreach($cols as $cols2) {
$stmt2->bindParam(':$cols2', $cols2);
}
but i now get the error about the implode:
Warning: implode() [function.implode]: Invalid arguments passed in /home/integra/public_html/admin/includes/functions.php on line 18
Warning: implode() [function.implode]: Invalid arguments passed in /home/integra/public_html/admin/includes/functions.php on line 22
which i think is because there is nothing to implode as there is only one column and one value
Use type hints to ensure the function arguments are arrays:
function InsertQuery ($table, array $cols, array $values) {
Make sure your PDO connection is accessible. If it's global, you have to declare it (credit to #u_mulder):
global $pdo_conn;
The following does nothing, get rid of it:
foreach($values as $values2) {
$values2 = $values2;
}
Use builtin array functions instead of foreach'ing everything:
$col_list = implode(",", $cols);
$param_list = implode(",", array_fill(1,count($cols), "?"));
Variables don't expand inside single-quotes. You need to use double-quotes (credit to #MichaelBerkowski).
Also, use $stmt for a PDOStatement object, and not for the SQL string. That's confusing.
$sql="INSERT into $table ($col_list) VALUES ($param_list)";
$stmt = $pdo_conn->prepare($sql);
You don't need to write a foreach loop to bindParam() in PDO. You can just pass an array of values to execute(). And you already have the values in an array, so it's really easy:
$stmt->execute($values);
}
For extra safety, make sure to delimit the columns, in case someone uses special characters or a SQL keyword in a column name:
$col_list = implode(",", array_map(function ($c) { return "`$c`" }, $cols));
And make sure the values is in a simple array, not an associative array:
$stmt->execute(array_values($values));
Re your comment:
would you be able to show me how to do the same with select, I'm not sure how it would work as if i have a where clause what would i do with it in the function?
One could for example design a function with an argument $where that is an associative array, whose keys are column names, and whose values are the values you're searching for.
Assume the resulting WHERE clause includes these column/value pairs as AND terms, and all the comparisons are equality.
function SelectQuery($table, array $where) {
global $pdo_conn;
$sql = "SELECT * FROM `$table` ";
$values = null;
if ($where) {
$sql .= "WHERE " . implode(" AND ",
array_map(function ($c) { return "`$c` = ?"; } array_keys($where)));
$values = array_values($where);
}
$stmt = $pdo_con->prepare($sql);
$stmt->execute($values);
}
Of course this supports only a small subset of the possible expressions you can have in a SELECT, but I'm just demonstrating a technique here.
If you want a more fully-feature query builder for PHP, take a look at Zend_Db_Sql or Doctrine QueryBuilder or Laravel query builder.
if anything changes on my server and the PDO stops working i can revert back to MySQL while i fix it.
PDO has been stable since 2005 and it will not stop working, unless you change your PHP environment and disable the extension or the mysql driver or something.
Whereas the ext/mysql extension will stop working. It is currently deprecated and PHP has announced they will remove it in a future version of PHP.
There are a few things going on:
First, $cols and $vals should be arrays, but they are strings.
Quick fix is to make them arrays:
$col = array('col1');
$val = array('val1');
InsertQuery("table1", $col, $val);
Second, $pdo_conn is unknown in the function scope. Make it a parameter or a global.
you can also use this for create query from array:
$myArray = array(
'col1' => 'val1',
'col2' => 'val2'
);
$query = "INSERT INTO table (" . implode(", ", array_keys($myArray)) . ") VALUES (" . implode(", ", $myArray) . ")";
maybe usefull for you
http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers

PDO: bindParam empty string

Theres's a similar question here, but actually that doesn't give me the answer:
PHP + PDO: Bind null if param is empty
I need my statement work in a loop, with only changing the binded variables.
Like:
$this->array = array(
"cell1" => "",
"cell2" => "",
);
$this->sth = $db->prepare("INSERT INTO `table`
(`coloumn1`, `coloumn2`)
VALUES (:coloumn1, :coloumn2)");
$this->sth->bindParam(:coloumn1, $this->array['cell1'], PDO::PARAM_STR);
$this->sth->bindParam(:coloumn2, $this->array['cell2'], PDO::PARAM_STR);
//Data proccessing...
foreach($data as $value){
$this->array['cell1'] = $value['cell1'];
$this->array['cell2'] = $value['cell2'];
try {
this->sth->execute();
print_r($this->sth->errorInfo());
}
catch(PDOException $e){
echo 'sh*t!';
}
}
Everything works well until either of the values is an empty string.
My problem is when 'cell1' is an empty string, the bound parameter is a nullreference, and it won't work. But I need the referenced binding because of the loop, so bindValue isn't a solution.
And I need the loop very bad, because of the huge data I want to process.
Any suggestion?
I tried right before execute:
foreach($this->array as $value){
if(!$value) {
$value = "";
}
}
It doesn't work.
The only way that solved my problem is modifying to this:
$this->array['cell1'] = !empty($value['cell1']) ? $value['cell1'] : "";
$this->array['cell2'] = !empty($value['cell2']) ? $value['cell2'] : "";
But this seems too rubbishy...
I know its necroposting, but maybe will help to someone.
You trying to check if the variable false, but not null. And not reapplying values back to array.
foreach($this->array as $value){
if(!$value) {
$value = "";
}
}
Try to check for null in loop
foreach($this->array as $index => $value)
{
$this->array[$index] = !empty($value) ? $value : '';
}
Your question has nothing to do with PDO but with basic PHP.
When there is no variable available - you can't use it at all. So, you have to create it somehow. The way you are using at the moment is not "rubbishy" but quite acceptable. I'd rather call whole code "rubbishy" as it's twice as big as as it should be.
But I need the referenced binding because of the loop, so bindValue isn't a solution.
This assumption is wrong too. Why do you think you can't use bind by value?
$sql = "INSERT INTO `table` (`coloumn1`, `coloumn2`) VALUES (?, ?)";
$sth = $db->prepare($sql);
foreach($data as $value)
{
$value['cell1'] = !empty($value['cell1']) ? $value['cell1'] : "";
$value['cell2'] = !empty($value['cell2']) ? $value['cell2'] : "";
$sth->execute($value);
}
as simple as this
And I need the loop very bad, because of the huge data I want to process.
I don't think it's really huge, as it fits for the PHP process memory. However, consider to use LOAD DATA INFILE query for the real huge amounts.

PDO PHP insert into DB from an associative array

I have an array like this
$a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
When I do a var-dump I get this ->
{ ["phone"]=> int(111111111) ["image"]=> string(19) "sadasdasd43eadasdad" }
Now I am trying to add this to the DB using the IN statement -
$q = $DBH->prepare("INSERT INTO user :column_string VALUES :value_string");
$q->bindParam(':column_string',implode(',',array_keys($a)));
$q->bindParam(':value_string',implode(',',array_values($a)));
$q->execute();
The problem I am having is that implode return a string. But the 'phone' column is an integer in the database and also the array is storing it as an integer. Hence I am getting the SQL error as my final query look like this --
INSERT INTO user 'phone,image' values '111111111,sadasdasd43eadasdad';
Which is a wrong query. Is there any way around it.
My column names are dynamic based what the user wants to insert. So I cannot use the placeholders like :phone and :image as I may not always get a values for those two columns. Please let me know if there is a way around this. otherwise I will have to define multiple functions each type of update.
Thanks.
Last time I checked, it was not possible to prepare a statement where the affected columns were unknown at preparation time - but that thing seems to work - maybe your database system is more forgiving than those I am using (mainly postgres)
What is clearly wrong is the implode() statement, as each variable should be handled by it self, you also need parenthesis around the field list in the insert statement.
To insert user defined fields, I think you have to do something like this (at least that how I do it);
$fields=array_keys($a); // here you have to trust your field names!
$values=array_values($a);
$fieldlist=implode(',',$fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="insert into user($fieldlist) values(${qs}?)";
$q=$DBH->prepare($sql);
$q->execute($values);
If you cannot trust the field names in $a, you have to do something like
foreach($a as $f=>$v){
if(validfield($f)){
$fields[]=$f;
$values[]=$v;
}
}
Where validfields is a function that you write that tests each fieldname and checks if it is valid (quick and dirty by making an associative array $valfields=array('name'=>1,'email'=>1, 'phone'=>1 ... and then checking for the value of $valfields[$f], or (as I would prefer) by fetching the field names from the server)
SQL query parameters can be used only where you would otherwise put a literal value.
So if you could see yourself putting a quoted string literal, date literal, or numeric literal in that position in the query, you can use a parameter.
You can't use a parameter for a column name, a table name, a lists of values, an SQL keyword, or any other expressions or syntax.
For those cases, you still have to interpolate content into the SQL string, so you have some risk of SQL injection. The way to protect against that is with whitelisting the column names, and rejecting any input that doesn't match the whitelist.
Because all other answers allow SQL injection. For user input you need to filter for allowed field names:
// change this
$fields = array('email', 'name', 'whatever');
$fieldlist = implode(',', $fields);
$values = array_values(array_intersect_key($_POST, array_flip($fields)));
$qs = str_repeat("?,",count($fields)-1) . '?';
$q = $db->prepare("INSERT INTO events ($fieldlist) values($qs)");
$q->execute($values);
I appreciated MortenSickel's answer, but I wanted to use named parameters to be on the safe side:
$keys = array_keys($a);
$sql = "INSERT INTO user (".implode(", ",$keys).") \n";
$sql .= "VALUES ( :".implode(", :",$keys).")";
$q = $this->dbConnection->prepare($sql);
return $q->execute($a);
You actually can have the :phone and :image fields bound with null values in advance. The structure of the table is fixed anyway and you probably should got that way.
But the answer to your question might look like this:
$keys = ':' . implode(', :', array_keys($array));
$values = str_repeat('?, ', count($array)-1) . '?';
$i = 1;
$q = $DBH->prepare("INSERT INTO user ($keys) VALUES ($values)");
foreach($array as $value)
$q->bindParam($i++, $value, PDO::PARAM_STR, mb_strlen($value));
I know this question has be answered a long time ago, but I found it today and have a little contribution in addition to the answer of #MortenSickel.
The class below will allow you to insert or update an associative array to your database table. For more information about MySQL PDO please visit: http://php.net/manual/en/book.pdo.php
<?php
class dbConnection
{
protected $dbConnection;
function __construct($dbSettings) {
$this->openDatabase($dbSettings);
}
function openDatabase($dbSettings) {
$dsn = 'mysql:host='.$dbSettings['host'].';dbname='.$dbSettings['name'];
$this->dbConnection = new PDO($dsn, $dbSettings['username'], $dbSettings['password']);
$this->dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
function insertArray($table, $array) {
$fields=array_keys($array);
$values=array_values($array);
$fieldlist=implode(',', $fields);
$qs=str_repeat("?,",count($fields)-1);
$sql="INSERT INTO `".$table."` (".$fieldlist.") VALUES (${qs}?)";
$q = $this->dbConnection->prepare($sql);
return $q->execute($values);
}
function updateArray($table, $id, $array) {
$fields=array_keys($array);
$values=array_values($array);
$fieldlist=implode(',', $fields);
$qs=str_repeat("?,",count($fields)-1);
$firstfield = true;
$sql = "UPDATE `".$table."` SET";
for ($i = 0; $i < count($fields); $i++) {
if(!$firstfield) {
$sql .= ", ";
}
$sql .= " ".$fields[$i]."=?";
$firstfield = false;
}
$sql .= " WHERE `id` =?";
$sth = $this->dbConnection->prepare($sql);
$values[] = $id;
return $sth->execute($values);
}
}
?>
dbConnection class usage:
<?php
$dbSettings['host'] = 'localhost';
$dbSettings['name'] = 'databasename';
$dbSettings['username'] = 'username';
$dbSettings['password'] = 'password';
$dbh = new dbConnection( $dbSettings );
$a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
$dbh->insertArray('user', $a);
// This will asume your table has a 'id' column, id: 1 will be updated in the example below:
$dbh->updateArray('user', 1, $a);
?>
public function insert($data = [] , $table = ''){
$keys = array_keys($data);
$fields = implode(',',$keys);
$pre_fields = ':'.implode(', :',$keys);
$query = parent::prepare("INSERT INTO $table($fields) VALUES($pre_fields) ");
return $query->execute($data);
}

Categories