PDO bindParam issue [duplicate] - php

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can PHP PDO Statements accept the table name as parameter?
I have a function in my class which is doing some trouble. Here the function
function insert($table,$column = array(),$value = array())
{
$array1 = implode(",", $column);
$array2 = implode(",", $value);
try
{
$sql = $this->connect->prepare("INSERT INTO :table (:date1) VALUES (:date2)");
$sql->bindParam(':table',$table, PDO::PARAM_STR);
$sql->bindParam(':data1',$array1, PDO::PARAM_STR);
$sql->bindParam(':data2',$array2, PDO::PARAM_STR);
$sql->execute();
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
I call the function with:
-> insert('coupons',array('categorie','name','link','code','id'),array('test11','test','test','test','NULL'));
The error I get is :
Warning: PDOStatement::execute() [pdostatement.execute]: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined in C:\xampp\htdocs\MYFRAMEWORK\lib\database.class.php on line 46
Line 46 is :
$sql->execute();
So now I don't really see where the issue is. Any pointers?

PDOs bind value data, not table and column names.
You are misunderstanding the use of bindings. You cannot bind table and column names with PDO. You bind data to insert INTO those columns. You need to construct the SQL to include the table names and columns using string operations.
Format the data
I've renamed your $column and $value to $column_array, $value_array to make it clear what they are, and assumed that each is a simple array: $column_array = array('column1', 'column2', ...) etc.
$placeholders = array_map(function($col) { return ":$col"; }, $column_array);
$bindvalues = array_combine($placeholders , $value_array);
$placeholders now looks like this:
$placeholders = array(
':column1',
':column2',
...
);
$bindvalues now looks like this:
$bindvalues = array(
':column1'=>'value1',
':column2'=>'value2',
...
);
Build, prepare, execute
$sql = $this->connect->prepare("INSERT INTO $table (" .implode(",", $column_array) .") VALUES (". implode(",", $placeholders) . ")";
This will give you a prepared statement of the form:
$sql = INSERT INTO table_name (column1, column2, ...) VALUES (:column1, :column2, ...)
You can then execute the prepared statement and pass the $values as an argument.
$sql->execute($bindValues);
Note:
One caveat that must be mentioned. Make sure that your original data has been sanitized against SQL Injection. PDO's take care of that for the bound values, but if you are constructing the columns from, say, $_POST data this is vulnerable and needs to be sanitized.

The query is not constructed properly, the values are missing surrounding ' quotes.
When you do an implode, the array2 looks like,
test1,test,test... //and so on.
It needs to be 'test1','test','test'... in-order to be properly binded inside the Insert query.
$sql = $this->connect->prepare("INSERT INTO :table (:date1) VALUES (:date2)");
Also, you have typos in the parameter names, date1 instead of data1 and date2 instead of data2.

Related

Multiple records insert with one Query [duplicate]

I'm looking for a SQL-injection-secure technique to insert a lot of rows (ca. 2000) at once with PHP and MySQLi.
I have an array with all the values that have to be include.
Currently I'm doing that:
<?php
$array = array("array", "with", "about", "2000", "values");
foreach ($array as $one)
{
$query = "INSERT INTO table (link) VALUES ( ?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);
$stmt->execute();
$stmt->close();
}
?>
I tried call_user_func_array(), but it caused a stack overflow.
What is a faster method to do this (like inserting them all at once?), but still secure against SQL injections (like a prepared statement) and stack overflows?
You should be able to greatly increase the speed by putting your inserts inside a transaction. You can also move your prepare and bind statements outside of your loop.
$array = array("array", "with", "about", "2000", "values");
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);
$mysqli->query("START TRANSACTION");
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
$mysqli->query("COMMIT");
I tested this code with 10,000 iterations on my web server.
Without transaction: 226 seconds.
With transaction: 2 seconds.
Or a two order of magnitude speed increase, at least for that test.
Trying this again, I don't see why your original code won't work with minor modifications:
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $one);
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
Yes, you can build a single big query manually, with something like:
$query = "";
foreach ($array as $curvalue) {
if ($query)
$query .= ",";
$query .= "('" . $mysqli->real_escape_string($curvalue) . "')";
}
if ($query) {
$query = "INSERT INTO table (link) VALUES " . $query;
$mysqli->query($query);
}
You should first convert your array into a string. Given that it is an array of strings (not a two-dimentional array), you can use the implode function.
Please be aware that each value should be enclosed into parenthesis and properly escaped to ensure a correct INSERT statement and to avoid the risk of an SQL injection. For proper escaping you can use the quote method of the PDOConnection -- assuming you're connecting to MySQL through PDO. To perform this operation on every entry of your array, you can use array_map.
After escaping each value and imploding them into a single string, you need to put them into the INSERT statement. This can be done with sprintf.
Example:
<?php
$connection = new PDO(/*...*/);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dataToBeSaved = [
'some',
'data',
'with "quotes"',
'and statements\'); DROP DATABASE facebook_main; --'
];
$connection->query(
sprintf(
'INSERT INTO table (link) VALUES %s',
implode(',',
// for each entry of the array
array_map(function($entry) use ($connection) {
// escape it and wrap it in parenthesis
return sprintf('(%s)', $connection->quote($entry));
}, $dataToBeSaved)
)
)
);
Note: depending on the amount of records you're willing to insert into the database, you may want to split them into several INSERT statements.

How Insert multi rows in database using mysqli query [duplicate]

I'm looking for a SQL-injection-secure technique to insert a lot of rows (ca. 2000) at once with PHP and MySQLi.
I have an array with all the values that have to be include.
Currently I'm doing that:
<?php
$array = array("array", "with", "about", "2000", "values");
foreach ($array as $one)
{
$query = "INSERT INTO table (link) VALUES ( ?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);
$stmt->execute();
$stmt->close();
}
?>
I tried call_user_func_array(), but it caused a stack overflow.
What is a faster method to do this (like inserting them all at once?), but still secure against SQL injections (like a prepared statement) and stack overflows?
You should be able to greatly increase the speed by putting your inserts inside a transaction. You can also move your prepare and bind statements outside of your loop.
$array = array("array", "with", "about", "2000", "values");
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);
$mysqli->query("START TRANSACTION");
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
$mysqli->query("COMMIT");
I tested this code with 10,000 iterations on my web server.
Without transaction: 226 seconds.
With transaction: 2 seconds.
Or a two order of magnitude speed increase, at least for that test.
Trying this again, I don't see why your original code won't work with minor modifications:
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $one);
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
Yes, you can build a single big query manually, with something like:
$query = "";
foreach ($array as $curvalue) {
if ($query)
$query .= ",";
$query .= "('" . $mysqli->real_escape_string($curvalue) . "')";
}
if ($query) {
$query = "INSERT INTO table (link) VALUES " . $query;
$mysqli->query($query);
}
You should first convert your array into a string. Given that it is an array of strings (not a two-dimentional array), you can use the implode function.
Please be aware that each value should be enclosed into parenthesis and properly escaped to ensure a correct INSERT statement and to avoid the risk of an SQL injection. For proper escaping you can use the quote method of the PDOConnection -- assuming you're connecting to MySQL through PDO. To perform this operation on every entry of your array, you can use array_map.
After escaping each value and imploding them into a single string, you need to put them into the INSERT statement. This can be done with sprintf.
Example:
<?php
$connection = new PDO(/*...*/);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dataToBeSaved = [
'some',
'data',
'with "quotes"',
'and statements\'); DROP DATABASE facebook_main; --'
];
$connection->query(
sprintf(
'INSERT INTO table (link) VALUES %s',
implode(',',
// for each entry of the array
array_map(function($entry) use ($connection) {
// escape it and wrap it in parenthesis
return sprintf('(%s)', $connection->quote($entry));
}, $dataToBeSaved)
)
)
);
Note: depending on the amount of records you're willing to insert into the database, you may want to split them into several INSERT statements.

