PHP Conference Japan 2024

mysqli_stmt::bind_result

mysqli_stmt_bind_result

(PHP 5、PHP 7、PHP 8)

mysqli_stmt::bind_result -- mysqli_stmt_bind_result将变量绑定到预处理语句以进行结果存储

描述

面向对象风格

public mysqli_stmt::bind_result(混合 &$var, 混合 &...$vars): 布尔值

过程化风格

mysqli_stmt_bind_result(mysqli_stmt $statement, 混合 &$var, 混合 &...$vars): 布尔值

将结果集中的列绑定到变量。

当调用 mysqli_stmt_fetch() 获取数据时,MySQL 客户端/服务器协议会将绑定列的数据放入指定的变量 var/vars 中。

可以在任何时间绑定或重新绑定列,即使在部分检索结果集之后也可以。新的绑定将在下次调用 mysqli_stmt_fetch() 时生效。

注意:

mysqli_stmt_execute() 之后以及调用 mysqli_stmt_fetch() 之前,必须绑定所有列。

注意:

根据列类型,绑定变量可能会静默更改为相应的 PHP 类型。

提示

此函数对于简单结果很有用。要检索可迭代的结果集,或将每一行作为数组或对象获取,请使用 mysqli_stmt_get_result()

参数

statement

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

var

要绑定的第一个变量。

vars

要绑定的其他变量。

返回值

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

示例

示例 #1 面向对象风格

<?php

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

/* 准备语句 */
$stmt = $mysqli->prepare("SELECT Code, Name FROM Country ORDER BY Name LIMIT 5");
$stmt->execute();

/* 将变量绑定到预处理语句 */
$stmt->bind_result($col1, $col2);

/* 获取值 */
while ($stmt->fetch()) {
printf("%s %s\n", $col1, $col2);
}

示例 #2 过程化风格

<?php

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

/* 准备语句 */
$stmt = mysqli_prepare($link, "SELECT Code, Name FROM Country ORDER BY Name LIMIT 5");
mysqli_stmt_execute($stmt);

/* 将变量绑定到预处理语句 */
mysqli_stmt_bind_result($stmt, $col1, $col2);

/* 获取值 */
while (mysqli_stmt_fetch($stmt)) {
printf("%s %s\n", $col1, $col2);
}

以上示例将输出类似以下内容

AFG Afghanistan
ALB Albania
DZA Algeria
ASM American Samoa
AND Andorra

参见

添加注释

用户贡献的注释 17 条注释

hamidhossain at gmail dot com
16 年前
很多人不喜欢 bind_result 如何与预处理语句一起使用!它要求您传递很长的参数列表,这些参数列表将在调用该函数时加载列值。

为了解决这个问题,我使用了 call_user_func_array 函数和 result_metadata 函数。这使得能够轻松地自动返回一个包含所有列结果的数组,这些结果存储在一个带有列名的数组中。

请不要忘记用您自己的凭据更改设置变量



<?php
$host
= 'localhost';
$user = 'root';
$pass = '1234';
$data = 'test';

$mysqli = new mysqli($host, $user, $pass, $data);
/* 检查连接 */
if (mysqli_connect_errno()) {
printf("连接失败: %s\n", mysqli_connect_error());
exit();
}

if (
$stmt = $mysqli->prepare("SELECT * FROM sample WHERE t2 LIKE ?")) {
$tt2 = '%';

$stmt->bind_param("s", $tt2);
$stmt->execute();

$meta = $stmt->result_metadata();
while (
$field = $meta->fetch_field())
{
$params[] = &$row[$field->name];
}

call_user_func_array(array($stmt, 'bind_result'), $params);

while (
$stmt->fetch()) {
foreach(
$row as $key => $val)
{
$c[$key] = $val;
}
$result[] = $c;
}

$stmt->close();
}
$mysqli->close();
print_r($result);
?>
nieprzeklinaj at gmail dot com
13 年前
我编写了一个函数,用于获取结果集中所有行 - 无论是普通结果集还是预处理结果集。

