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(mixed &$var, mixed &...$vars): bool

程序式風格

mysqli_stmt_bind_result(mysqli_stmt $statement, mixed &$var, mixed &...$vars): bool

將結果集中的欄位綁定到變數。

當呼叫 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
函式 fetch($result)
{
$array = 陣列();

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

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

while(
$field = $meta->fetch_field())
$variables[] = &$data[$field->name]; // 以傳址方式傳遞

call_user_func_array(陣列($result, 'bind_result'), $variables);

$i=0;
while(
$result->fetch())
{
$array[$i] = 陣列();
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 年前
如果您選擇 LOB,請使用以下執行順序,否則可能會導致 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 會有一堆 $byref - 同一個陣列重複多次。(所以您看到的 $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
函式 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();
}
}
?>
kaspy at example dot com
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;
?>
請不要使用陣列來綁定結果 :)
scragar at gmail dot com
13 年前
為了釐清任何在陣列上遇到問題的人,PHP 會自動將陣列作為參考傳遞,如果需要設定或取消設定陣列的一部分,則會複製該陣列,更改參考變數不會觸發複製。

這是為了效率而做的,要複製包含此資訊的陣列,您可以使用 foreach 迴圈,或設定/取消設定一個鍵值。像 array_values 這樣的技巧也可以使用,前提是您不介意遺失鍵值。
pcc at pccglobal dot com
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() 失敗: " . $mysqli->error . "\n" ;
}
else
{
$res = $stmt->fetch() ;
if(
$res)
{
echo
"<pre>" . htmlentities(print_r($data, true)) . "</pre>\n" ;
}
else
{
echo ((
false !== $res) ? "資料結束" : $stmt->error) . "\n" ;
}
}
$stmt->close() ;
$mysqli->close() ;
exit ;
?>

以上範例應該輸出
陣列 (
[fidentity] => 24
[fvarchar] => the rain in spain
[fdate] => 2010-07-31
[ftinyblob] => GIF89a...(更多 BLOB 資料)
)
timchampion dot NOSPAM at gmail dot com
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 年前
對於沒有 mysqlnd 驅動程式或由於某些原因無法使用 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 個欄位:任務、年份和報告。
$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
"任務: " . $output[0] . "\n";
echo
"年份: " . $output[1] ."\n";
echo
"報告: " . $output[2] . "\n";
}

} else {
echo
"錯誤:錯誤的時間線\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 綁定到語句結果 mambo jambo!
$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
函式 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;
}

函式
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