in my mysql table having rows in one single cell i have array. How can i select rows which are having a perticular array value? [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);

Prepared statement WHERE IN clause behaving unexpected [duplicate]

This question already has answers here:
Can I bind an array to an IN() condition in a PDO query?
(23 answers)
Closed 1 year ago.
I'm reworking some PHP code to use PDO for the database access, but I'm running into a problem with a "WHERE... IN" query.
I'm trying to delete some things from a database, based on which items on a form are checked. The length and content of the list will vary, but for this example, imagine that it's this:
$idlist = '260,201,221,216,217,169,210,212,213';
Then the query looks like this:
$query = "DELETE from `foo` WHERE `id` IN (:idlist)";
$st = $db->prepare($query);
$st->execute(array(':idlist' => $idlist));
When I do this, only the first ID is deleted. (I assume it throws out the comma and everything after it.)
I've also tried making $idlist an array, but then it doesn't delete anything.
What's the proper way to use a list of items in a PDO prepared statement?
Since you can't mix Values (the Numbers) with control flow logic (the commas) with prepared statements you need one placeholder per Value.
$idlist = array('260','201','221','216','217','169','210','212','213');
$questionmarks = str_repeat("?,", count($idlist)-1) . "?";
$stmt = $dbh->prepare("DELETE FROM `foo` WHERE `id` IN ($questionmarks)");
and loop to bind the parameters.
This may be helpful too:
https://phpdelusions.net/pdo#in
$arr = [1,2,3];
$in = str_repeat('?,', count($arr) - 1) . '?';
$sql = "SELECT * FROM table WHERE column IN ($in)";
$stm = $db->prepare($sql);
$stm->execute($arr);
$data = $stm->fetchAll();
I would make $idlist and array, then simply loop through the array using foreach to delete the specific item.
$idlist = array('260','201','221','216','217','169','210','212','213');
$stmt = $dbh->prepare("DELETE FROM `foo` WHERE `id` = ?");
$stmt->bindParam(1, $id);
foreach ($idlist as $item){
$id = $item;
$stmt->execute();
}

