bindValue() 文档没有解释的一点(如果不仔细阅读的话),就是 bindParam() 是通过引用传递给 PDO 的,而 bindValue() 则不是。
因此,使用 bindValue(),您可以执行类似 $stmt->bindValue(":something", "bind this"); 的操作,而使用 bindParam() 则会失败,因为您无法通过引用传递字符串,例如。
(PHP 5 >= 5.1.0, PHP 7, PHP 8, PECL pdo >= 1.0.0)
PDOStatement::bindValue — 绑定值到参数
将值绑定到 SQL 语句中相应的命名或问号占位符,该 SQL 语句用于准备语句。
param
参数标识符。对于使用命名占位符的准备语句,这将是 :name 格式的参数名称。对于使用问号占位符的准备语句,这将是参数的 1 索引位置。
value
要绑定到参数的值。
type
使用 PDO::PARAM_*
常量 的参数的显式数据类型。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_WARNING
,则发出级别为 E_WARNING
的错误。
如果属性 PDO::ATTR_ERRMODE
设置为 PDO::ERRMODE_EXCEPTION
,则抛出 PDOException。
示例 #1 使用命名占位符执行准备语句
<?php
/* 通过绑定 PHP 变量来执行准备语句 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour');
/* 使用其名称设置参数值 */
$sth->bindValue('calories', $calories, PDO::PARAM_INT);
/* 可选地,参数名称也可以以冒号 ":" 为前缀 */
$sth->bindValue(':colour', $colour, PDO::PARAM_STR);
$sth->execute();
?>
示例 #2 使用问号占位符执行准备语句
<?php
/* 通过绑定 PHP 变量来执行准备语句 */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < ? AND colour = ?');
$sth->bindValue(1, $calories, PDO::PARAM_INT);
$sth->bindValue(2, $colour, PDO::PARAM_STR);
$sth->execute();
?>
bindValue() 文档没有解释的一点(如果不仔细阅读的话),就是 bindParam() 是通过引用传递给 PDO 的,而 bindValue() 则不是。
因此,使用 bindValue(),您可以执行类似 $stmt->bindValue(":something", "bind this"); 的操作,而使用 bindParam() 则会失败,因为您无法通过引用传递字符串,例如。
在绑定参数时,显然您不能重复使用占位符(例如 "select * from mails where sender=:me or recipient=:me"),您必须为它们指定不同的名称,否则您的查询将返回空结果(但不会失败,不幸的是)。如果您遇到类似问题,请注意这一点。
使用 PDO::PARAM_INT 进行验证时要小心。
请考虑以下示例
<?php
/* php --version
* PHP 5.6.25 (cli) (built: Aug 24 2016 09:50:46)
* Copyright (c) 1997-2016 The PHP Group
* Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
*/
$id = '1a';
$stm = $pdo->prepare('select * from author where id = :id');
$bind = $stm->bindValue(':id', $id, PDO::PARAM_INT);
$stm->execute();
$authors = $stm->fetchAll();
var_dump($id); // string(2)
var_dump($bind); // true
var_dump((int)$id); // int(1)
var_dump(is_int($id)); // false
var_dump($authors); // the author id=1 =(
// remember
var_dump(1 == '1'); // true
var_dump(1 === '1'); // false
var_dump(1 === '1a'); // false
var_dump(1 == '1a'); // true
?>
我的观点:bindValue() 应该首先在内部测试 is_int(),然后再进行任何操作。
这是一个错误吗?我不确定。
虽然 bindValue() 会转义引号,但它不会转义 "%" 和 "_",因此在使用 LIKE 时要小心。如果参数本身没有转义,恶意参数中充满 "%%" 可以转储整个数据库。
请注意,在大多数情况下,第三个参数 ($data_type) 不会将值类型转换为查询中使用的任何其他类型,也不会在类型与提供的值不匹配时抛出任何错误。此参数本质上没有任何效果,除非设置了它并且不是浮点数,才会抛出错误,因此不要认为它为查询增加了任何额外的安全级别。
执行类型转换的两个例外情况:
- 如果使用 PDO::PDO_PARAM_INT 并提供布尔值,则会将其转换为长整型
- 如果使用 PDO::PDO_PARAM_BOOL 并提供长整型,则会将其转换为布尔值
<?php
$query = 'SELECT * FROM `users` WHERE username = :username AND `password` = ENCRYPT( :password, `crypt_password`)';
$sth= $dbh->prepare($query);
// First try passing a random numerical value as the third parameter
var_dump($sth->bindValue(':username','bob', 12345.67)); // bool(true)
// Next try passing a string using the boolean type
var_dump($sth->bindValue(':password','topsecret_pw', PDO::PARAM_BOOL)); // bool(true)
$sth->execute(); // Query is executed successfully
$result = $sth->fetchAll(); // Returns the result of the query
?>
此函数用于在数组上绑定值。可以使用 $typeArray 预先指定值的类型。
<?php
/**
* @param string $req : 要链接值的查询
* @param array $array : 包含要绑定的值的关联数组
* @param array $typeArray : 关联数组,其中包含与 $array 中相应键的期望值
* */
function bindArrayValue($req, $array, $typeArray = false)
{
if(is_object($req) && ($req instanceof PDOStatement))
{
foreach($array as $key => $value)
{
if($typeArray)
$req->bindValue(":$key",$value,$typeArray[$key]);
else
{
if(is_int($value))
$param = PDO::PARAM_INT;
elseif(is_bool($value))
$param = PDO::PARAM_BOOL;
elseif(is_null($value))
$param = PDO::PARAM_NULL;
elseif(is_string($value))
$param = PDO::PARAM_STR;
else
$param = FALSE;
if($param)
$req->bindValue(":$key",$value,$param);
}
}
}
}
/**
* ## 例子 ##
* $array = array('language' => 'php','lines' => 254, 'publish' => true);
* $typeArray = array('language' => PDO::PARAM_STR,'lines' => PDO::PARAM_INT,'publish' => PDO::PARAM_BOOL);
* $req = 'SELECT * FROM code WHERE language = :language AND lines = :lines AND publish = :publish';
* 可以像这样绑定 $array:
* bindArrayValue($array,$req,$typeArray);
* 当使用 limit 子句时,该函数更有用,因为它们需要整型。
* */
?>
<?php
/**
* 绑定位值。
*/
$sql = 'SELECT * FROM myTable WHERE level & ?';
$sth = \App::pdo()->prepare($sql);
$sth->bindValue(1, 0b0101, \PDO::PARAM_INT);
$sth->execute();
$result = $sth->fetchAll(\PDO::FETCH_ASSOC);
我们无法在调用 bindValue() 之后定义值变量的原因是,它会立即将值绑定到准备好的语句,而不会等待 execute() 的执行。
以下代码将发出一个通知,并阻止查询执行
<?php
$st = $db->prepare ("SELECT * FROM posts WHERE id= :val ");
$st->bindValue(':val',$val);
$val = '2';
$st->execute();
?>
输出
注意:未定义变量:val。
而在 bindParam 的情况下,对参数的值的计算将不会在执行 execute() 之前进行。这是为了获得引用传递的优势。
<?php
$st = $db->prepare ("SELECT * FROM posts WHERE id = :val ");
$st->bindParam(':val',$val);
$val = '2';
//
// some code
//
$val = '3'; // 重新分配值变量
$st->execute();
?>
运行良好。
这实际上可以用于在 MySQL 中的整数字段上绑定 NULL
$stm->bindValue(':param', null, PDO::PARAM_INT);
注意边缘情况!
使用 MySQL 原生预处理,您的整数可能会在某些机器上发生溢出。
<?php
$x = 2147483648;
var_dump($x); // 输出: int(2147483648)
$s = $db->prepare('SELECT :int AS I, :str AS S;');
$s->bindValue(':int', $x, PDO::PARAM_INT);
$s->bindValue(':str', $x, PDO::PARAM_STR);
$s->execute();
var_dump( $s->fetchAll(PDO::FETCH_ASSOC) );
/* 输出: array(2) {
["I"]=>
string(11) "-2147483648"
["S"]=>
string(10) "2147483648"
} */
?>
此外,尝试在 MySQL 中使用原生预处理绑定 PDO::PARAM_BOOL 可能会导致查询静默失败并返回空集。
在这些情况下,模拟预处理工作更稳定,因为它们将所有内容转换为字符串,并仅决定是否对参数进行引用。
bindValue 带数据类型取决于参数名称
<?php
$db = new PDO (...);
$db -> setAttribute (PDO::ATTR_STATEMENT_CLASS, array ('MY_PDOStatement ', array ($db)));
class MY_PDOStatement extends PDOStatement {
public function execute ($input = array ()) {
foreach ($input as $param => $value) {
if (preg_match ('/_id$/', $param))
$this -> bindValue ($param, $value, PDO::PARAM_INT);
else
$this -> bindValue ($param, $value, PDO::PARAM_STR);
}
return parent::execute ();
}
}
?>
一次性绑定整个数组
<?php
function PDOBindArray(&$poStatement, &$paArray){
foreach ($paArray as $k=>$v){
@$poStatement->bindValue(':'.$k,$v);
} // foreach
} // function
// 例子
$stmt = $dbh->prepare("INSERT INTO tExample (id,value) VALUES (:id,:value)");
$taValues = array(
'id' => '1',
'value' => '2'
); // 数组
PDOBindArray($stmt,$taValues);
$stmt->execute();
?>
参数名称必须像 PHP 变量一样。
例如:
<?php
$dbh = new PDO("mysql:dbname=test;host=127.0.0.1", "user", "password");
$sth = $dbh->prepare("SELECT * FROM `table` WHERE `last-name`=:last-name");
if($sth !== false && $sth->bindValue(":last-name", "Ngo")) {
$sth->execute();
}
// 输出: PHP Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: parameter was not defined
?>