mysqli_stmt::bind_param

mysqli_stmt_bind_param

(PHP 5, PHP 7, PHP 8)

mysqli_stmt::bind_param -- mysqli_stmt_bind_param将变量绑定到准备好的语句作为参数

描述

面向对象风格

public mysqli_stmt::bind_param(string $types, mixed &$var, mixed &...$vars): bool

过程式风格

mysqli_stmt_bind_param(
    mysqli_stmt $statement,
    string $types,
    mixed &$var,
    mixed &...$vars
): bool

为由 mysqli_prepare()mysqli_stmt_prepare() 准备的 SQL 语句中的参数标记绑定变量。

注意:

如果变量的数据大小超过最大允许的数据包大小 (max_allowed_packet),则必须在 types 中指定 b 并使用 mysqli_stmt_send_long_data() 以数据包形式发送数据。

注意:

在将 mysqli_stmt_bind_param()call_user_func_array() 结合使用时,必须小心。请注意,mysqli_stmt_bind_param() 要求参数按引用传递,而 call_user_func_array() 可以接受一个参数,该参数可以表示引用的变量列表或值列表。

参数

statement

仅过程式风格:由 mysqli_stmt_init() 返回的 mysqli_stmt 对象。

types

包含一个或多个字符的字符串,用于指定对应绑定变量的类型

类型规范字符
字符 描述
i 对应变量的类型为 int
d 对应变量的类型为 float
s 对应变量的类型为 string
b 对应变量是一个 blob,并将以数据包形式发送

var
vars

变量数量和字符串 types 的长度必须与语句中的参数匹配。

返回值

成功时返回 true,失败时返回 false

错误/异常

如果启用了 mysqli 错误报告 (MYSQLI_REPORT_ERROR) 并且请求的操作失败,则会生成警告。如果此外,模式设置为 MYSQLI_REPORT_STRICT,则会抛出一个 mysqli_sql_exception

示例

示例 #1 mysqli_stmt::bind_param() 示例

面向对象风格

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');

$stmt = $mysqli->prepare("INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
$stmt->bind_param('sssd', $code, $language, $official, $percent);

$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;

$stmt->execute();

printf("%d 行插入。\n", $stmt->affected_rows);

/* 清理 CountryLanguage 表 */
$mysqli->query("DELETE FROM CountryLanguage WHERE Language='Bavarian'");
printf("%d 行删除。\n", $mysqli->affected_rows);

过程式风格

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$link = mysqli_connect('localhost', 'my_user', 'my_password', 'world');

$stmt = mysqli_prepare($link, "INSERT INTO CountryLanguage VALUES (?, ?, ?, ?)");
mysqli_stmt_bind_param($stmt, 'sssd', $code, $language, $official, $percent);

$code = 'DEU';
$language = 'Bavarian';
$official = "F";
$percent = 11.2;

mysqli_stmt_execute($stmt);

printf("%d 行插入。\n", mysqli_stmt_affected_rows($stmt));

/* 清理 CountryLanguage 表 */
mysqli_query($link, "DELETE FROM CountryLanguage WHERE Language='Bavarian'");
printf("%d 行删除。\n", mysqli_affected_rows($link));

以上示例将输出

1 row inserted.
1 row deleted.

示例 #2 使用 ... 提供参数

... 运算符可用于提供可变长度的参数列表,例如在 WHERE IN 子句中。

<?php

mysqli_report
(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'my_user', 'my_password', 'world');

$stmt = $mysqli->prepare("SELECT Language FROM CountryLanguage WHERE CountryCode IN (?, ?)");
/* 使用 ... 提供参数 */
$stmt->bind_param('ss', ...['DEU', 'POL']);
$stmt->execute();
$stmt->store_result();

printf("%d 行找到。\n", $stmt->num_rows());

以上示例将输出

10 rows found.

另请参阅

添加备注

用户贡献的备注 19 个备注

jk at jankriedner dot de
13 年前
使用 mysqli::bind_param() 和数组元素时,有一些注意事项。
重新分配数组将破坏任何引用,无论键是否相同。
必须显式地重新分配数组中的每个值,才能保持引用。
最佳示例如下:
<?php
function getData() {
return array(
0=>array(
"name"=>"test_0",
"email"=>"[email protected]"
),
1=>array(
"name"=>"test_1",
"email"=>"[email protected]"
)
);
}
$db = new mysqli("localhost","root","","tests");
$sql = "INSERT INTO `user` SET `name`=?,`email`=?";
$res = $db->prepare($sql);
// 如果将数组元素绑定到预处理语句,则必须先使用使用的键声明数组:
$arr = array("name"=>"","email"=>"");
$res->bind_param("ss",$arr['name'],$arr['email']);
// 这是介绍部分...

/*
示例 1(不会按预期工作,创建两个空条目)
在 while() 头中重新分配数组会生成一个新数组,而 bind_param 中的引用会保留到旧数组
*/
foreach( getData() as $arr ) {
$res->execute();
}

/*
示例 2(将按预期工作)
显式地重新分配每个值会使引用保持有效
*/
foreach( getData() as $tempArr ) {
foreach(
$tempArr as $k=>$v) {
$arr[$k] = $v;
}
$res->execute();
}
?>
匿名
13 年前
除了 Blob 和 NULL 处理之外,关于如何根据类型字符串参数自动转换和转发参数值到 Mysql 引擎,还有几点需要注意。

1) PHP 将在后台自动将值转换为对应于绑定类型字符串的底层类型。例如:

