How to avoid PDOStatement::bindParam messing with the referenced value
Question
I'm facing the problem described by Steve M but on a "large scale", please see for details http://www.php.net/manual/en/pdostatement.bindparam.php#94711
I'm using Phalcon, but it mostly acts as a wrapper, the question must not be limited by that. The detailed issue is here – https://github.com/phalcon/cphalcon/issues/2111
TL;DR Int values in array are converted to strings, as such:
var_dump($params); // array(2) { [0]=> int(6609) [1]=> int(6664) }
$adapter->fetchAll($sql, Db::FETCH_ASSOC, $params);
var_dump($params); // array(2) { [0]=> string(4) "6609" [1]=> string(4) "6664" }
I reuse that array later and heavily rely on the ints. When they turn into strings it spoils all the joy… No matter what I do with it (copies values with foreach
, array_merge
into new array, ArrayObject::getArrayCopy
to get a copy) the original values keep changing (and values of other arrays they've been copied from). They only way with copying that works is:
$adapter->fetchAll($sql, Db::FETCH_ASSOC, unserialize(serialize(($params)));
That feels like an massive overkill. Another solution that seems to work is below, though $paramTypes
is not documented (Phalcon code that handles it for those who're interested).
$paramTypes = [];
foreach ($params as $param) {
if (is_int($param)) {
$paramTypes[] = \PDO::PARAM_INT;
} else {
$paramTypes[] = null;
}
}
$rows = $this->adapter->fetchAll($sql, Db::FETCH_ASSOC, $params, $paramTypes);
To me it seems to be the best approach, BUT here is the same method described by Axeia that received negative feedback – http://www.php.net/manual/en/pdo.constants.php#97130.
My questions:
- Why it might be a bad idea doing it the way Axeia suggests?
- Can you suggest another alternative to this? Apart from converting the modified array back to integers.
- WTF???!!! Why it need to take a reference and modify it? Should there be a bug report???
Many thanks ya all!
Solution 2
The problem was with the fact that Phalcon inadequately did use PDOStatement::bindParam
instead of PDOStatement::bindValue
. The big debate is covered on the github and followed with a pull request, which eventually has been merged into version 1.3.2.
OTHER TIPS
Your mysql database will keep data type as it is width php-mysqlnd driver and these two attributes set to false:
$dbh->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false);
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$parm1 = 2;
var_dump($parm1);
// int(2)
$sth = $dbh->prepare('SELECT * FROM test.table1 WHERE id = ?');
$sth->bindParam(1, $parm1, PDO::PARAM_INT);
$sth->execute();
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
var_dump($parm1);
// int(2)
var_dump($data);
/* you get nice and clean result:
array(1) {
[0]=>
array(4) {
["id"]=> int(2)
["name"]=> string(3) "XYZ"
["someint"]=> int(543)
["somefloat"]=> float(1000.0001220703)
}
}
And table is:
CREATE TABLE `table1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`someint` int(8) DEFAULT NULL,
`somefloat` float(10,5) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
*/