SELECT * FROM in MySQLi

My site is rather extensive, and I just recently made the switch to PHP5 (call me a late bloomer).
All of my MySQL query's before were built as such:
"SELECT * FROM tablename WHERE field1 = 'value' && field2 = 'value2'";
This made it very easy, simple and friendly.
I am now trying to make the switch to mysqli for obvious security reasons, and I am having a hard time figuring out how to implement the same SELECT * FROM queries when the bind_param requires specific arguments.
Is this statement a thing of the past?
If it is, how do I handle a query with tons of columns involved? Do I really need to type them all out every time?
I could be wrong, but for your question I get the feeling that bind_param() isn't really the problem here. You always need to define some conditions, be it directly in the query string itself, of using bind_param() to set the ? placeholders. That's not really an issue.
The problem I had using MySQLi SELECT * queries is the bind_result() part. That's where it gets interesting. I came across this post from Jeffrey Way: http://jeff-way.com/2009/05/27/tricky-prepared-statements/(This link is no longer active). The script basically loops through the results and returns them as an array — no need to know how many columns there are, and you can still use prepared statements.
In this case it would look something like this:
$stmt = $mysqli->prepare(
'SELECT * FROM tablename WHERE field1 = ? AND field2 = ?');
$stmt->bind_param('ss', $value, $value2);
$stmt->execute();
Then use the snippet from the site:
$meta = $stmt->result_metadata();
while ($field = $meta->fetch_field()) {
$parameters[] = &$row[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
foreach($row as $key => $val) {
$x[$key] = $val;
}
$results[] = $x;
}
And $results now contains all the info from SELECT *. So far I found this to be an ideal solution.
"SELECT * FROM tablename WHERE field1 = 'value' && field2 = 'value2'";
becomes
"SELECT * FROM tablename WHERE field1 = ? && field2 = ?";
which is passed to the $mysqli::prepare:
$stmt = $mysqli->prepare(
"SELECT * FROM tablename WHERE field1 = ? && field2 = ?");
$stmt->bind_param( "ss", $value, $value2);
// "ss' is a format string, each "s" means string
$stmt->execute();
$stmt->bind_result($col1, $col2);
// then fetch and close the statement
OP comments:
so if i have 5 parameters, i could potentially have "sssis" or something (depending on the types of inputs?)
Right, one type specifier per ? parameter in the prepared statement, all of them positional (first specifier applies to first ? which is replaced by first actual parameter (which is the second parameter to bind_param)).
While you are switching, switch to PDO instead of mysqli, It helps you write database agnositc code and have better features for prepared statements.
http://www.php.net/pdo
Bindparam for PDO:
http://se.php.net/manual/en/pdostatement.bindparam.php
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = :value1 && field2 = :value2");
$sth->bindParam(':value1', 'foo');
$sth->bindParam(':value2', 'bar');
$sth->execute();
or:
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = ? && field2 = ?");
$sth->bindParam(1, 'foo');
$sth->bindParam(2, 'bar');
$sth->execute();
or execute with the parameters as an array:
$sth = $dbh->prepare("SELECT * FROM tablename WHERE field1 = :value1 && field2 = :value2");
$sth->execute(array(':value1' => 'foo' , ':value2' => 'bar'));
It will be easier for you if you would like your application to be able to run on different databases in the future.
I also think you should invest some time in using some of the classes from Zend Framwework whilst working with PDO. Check out their Zend_Db and more specifically [Zend_Db_Factory][2]. You do not have to use all of the framework or convert your application to the MVC pattern, but using the framework and reading up on it is time well spent.
Is this statement a thing of the past?
Yes. Don't use SELECT *; it's a maintenance nightmare. There are tons of other threads on SO about why this construct is bad, and how avoiding it will help you write better queries.
See also:
What is the reason not to use select *?
Performance issue in using SELECT *?
Why is using '*' to build a view bad?
You can still use it (mysqli is just another way of communicating with the server, the SQL language itself is expanded, not changed). Prepared statements are safer, though - since you don't need to go through the trouble of properly escaping your values each time. You can leave them as they were, if you want to but the risk of sql piggybacking is reduced if you switch.
you can use get_result() on the statement.
http://php.net/manual/en/mysqli-stmt.get-result.php
I was looking for a nice and complete example of how to bind multiple query parameters dynamically to any SELECT, INSERT, UPDATE and DELETE query. Alec mentions in his answer a way of how to bind result, for me the get_result() after execute() function for SELECT queries works just fine, and am able to retrieve all the selected results into an array of associative arrays.
Anyway, I ended up creating a function where I am able to dynamically bind any amount of parameters to a parametrized query ( using call_user_func_array function) and obtain a result of the query execution. Below is the function with its documentation (please read before it before using - especially the $paremetersTypes - Type specification chars parameter is important to understand)
/**
* Prepares and executes a parametrized QUERY (SELECT, INSERT, UPDATE, DELETE)
*
* #param[in] $dbConnection mysqli database connection to be used for query execution
* #param[in] $dbQuery parametrized query to be bind parameters for and then execute
* #param[in] $isDMQ boolean value, should be set to TRUE for (DELETE, INSERT, UPDATE - Data manipulaiton queries), FALSE for SELECT queries
* #param[in] $paremetersTypes String representation for input parametrs' types as per http://php.net/manual/en/mysqli-stmt.bind-param.php
* #param[in] $errorOut A variable to be passed by reference where a string representation of an error will be present if a FAUILURE occurs
* #param[in] $arrayOfParemetersToBind Parameters to be bind to the parametrized query, parameters need to be specified in an array in the correct order
* #return array of feched records associative arrays for SELECT query on SUCCESS, TRUE for INSERT, UPDATE, DELETE queries on SUCCESS, on FAIL sets the error and returns NULL
*/
function ExecuteMySQLParametrizedQuery($dbConnection, $dbQuery, $isDMQ, $paremetersTypes, &$errorOut, $arrayOfParemetersToBind)
{
$stmt = $dbConnection->prepare($dbQuery);
$outValue = NULL;
if ($stmt === FALSE)
$errorOut = 'Failed to prepare statement for query: ' . $dbQuery;
else if ( call_user_func_array(array($stmt, "bind_param"), array_merge(array($paremetersTypes), $arrayOfParemetersToBind)) === FALSE)
$errorOut = 'Failed to bind required parameters to query: ' . $dbQuery . ' , parameters :' . json_encode($arrayOfParemetersToBind);
else if (!$stmt->execute())
$errorOut = "Failed to execute query [$dbQuery] , erorr:" . $stmt->error;
else
{
if ($isDMQ)
$outValue = TRUE;
else
{
$result = $stmt->get_result();
if ($result === FALSE)
$errorOut = 'Failed to obtain result from statement for query ' . $dbQuery;
else
$outValue = $result->fetch_all(MYSQLI_ASSOC);
}
}
$stmt->close();
return $outValue;
}
usage:
$param1 = "128989";
$param2 = "some passcode";
$insertQuery = "INSERT INTO Cards (Serial, UserPin) VALUES (?, ?)";
$rowsInserted = ExecuteMySQLParametrizedQuery($dbConnection, $insertQuery, TRUE, 'ss', $errorOut, array(&$param1, &$param2) ); // Make sure the parameters in an array are passed by reference
if ($rowsInserted === NULL)
echo 'error ' . $errorOut;
else
echo "successfully inserted row";
$selectQuery = "SELECT CardID FROM Cards WHERE Serial like ? AND UserPin like ?";
$arrayOfCardIDs = ExecuteMySQLParametrizedQuery($dbConnection, $selectQuery, FALSE, 'ss', $errorOut, array(&$param1, &$param2) ); // Make sure the parameters in an array are passed by reference
if ($arrayOfCardIDs === NULL)
echo 'error ' . $errorOut;
else
{
echo 'obtained result array of ' . count($arrayOfCardIDs) . 'selected rows';
if (count($arrayOfCardIDs) > 0)
echo 'obtained card id = ' . $arrayOfCardIDs[0]['CardID'];
}

Categories