<?php

$var
= true;
bind_param('i', $var); // 作为 1 转发到 Mysql

?>

2) 虽然 PHP 数字如果大于 PHP_INT_MAX,无法可靠地强制转换为 (int),但在后台,值仍将根据大小转换为最多 long long。这意味着要记住精度限制,并避免手动将变量首先强制转换为 (int),您仍然可以使用 'i' 绑定类型用于较大的数字。例如:

<?php

$var
= '429496729479896';
bind_param('i', $var); // 作为 429496729479900 转发到 Mysql

?>

3) 在大多数情况下,您可以对大多数参数参数默认使用 's'。然后,该值将在后台自动转换为字符串,然后传递给 Mysql 引擎。然后 Mysql 将使用从 PHP 收到的值执行自己的转换。这使您可以绑定到更大的数字,而无需担心精度,也可以绑定到对象,只要该对象具有 '__toString' 方法即可。

这种自动字符串转换行为极大地改善了像日期时间处理这样的事情。例如:如果您扩展了 DateTime 类以添加一个返回 Mysql 预期日期时间格式的 __toString 方法,那么您只需使用 's' 类型绑定到该 DateTime_Extended 对象即可。例如:

<?php

// DateTime_Extended 定义了 __toString,以返回 Mysql 格式的日期时间
$var = new DateTime_Extended;
bind_param('s', $var); // 作为 '2011-03-14 17:00:01' 转发到 Mysql

?>
davidharrison at gmail dot com
7 年前
此页面中提供了两种通过 call_user_func_array() 调用 bind_param() 的解决方案,它们涉及使用名为 refValues() 的用户创建的函数,以便您可以将参数作为引用传递给 bind_param()。

这在 PHP v5.3 中(我认为在之前版本中)完美运行,但升级到 PHP v7.1.7 后,此处的 refValues() 函数不再能正确地将数组转换为引用数组。相反,您将收到警告

"PHP 警告:mysqli_stmt_bind_param() 的参数 3 预期为引用,但提供的值"

我相信这是因为对数组和引用处理的更改,如“从 PHP 5.6.x 迁移到 PHP 7.0.x”指南中“向后不兼容性”(更改:“按值进行 foreach 操作会复制数组)中所述。

因此,至少在 PHP v7.1.7 中,用户创建的函数 refValues() 不再返回引用数组,而是返回普通的值数组。