<?php
function fetch($result)
{
$array = array();

if(
$result instanceof mysqli_stmt)
{
$result->store_result();

$variables = array();
$data = array();
$meta = $result->result_metadata();

while(
$field = $meta->fetch_field())
$variables[] = &$data[$field->name]; // 传递引用

call_user_func_array(array($result, 'bind_result'), $variables);

$i=0;
while(
$result->fetch())
{
$array[$i] = array();
foreach(
$data as $k=>$v)
$array[$i][$k] = $v;
$i++;

// 不知道为什么,但是当我尝试使用 $array[] = $data 时,我在所有行中都得到了相同的结果
}
}
elseif(
$result instanceof mysqli_result)
{
while(
$row = $result->fetch_assoc())
$array[] = $row;
}

return
$array;
}
?>

只需传入结果集或已执行的语句调用它,你将获得所有已获取的行。
andrey at php dot net
19 年前
如果你选择 LOBs,请使用以下执行顺序,否则你可能会导致 mysqli 分配比实际使用更多的内存。

1)prepare()
2)execute()
3)store_result()
4)bind_result()

如果你跳过 3) 或交换 3) 和 4),那么 mysqli 将为该列的最大长度分配内存,对于 tinyblob 为 255,对于 blob 为 64k(仍然可以),对于 MEDIUMBLOB 为 16MB - 相当多,对于 LONGBLOB 为 4G(如果你有这么多的内存)。使用此顺序的查询在存在 LOB 时会稍微慢一些,但这是避免内存耗尽的代价。
quano
13 年前
如果我的结果中有一个 longtext 字段,整个页面将变为空白,没有任何错误提示。这是因为 PHP _崩溃_了。我花了一整个上午才找出原因。

显然,如果你有 longtext 字段,则必须在使用 bind_result 之前调用 store_result。

http://bugs.php.net/bug.php?id=47928
uramihsayibok, gmail, com
15 年前
注意想要返回结果数组的人 - 也就是说,查询的所有结果的数组,而不仅仅是一个接一个。

<?php

// 等等...
call_user_func_array(array($mysqli_stmt_object, "bind_result"), $byref_array_for_fields);

$results = array();
while (
$mysqli_stmt_object->fetch()) {
$results[] = $byref_array_for_fields;
}

?>
这将不起作用。$results 将包含一堆数组,但每个数组都将引用 $byref。

PHP 在这里优化了性能:与其将 $byref 数组复制到 $results 中,不如说你是在 *添加* 它。这意味着 $results 将包含一堆 $byrefs - 多次重复的相同数组。(所以你看到的是 $results 全部是查询中最后一项的副本。)

hamidhossain (2008 年 9 月 1 日) 展示了如何解决这个问题:在获取结果的循环中,你还必须遍历字段列表,在遍历过程中复制它们。实际上,逐个复制所有内容。

就我个人而言,我宁愿使用某种有效地复制数组的函数,而不是编写自己的代码。许多内置的数组函数不起作用,显然是使用引用而不是副本,但 array_map 和 create_function 的组合可以。

<?php

// 等等...
call_user_func_array(array($mysqli_stmt_object, "bind_result"), $byref_array_for_fields);

// 返回值的副本
$copy = create_function('$a', 'return $a;');

$results = array();
while (
$mysqli_stmt_object->fetch()) {
// array_map 在此处完成时将保留键,并且以此方式
$results[] = array_map($copy, $byref_array_for_fields);
}

?>

如果他们只是为预处理语句实现了 fetch_assoc 甚至 fetch_array,所有这些问题都将消失...
atulkashyap1 at hotmail dot com
15 年前
bind_result 也可以用于从函数返回变量数组,
我花了很长时间才弄清楚这一点,所以我想分享一下。



