How to Pass an Array of Pdo Parameters Yet Still Specify Their Types

How can I pass an array of PDO parameters yet still specify their types?

If you turn off the default setting of PDO::ATTR_EMULATE_PREPARES, then it will work. I just found out that that setting is on by default for mysql, which means you never actually use prepared statements, php internally creates dynamic sql for you, quoting the values for you and replacing the placeholders. Ya, a major wtf.

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$stmt = $pdo->prepare($sql);
$stmt->execute(array(5)); //works!

The prepares are emulated by default because of performance reasons.

See as well PDO MySQL: Use PDO::ATTR_EMULATE_PREPARES or not?

Setting PDO/MySQL LIMIT with Named Placeholders

The problem is that execute() quotes the numbers and treats as strings:

From the manual - An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as PDO::PARAM_STR.

<?php 
public function getLatestWork($numberOfSlides=10, $type=0) {

$numberOfSlides = intval(trim($numberOfSlides));

$STH = $this->_db->prepare("SELECT slideID
FROM slides
WHERE visible = 'true'
AND type = :type
ORDER BY order
LIMIT :numberOfSlides;");

$STH->bindParam(':numberOfSlides', $numberOfSlides, PDO::PARAM_INT);
$STH->bindParam(':type', $type, PDO::PARAM_INT);

$STH->execute();
$result = $STH->fetchAll(PDO::FETCH_COLUMN);

return $result;
}
?>

PDO OCI PHP pass array parameter to stored procedure

I don't know if you are still looking for an answer to this question, but I will give you my answer in case others are having the same problem. I found my answer at the following link.

http://www.oracle.com/technetwork/articles/fuecks-sps-095636.html

Here is a function that I created to pass parameters to an Oracle procedure. The procedure that I am using is not returning any results so therefore this function does not catch anything.

public function bindVariablesToProcedure($var1, $var2, $var3)
{
$rtn = []; // initalize array
if($this->dbconnect)
{
/* schema is your database schema
BindVariable is your stored procedure */
$bindSql = 'BEGIN schema.BindVariable(:var1, :var2, :var3); END;';

/* The numbers for the fourth parameter are the character lengths
that are in my database. I found that without these numbers
the "oci_bind_by_name" function would error out. */
$bindRes = oci_parse($this->dbconnect, $bindSql);
oci_bind_by_name($bindRes, ':var1', $var1, 100);
oci_bind_by_name($bindRes, ':var2', $var2, 5);
oci_bind_by_name($bindRes, ':var3', $var3, 100);

if(oci_execute($bindRes))
{
$rtn['status'] = "success";
}
else
{
$e = oci_error($bindRes); // For oci_execute errors pass the statement handle
$rtn['bindErrorSql'] = $e['sqltext'];
$rtn['bindErrorCode'] = $e['code'];
$rtn['bindErrorMsg'] = $e['message'];
$rtn['status'] = "failed";
}
}
return $rtn;
}

PHP PDO error when using placeholders in the LIMIT clause of a MySQL query

I solved it.I Type casted the :numRows placeholder.

$numRows=(int)$numRows;
$sql = 'SELECT sql_calc_found_rows * FROM ' .
TBL_MEMBERS .'ORDER BY'. $order .'LIMIT :startRow,:numRows';
try {
$st = $conn->prepare($sql);
$st->bindValue(":startRow", $startRow, PDO::PARAM_INT);
$st->bindValue(":numRows", $numRows, PDO::PARAM_INT);
$st->execute();
...

And it worked. I also noticed the ' should be use instead of ".

PHP - PDO not taking imploded array as question mark parameter for IN clause in SELECT query

A ? placeholder can only substitute for a single literal. If you want an IN clause to accept an arbitrary number of values, you must prepare a new query for each possible length of your array.

E.g., if you want to select ids in array [1, 2], you need a query that looks like SELECT * FROM tbl WHERE id IN (?,?). If you then pass in a three-item array, you need to prepare a query like SELECT * FROM tbl WHERE id IN (?,?,?), and so on.

In other words, you cannot know with certainty what query you want to build/create until the moment you have the data you want to bind to the prepared statement.

This is not a PDO limitation, it is fundamental to how prepared queries work in SQL databases. Think about it--what datatype would the ? be in SQL-land if you said IN ? but had ? stand in for something non-scalar?

Some databases have array-types (such as PostgreSQL). Maybe they can interpret IN <array-type> the same way as IN (?,?,...) and this would work. But PDO has no way of sending or receiving array-type data (there is no PDO::PARAM_ARRAY), and since this is an uncommon and esoteric feature it's unlikely PDO ever will.

Note there is an extra layer of brokenness here. A normal database, when faced with the condition int_id = '1,2,3,4' would not match anything since '1,2,3,4' cannot be coerced to an integer. MySQL, however, will convert this to the integer 1! This is why your query:

$pstmt = $db->prepare('SELECT * FROM `oc_product` WHERE `product_id` IN (?)');
$pstmt->execute(array('28,29,30,46,47'));

Will match product_id = 28. Behold the insanity:

mysql> SELECT CAST('28,29,30,46,47' AS SIGNED INTEGER);
+------------------------------------------+
| CAST('28,29,30,46,47' AS SIGNED INTEGER) |
+------------------------------------------+
| 28 |
+------------------------------------------+
1 rows in set (0.02 sec)

PDO IN() Array Statement AND a placeholder

Solution

This should work, if $values is an array:

$query = "SELECT * FROM table WHERE id IN ($placeholders) AND product=?";
$stm->execute(array_merge($values, array($product)));

Explanation

execute() expects one parameter - in this case an array - to be provided. By adding array_merge($values, array($product)) you create one array with $product added at the end, so the query should work correctly.

See the demo here: http://ideone.com/RcClX

MySQL PDO Name-Value Prepared Statement Using Last Parameter Only

Thanks for all the help everybody!

I went with Michael's solution, but tested Ryan's too.

i.e.

Update to note as solved. Using...

$stmt->execute($params); // scrap the foreach nonsense...

bindValue() rather than bindParam() is also appropriate.

To wrap things up, as per Ryan's comment, I'm pushing an answer out.

Thanks again!

Why do we need to specify the parameter type in bindParam()?

Using bindParam() with types could be considered safer, because it allows for stricter verification, further preventing SQL injections. However, I wouldn't say there is a real security risk involved if you don't do it like that, as it is more the fact that you do a prepared statement that protects from SQL injections than type verification. A simpler way to achieve this is by simply passing an array to the execute() function instead of using bindParam(), like this:

$calories = 150; 
$colour = 'red';

$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour');

$sth->execute(array(
'calories' => $calories,
'colour' => $colour
));

You're not obligated to use a dictionary, you can also do it just like you did with questionmarks and then put it in the same order in the array. However, even if this works perfectly, I'd recommend making a habit of using the first one, since this method is a mess once you reach a certain number of parameters. For the sake of being complete, here's what it looks like:

$calories = 150; 
$colour = 'red';

$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?');

$sth->execute(array($calories, $colour));


Related Topics



Leave a reply



Submit