将 refValues() 的函数定义更改为接受数组作为引用似乎解决了这个问题 - 正如预期的那样,它返回引用数组,因此 bind_param() 按预期工作(尽管我还没有对它进行超级彻底的测试,以确保没有其他不良影响,尤其是在旧版本的 PHP 中)。

新的 refValues() 定义很简单

<?php
function refValues(&$arr) // 将 $arr 更改为 PHP v7.1.7 的引用
{
if (
strnatcmp(phpversion(),'5.3') >= 0) // PHP 5.3+ 需要引用
{
$refs = array();
foreach(
$arr as $key => $value)
$refs[$key] = &$arr[$key];
return
$refs;
}
return
$arr;
}
?>
travis at twooutrally dot com
8 年前
参数类型比您想象的更重要!

对任何想要找到不那么彻底的自动化预处理语句解决方案的人来说,这是一个警示故事。以以下 mysqli_stmt 扩展方法为例。

<?php

public function param_type($param)
{
if (
ctype_digit((string) $param)
return
$param <= PHP_INT_MAX ? 'i' : 's';

if (
is_numeric($param))
return
'd';

return
's';
}

?>

乍一看,这个函数似乎非常简单直白,没有任何问题。类似的代码曾经用作一个大型自动化扩展中的一个小片段,它尽职尽责地高效运行,每天处理数十万个查询。

现在我知道你在想什么:它不处理 Blob 类型。好吧,我们没有处理 Blob 类型(现在仍然没有),所以这从来不是问题。这个问题远比这更隐蔽,最终也更具破坏性。

那么,出了什么问题呢?问题开始出现时,我们开始对一个新创建的索引进行 SELECT 查询自动化,该索引用于存储电话号码的列。该列的类型为 VARCHAR,但存储的数据始终以整数格式格式化。在执行写入操作时这不是问题,但一旦我们开始从该索引的表中读取,一切都乱了套。

我们不完全确定,但据我们所知,在读取查询中将参数绑定到 VARCHAR 索引作为 'i' 而不是 's' 的行为是有害的,其方式如下:MySQL 将忽略索引上的 B 树并执行全表扫描。对于较小的表,这可能永远不会成为一个重大的性能问题。但是,当你的表达到数千万行时……
匿名
13 年前
你可以将 NULL 值绑定到变量,并且在更新和插入查询中,无论你为其关联的绑定字符串类型是什么,相应的字段都将更新为 NULL。但是,对于用于 WHERE 子句的参数(即 where field = ?),查询将不会有任何效果,并且不会产生任何结果。

当将一个值与 NULL 进行比较时,MYSQL 语法是 "value IS NULL" 或 "value IS NOT NULL"。因此,你不能传递类似 "WHERE (value = ?)" 的内容,并期望使用空值参数来使其工作。

相反,你可以在 WHERE 子句中执行以下操作

"WHERE (IF(ISNULL(?), field1 is null, field1 = ?))"

然后,传递你要测试的值两次

bind_param('ss', $value1, $value1);
tomasz at marcinkowski dot pl
10 年前
当你尝试绑定一个字符串参数时,你会得到一个 "Number of variables doesn't match number of parameters in prepared statement" 错误,请确保你没有用引号括起问号。

我错误地写了这样的查询
SELECT something FROM table WHERE param_name = "?"

<?php $stmt->bind('s', $param_value); ?> 绑定它一直失败。我所要做的就是删除围绕 "?" 的引号。
希望这能节省一些人的时间。
eisoft
14 年前
我为在一个简单的表中插入数据编写了一个预处理语句 - 图像 (blob) 以及它们唯一的标识符 (字符串)。我所有的 Blob 的大小都小于 MAX-ALLOWED-PACKET 值。

我发现,当我绑定我的 BLOB 参数时,我需要将其作为字符串传递,否则它会在我的表中被截断为零长度。所以我必须这样做

<?php
$ok
= $stmt->bind_param( 'ss', $id, $im ) ;
?>
asb(.d o,t )han(a t)n i h e i(d.o_t)dk
13 年前
需要注意的是,MySQL 在预处理语句中使用 IN 子句方面存在一些问题。

例如,代码
<?php

$idArr
= "1, 2, 3, 4";
$int_one = 1;
$int_two = 2;
$int_three = 3;
$int_four = 4;

$db = new MySQLi();
$bad_stmt = $db->prepare(SELECT `idAsLetters` FROM `tbl` WHERE `id` IN(?));
$bad_stmt->bind_param("s", $idArr);
$bad_stmt->bind_result($ias);
$bad_stmt->execute();

echo
"Bad results:" . PHP_EOL;
while(
$stmt->fetch()){
echo
$ias . PHP_EOL;
}

$good_stmt->close();

$good_stmt = $db->prepare(SELECT `idAsLetters` FROM `tbl` WHERE `id` IN(?, ?, ?, ?));
$good_stmt->bind_param("iiii", $int_one, $int_two, $int_three, $int_four);
$good_stmt->bind_result($ias);
$good_stmt->execute();

echo
"God results:" . PHP_EOL;
while(
$stmt->fetch()){
echo
$ias . PHP_EOL;
}
$bad_stmt->close();

$db->close();
?>
将打印以下结果

错误的结果


正确的结果





在预处理语句中使用 "IN(?)" 将只从表/视图中返回一行(第一行)。这不是 PHP 中的错误,而仅仅是 MySQL 处理预处理语句的方式。
rejohns at nOsPaMpost dot harvard dot edu
14 年前
事实上,你可以使用 mysqli_bind_parameter 将 NULL 值传递到数据库。只需创建一个变量,并将 NULL 值(参见其手册页)存储到该变量,然后绑定它。这对我来说很好用。
xianrenb at gmail dot com
11 年前
据信,如果在 $types 中指定了 'b',则相应的变量应该设置为 null,并且必须使用 mysqli_stmt::send_long_data() 或 mysqli_stmt_send_long_data() 来发送 Blob,否则 Blob 值将被视为空值。
flame
17 年前
类型为 bigint 的列需要指定为 'd' 类型,而不是 'i'。

使用 'i' 会导致大数字(例如 3000169151)被截断。

--
火焰
accountant
7 年前
如果 bind_param() 由于 "Number of elements in type definition string doesn't match number of bind variables" 而失败。它会触发一个 E_WARNING 错误。并且你不会在 $stmt->error 属性中找到该错误。
andersmmg at gmail dot com
5 年前
我有时会忘记你不能在里面放函数。例如

如果我想对某个值使用 md5(),像这样
<?php
$stmt
->bind_param("s",md5($val));
?>
这是行不通的。因为它使用变量通过绑定它们,你需要事先更改它们,像这样
<?php
$val
= md5($val);
$stmt->bind_param("s",$val);
?>
Matze
7 年前
嗨,各位,

只是想提一下,参数只能用于输入数据,不能用于表、列或数据库名称。
这让我昨天头疼了好久!
所以这段代码将不起作用

$searchtype = "Title";
$searchterm = "The Fellowship of the Ring: The Lord of the Rings";

$query =
"SELECT ISBN, Author, Title, Price
FROM books
WHERE ? = ?";

$mySql_stmt = $db->prepare($query);
$mySql_stmt->bind_param("ss" , $searchtype, $searchterm);
$mySql_stmt->execute();

相反,你必须像这样直接在查询中包含 searchtype

$searchtype = "Title";
$searchterm = "The Fellowship of the Ring: The Lord of the Rings";
$query =
"SELECT ISBN, Author, Title, Price
FROM books
WHERE $searchtype = ?";

$mySql_stmt = $db->prepare($query);
$mySql_stmt->bind_param("s", $searchterm);
$mySql_stmt->execute();

希望这能帮助某人睡个安稳觉 :)
c at zp1 dot net
3 年前
重要的是要明白,你不能提供 bind_param 值