<?php
function extracting(){
$query="SELECT topic, detail, date, tags
FROM updates
ORDER BY date DESC
LIMIT 5 "
;
if(
$stmt = $this->conn->prepare($query)) {
$stmt->execute();
$stmt->bind_result($updates[0],$updates[1],$updates[2],$updates[3]);
$i=0;
while(
$stmt->fetch()){
$i++;
$name='t'.$i;
$
$name = array($updates[0],$updates[1],$updates[2],$updates[3]);
}
return array(
$t1,$t2,$t3,$t4,$t5,);
$stmt->close();
}
}
?>
[email protected]
9年前
对于那些试图将行绑定到数组的人,
<?php
$stmt
= $db->prepare('SELECT id, name, mail, phone, FROM contacts');
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($arr['id'], $arr['name'], $arr['mail'], $arr['phone']);
while (
$stmt->fetch()) {
$outArr[] = $arr;
}
$stmt->close();
return
$outArr;
?>
这将为您提供所有您请求的行,但由于后台代码中的一些错误(我听说PHP试图在这里节省内存),它们都将与第一个相同。

但这个有效
<?php
$stmt
= $db->prepare('SELECT id, name, mail, phone, FROM contacts');
$stmt->execute();
$stmt->store_result();
$stmt->bind_result($a,$b,$c,$d);
while (
$stmt->fetch()) {
$outArr[] = ['id' => $a, 'name' => $b, 'mail' => $c, 'phone' => $d];
}
$stmt->close();
return
$outArr;
?>
不要使用数组来绑定结果 :)
[email protected]
13 年前
为了澄清任何遇到数组问题的人,PHP会自动将数组作为引用传递,并在需要设置或取消设置其一部分时克隆数组,更改引用变量不会触发克隆。

这样做是为了提高效率,要克隆包含此信息的数组,您可以使用foreach循环,或设置/取消设置键。只要您不介意丢失键,像array_values这样的技术也能工作。
[email protected]
14年前
如果正确使用,“call_user_func_array()”可以将变量绑定到多列结果,包括BLOB列。

示例

<?php
$data
= array() ; // 接受数据的数组。
$params = array() ; // 传递给'bind_result()'的参数数组
$column = array("fidentity", "fvarchar", "fdate", "ftinyblob") ; // 列名。
foreach($column as $col_name)
{
// 'fetch()'会将获取的值赋给变量'$data[$col_name]'
$params[] =& $data[$col_name] ;
}
$res = call_user_func_array(array($stmt, "bind_result"), $params) ;
?>

这是完整的示例。
警告:当使用“prepare”准备检索LOB的语句时,方法顺序很重要。
此外,必须调用方法“store_result()”,并且必须按正确的顺序调用。
未能遵守此操作会导致PHP/MySQLi崩溃或返回错误值。
正确的过程顺序是:prepare -> execute -> store_result -> bind -> fetch

<?php
$database
= "test" ;
$table = "test" ;
$column = array("fidentity", "fvarchar", "fdate", "ftinyblob") ;
$select_set = "`fidentity`, `fvarchar`, `fdate`, `ftinyblob`" ;
$mysqli = new mysqli("localhost", "root", $password, $database);
// 正确的过程顺序:prepare -> execute -> store_result -> bind -> fetch
$stmt = $mysqli->prepare("SELECT $select_set FROM `$table`") ;
$stmt->execute();
$stmt->store_result();
$data = array() ; // 接受数据的数组。
$params = array() ; // 传递给'bind_result()'的参数数组
foreach($column as $col_name)
{
// 将获取的值赋给变量'$data[$name]'
$params[] =& $data[$col_name] ;
}
$res = call_user_func_array(array($stmt, "bind_result"), $params) ;
if(!
$res)
{
echo
"bind_result() failed: " . $mysqli->error . "\n" ;
}
else
{
$res = $stmt->fetch() ;
if(
$res)
{
echo
"<pre>" . htmlentities(print_r($data, true)) . "</pre>\n" ;
}
else
{
echo ((
false !== $res) ? "End of data" : $stmt->error) . "\n" ;
}
}
$stmt->close() ;
$mysqli->close() ;
exit ;
?>