这将不起作用

$stmt -> bind_param("s", "value");


你必须这样做

$var = "value";
$stmt -> bind_param("s", $var);
Darky
7 年前
从我的尝试中得出一个小小的结论
- 如果你使用带 `bind_param` 的预处理语句,并且你的查询看起来像
"SELECT user_id FROM users WHERE ... = ?",然后你绑定一个整数参数到它,你得到的 `user_id` 会被强制转换为整数类型。另一方面,如果你不使用预处理语句,而是使用类似 "SELECT user_id FROM users WHERE ... = $var" 的语句,其中 `$var` 是一个整数,并且只是执行查询,那么获取的结果将是字符串类型。(例如,在 `var_dump` 中,对于某一行,["user_id"]=> string(1) "6")
这只是我从我的项目中观察到的,希望是正确的。
laurence dot mackenzie at stream dot com
10 年前
我刚刚在使用 `bind_param()` 和反射类时遇到了非常奇怪的行为。我认为我应该在这里发布它,以防其他遇到同样问题的人能够节省一小时的时间(就像我刚刚做的那样)。

首先,一些背景信息:我有一组类,每个类对应一个文件格式(例如 CSV、HTML 表格等),这些类将数据从平面文件导入到我数据库中的临时表中。然后,该类将数据转换为 3NF。