上面的示例应该输出
Array (
[fidentity] => 24
[fvarchar] => the rain in spain
[fdate] => 2010-07-31
[ftinyblob] => GIF89a...(更多BLOB数据)
)
[email protected]
12年前
只想确保所有人都知道get_result,因为那些需要以数组格式获取结果的人。

在代码示例中,执行execute()后,执行get_result(),如下所示

<?php

// ... 此文档的示例代码:

$stmt->execute();

/* 替代 bind_result: */
$result = $stmt->get_result();

/* 现在您可以将结果提取到数组中 - 很棒 */
while ($myrow = $result->fetch_assoc()) {
printf("%s %s\n", $myrow['Code'], $myrow['Name']);
}

?>

当您的查询返回十几个或更多字段时,这会方便很多。希望这有帮助。另外,正如 get_result 的注释中所述,它需要 mysqlnd。
Masterkitano
9年前
对于没有 mysqlInd 驱动程序或由于某种原因无法使用 stmt->get_result 的用户,我编写了这个函数,它允许您“模拟” mysqli_result::fetch_assoc。

function fetchAssocStatement($stmt)
{
if($stmt->num_rows>0)
{
$result = array();
$md = $stmt->result_metadata();
$params = array();
while($field = $md->fetch_field()) {
$params[] = &$result[$field->name];
}
call_user_func_array(array($stmt, 'bind_result'), $params);
$stmt->fetch();
return $result;
}

return null;
}

您可以在 while 语句中使用它来从语句中提取并返回关联数组(只要语句处于打开状态)。
用法
$statement = $mysqli->prepare($query));
$statement.execute();
while($rowAssocArray = fetchAssocStatement($statement))
{
//执行某些操作
}

$statement.close();

希望这有帮助。
carloshritzmann at gmail dot com
2 年前
从 PHP 7.4+ 开始,您可以使用扩展运算符 (...) 来轻松自动化将变量分配给查询语句的过程,并在使用预处理语句执行 SELECT 请求时提取数据。

但是,扩展运算符会考虑数组的大小。因此,它必须在任何上下文中使用之前预先分配。

<?php
$link
= // 创建数据库连接

$query_text = "SELECT mission, year, report FROM table WHERE id=? AND name=?;";

// 要分配的数据
$types = "is";
$param = [0, "John Titor"];

// 准备并执行语句
$stmt = mysqli_stmt_prepare($link, $query_text);
if (
mysqli_stmt_execute($stmt)) {

// 我们知道我们将检索 3 列:Mission、Year 和 Report。
$output = [0, 0, 0];

// 在幕后,扩展运算符在使用时会产生这样的效果:
// mysqli_stmt_bind_result($link, $output[0], $output[1], $output[2]);
mysqli_stmt_bind_result($stmt, ...$output);

// 提取数据
while (mysqli_stmt_fetch($stmt)) {
echo
"Mission: " . $output[0] . "\n";
echo
"Year: " . $output[1] ."\n";
echo
"Report: " . $output[2] . "\n";
}

} else {
echo
"Error: 错误的时间线\n";
}

// 关闭数据库连接
?>
dev+php at alepe dot com
13 年前
根据以上文档
"根据绑定变量的列类型,它们可能会静默地更改为相应的 PHP 类型。"

如果您将字段指定为带 zerofill 属性的 int(tinyint、mediumint 等),它将(静默地)转换为 PHP 整数(删除前导零)。为了保留这些前导零,一种解决方案是将字段指定为 decimal。

请注意,这仅在使用预处理语句时发生,而不是在直接执行查询时发生。
Miguel Hatrick
15 年前
从这里获取了一些很酷的代码,并为那些面向对象的家伙创建了一个小类

使用方法如下

<?php
// 执行预处理语句
$stmt->execute();
$stmt->store_result();

// 自定义类 :D 绑定到语句结果 杂七杂八!
$sr = new Statement_Result($stmt);

$stmt->fetch();
printf("ID: %d\n", $sr->Get('id') );

/////////////////////////////////

class Statement_Result
{
private
$_bindVarsArray = array();
private
$_results = array();

public function
__construct(&$stmt)
{
$meta = $stmt->result_metadata();

while (
$columnName = $meta->fetch_field())
$this->_bindVarsArray[] = &$this->_results[$columnName->name];

call_user_func_array(array($stmt, 'bind_result'), $this->_bindVarsArray);

$meta->close();
}

public function
Get_Array()
{
return
$this->_results;
}

public function
Get($column_name)
{
return
$this->_results[$column_name];
}
}
?>
bb at servertje dot nl
16 年前
尽管受到早期帖子的启发,但此方法可以添加到您的任何数据库对象中。它是早期帖子的面向对象实现。

该方法返回一个包含表示行的对象的数组。每个属性代表一个列及其值。

<?php
private function getresult($stmt)
{
$result = array();

$metadata = $stmt->result_metadata();
$fields = $metadata->fetch_fields();

for (;;)
{
$pointers = array();
$row = new stdClass();

$pointers[] = $stmt;
foreach (
$fields as $field)
{
$fieldname = $field->name;
$pointers[] = &$row->$fieldname;
}

call_user_func_array(mysqli_stmt_bind_result, $pointers);

if (!
$stmt->fetch())
break;

$result[] = $row;
}

$metadata->free();

return
$result;
}
?>
brad dot jackson at resiideo dot com
19 年前
在从预处理语句绑定结果参数时存在一个潜在的问题,这些参数引用大型数据类型,如 mediumblobs。我们的一个数据库表包含一个二进制图像数据的表。我们在此表中最大的图像大约为 50Kb,但即使如此,该列也类型化为 mediumblob 以允许大于 64Kb 的文件。我花了令人沮丧的一个小时试图找出为什么 mysqli_stmt_bind_result 在尝试为最多应为 50Kb 的结果分配 16MB 的内存时会卡住,直到我意识到该函数首先检查列类型以找出可能检索到的结果有多大,并尝试分配这么多内存来包含它。我的解决方案是使用更基本的 mysqli_result() 查询。另一种选择可能是将图像数据列重新类型化为 blob(64Kb 限制)。
thejkwhosaysni at gmail dot com
19 年前
我创建了这些函数,它们将像 mysqli_fetch_array() 和 mysqli_fetch_object() 一样工作,但适用于绑定结果。

<?php
function fetch_object() {
$data = mysqli_stmt_result_metadata($this->stmt);
$count = 1; //从 1 开始计数。第一个值必须是 stmt 的引用。
$fieldnames[0] = &$this->stmt;
$obj = new stdClass;
while (
$field = mysqli_fetch_field($data)) {
$fn = $field->name; //获取所有字段名
$fieldnames[$count] = &$obj->$fn; //将字段名加载到对象中。
$count++;
}
call_user_func_array(mysqli_stmt_bind_result, $fieldnames);
mysqli_stmt_fetch($this->stmt);
return
$obj;
}

function
fetch_array() {
$data = mysqli_stmt_result_metadata($this->stmt);
$count = 1; //从 1 开始计数。第一个值必须是 stmt 的引用,因为 bind_param 需要将链接到 $stmt 作为第一个参数。
$fieldnames[0] = &$this->stmt;
while (
$field = mysqli_fetch_field($data)) {
$fieldnames[$count] = &$array[$field->name]; //将字段名加载到数组中。
$count++;
}
call_user_func_array(mysqli_stmt_bind_result, $fieldnames);
mysqli_stmt_fetch($this->stmt);
return
$array;

}

?>

希望这对某些人有所帮助,我对此困惑了一段时间。
To Top