我使用反射类将数组传递给 `mysqli->bind_param()`,因为列数和类型是可变的。我遇到的问题代码(简化版)如下:

<?php

/* 代码循环遍历平面文件中的行和列,并将 MySQLi 的 '类型' 字母追加到
* `$typeString` 变量中,并将实际值
* 追加到 `$data` 数组中。我省略了代码,因为它
* (可能)不相关,并且会使帖子变得臃肿。
*/
$stmtInsert = $db->prepare('INSERT.....');
$typeString = 'ississis';
$data = array(1, 'two', 'three', 4, 'five', 'six', 7, 'eight');

/* 这就是奇怪的事情开始发生的地方
*/

// 将参数类型与参数值合并
$data = array_merge((array) $typeString, $data);

// 创建反射类
$ref = new \ReflectionClass('mysqli_stmt');

// 获取 `bind_param` 方法
$method = $ref->getMethod('bind_param');

// 使用 `$data` 调用它
$method->invokeArgs($stmtInsert, $data);

// 执行语句
$stmtInsert->execute();

}
?>

奇怪的是,在一个(也是唯一一个)情况下,它开始抛出 "Warning: Parameter 41 to mysqli_stmt::bind_param() expected to be a reference, value given"。反射类抛出异常。其他使用此代码的导入集都能正常工作。参数 41 是最后一个参数。通过如下方式更改受影响的代码解决了此问题:

<?php

$ref
= new \ReflectionClass("mysqli_stmt");
$method = $ref->getMethod("bind_param");
$data[count($data)-1] = (string) $data[count($data)-1];
$method->invokeArgs($stmtInsert, $data);
$stmtInsert->execute();

?>

不知道这里发生了什么,但就像我说的,希望这能让下一个遇到同样问题的人不再觉得自己完全疯了。
alex dot deleyn at gmail dot com
13 年前
MySQL 有一个“空值安全的等于”运算符(我猜是自 5.0 版本开始的)。
https://dev.mysqlserver.cn/doc/refman/5.0/en/comparison-operators.html#operator_equal-to

如果你使用这个运算符而不是通常的 =,你可以在 where 子句中互换值和空值。

但是,在使用此运算符与 datetime 或 timestamp 字段时,存在一个已知的错误:http://bugs.mysql.com/bug.php?id=36100
Anonymous
16 年前
值得注意的是,你必须一次性绑定所有参数 - 你不能逐个调用 `bind_param`。
To Top