PHP Conference Japan 2024

mysqli_stmt::prepare

mysqli_stmt_prepare

(PHP 5, PHP 7, PHP 8)

mysqli_stmt::prepare -- mysqli_stmt_prepare準備執行 SQL 陳述式

說明

物件導向風格

public mysqli_stmt::prepare(字串 $query): 布林值

程序風格

mysqli_stmt_prepare(mysqli_stmt $statement, string $query): bool

準備執行一條敘述。該查詢必須由單個 SQL 敘述組成。

敘述範本可以包含零個或多個問號 (?) 參數標記,也稱為佔位符。在執行敘述之前,必須使用 mysqli_stmt_bind_param() 將參數標記綁定到應用程式變數。

注意事項:

如果傳遞給 mysqli_stmt_prepare() 的敘述長度超過伺服器的 max_allowed_packet,則根據您使用的是 MySQL 原生驅動程式 (mysqlnd) 還是 MySQL 用戶端程式庫 (libmysqlclient),返回的錯誤碼會有所不同。其行為如下:

  • Linux 上的 mysqlnd 會返回錯誤碼 1153。錯誤訊息表示「收到一個大於 max_allowed_packet 位元組的封包」。

  • Windows 上的 mysqlnd 會返回錯誤碼 2006。此錯誤訊息表示「伺服器已斷線」。

  • 所有平台上的 libmysqlclient 都會返回錯誤碼 2006。此錯誤訊息表示「伺服器已斷線」。

參數

statement

僅限程序式風格:由 mysqli_stmt_init() 返回的 mysqli_stmt 物件。

query

查詢,以字串表示。它必須由單個 SQL 敘述組成。

SQL 敘述可以包含零個或多個由問號 (?) 字元在適當位置表示的參數標記。

注意事項:

標記僅在 SQL 敘述中的某些位置合法。例如,它們允許在 INSERT 敘述的 VALUES() 列表中(用於指定一列的欄值),或在 WHERE 子句中與欄的比較中用於指定比較值。但是,它們不允許用於識別符號(例如表格或欄名稱)。

返回值

成功時返回 true,失敗時返回 false

錯誤/例外

如果啟用了 mysqli 錯誤報告 (MYSQLI_REPORT_ERROR) 且請求的操作失敗,則會產生警告。此外,如果模式設定為 MYSQLI_REPORT_STRICT,則會改為拋出 mysqli_sql_exception

範例

範例 #1 mysqli_stmt::prepare() 範例

物件導向風格

<?php

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

$city = "Amersfoort";

// 建立預備語句
$stmt = $mysqli->stmt_init();
$stmt->prepare("SELECT District FROM City WHERE Name=?");

// 綁定參數
$stmt->bind_param("s", $city);

// 執行查詢
$stmt->execute();

// 綁定結果變數
$stmt->bind_result($district);

// 获取值
$stmt->fetch();

printf("%s 位於 %s 區\n", $city, $district);

程序風格

<?php

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

$city = "Amersfoort";

/* 建立一個預處理語句 */
$stmt = mysqli_stmt_init($link);
mysqli_stmt_prepare($stmt, "SELECT District FROM City WHERE Name=?");

/* 綁定佔位符號的參數 */
mysqli_stmt_bind_param($stmt, "s", $city);

/* 執行查詢 */
mysqli_stmt_execute($stmt);

/* 綁定結果變數 */
mysqli_stmt_bind_result($stmt, $district);

/* 取得值 */
mysqli_stmt_fetch($stmt);

printf("%s is in district %s\n", $city, $district);

以上範例會輸出

Amersfoort is in district Utrecht

另請參閱

新增註釋

使用者貢獻的註釋 9 則註釋

logos-php at kith dot org
12 年前
請注意,如果您在 MySQL 查詢中使用問號作為字串值的佔位符,則不需要用引號將其括起來。

例如,應該這樣做:

mysqli_stmt_prepare($stmt, "SELECT * FROM foo WHERE foo.Date > ?");

不要這樣做:

mysqli_stmt_prepare($stmt, "SELECT * FROM foo WHERE foo.Date > '?'");

如果您在查詢中將問號用引號括起來,PHP 就無法將問號識別為佔位符,當您嘗試使用 mysqli_stmt_bind_param() 時,它會產生參數數量錯誤的錯誤訊息。

這個頁面的官方範例中隱含了字串佔位符不需使用引號,但文件中並沒有明確說明,我花了一些時間才弄清楚,所以覺得值得發佈出來。
logos-php at kith dot orgpp
12 年前
事實證明,您不能直接在 IN() 子句中使用帶有佔位符的預備語句。

有一些解決方法(例如建構一個由 n 個問號以逗號分隔的字串,然後在 IN() 子句中使用該組佔位符),但您不能直接使用 IN (?)。

這是 MySQL 的限制,而不是 PHP 的限制,但在 MySQL 文件中也沒有真正說明,所以我覺得值得在這裡提一下。

(順帶一提,原來之前有人發佈過我上則留言中的資訊,關於不要使用引號。很抱歉重複了;不知道我怎麼錯過了之前的留言。)
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 時會稍微慢一些,但這是避免在幾秒鐘內耗盡記憶體的代價。
kontakt at arthur minus schiwon dot de
16 年前
如果您將佔位符用引號括起來,您會遇到類似「變數數量與預備語句中的參數數量不符」的警告(至少在 INSERT 語句中會這樣)。
ndungi at gmail dot com
15 年前
`prepare`、`bind_param`、`bind_result`、`fetch` result、`close` stmt 迴圈有時會很繁瑣。這裡有一個物件,當您只想在預備語句上執行 `preparedSelect` 時,它會為您處理所有 mysqli 的繁瑣細節。該方法將結果集作為二維關聯陣列返回,以 `select` 的欄位作為鍵。我沒有做足夠的錯誤檢查,它也可能有一些錯誤。請協助除錯和改進它。

我使用了來自 http://www.biblesql.net/sites/biblesql.net/files/bible.mysql.gz. 的 bible.sql 資料庫。

Baraka tele!(斯瓦希里語,祝你好運)

============================

<?php

class DB
{
public
$connection;

#establish db connection
public function __construct($host="localhost", $user="user", $pass="", $db="bible")
{
$this->connection = new mysqli($host, $user, $pass, $db);

if(
mysqli_connect_errno())
{
echo(
"Database connect Error : "
. mysqli_connect_error($mysqli));
}
}

#store mysqli object
public function connect()
{
return
$this->connection;
}

#run a prepared query
public function runPreparedQuery($query, $params_r)
{
$stmt = $this->connection->prepare($query);
$this->bindParameters($stmt, $params_r);

if (
$stmt->execute()) {
return
$stmt;
} else {
echo(
"Error in $statement: "
. mysqli_error($this->connection));
return
0;
}

}

# To run a select statement with bound parameters and bound results.
# Returns an associative array two dimensional array which u can easily
# manipulate with array functions.

public function preparedSelect($query, $bind_params_r)
{
$select = $this->runPreparedQuery($query, $bind_params_r);
$fields_r = $this->fetchFields($select);

foreach (
$fields_r as $field) {
$bind_result_r[] = &${$field};
}

$this->bindResult($select, $bind_result_r);

$result_r = array();
$i = 0;
while (
$select->fetch()) {
foreach (
$fields_r as $field) {
$result_r[$i][$field] = $$field;
}
$i++;
}
$select->close();
return
$result_r;
}


#takes in array of bind parameters and binds them to result of
#executed prepared stmt

private function bindParameters(&$obj, &$bind_params_r)
{
call_user_func_array(array($obj, "bind_param"), $bind_params_r);
}

private function
bindResult(&$obj, &$bind_result_r)
{
call_user_func_array(array($obj, "bind_result"), $bind_result_r);
}

#returns a list of the selected field names

private function fetchFields($selectStmt)
{
$metadata = $selectStmt->result_metadata();
$fields_r = array();
while (
$field = $metadata->fetch_field()) {
$fields_r[] = $field->name;
}

return
$fields_r;
}
}
#end of class

#An example of the DB class in use

$DB = new DB("localhost", "root", "", "bible");
$var = 5;
$query = "SELECT abbr, name from books where id > ?" ;
$bound_params_r = array("i", $var);

$result_r = $DB->preparedSelect($query, $bound_params_r);

#loop thru result array and display result

foreach ($result_r as $result) {
echo
$result['abbr'] . " : " . $result['name'] . "<br/>" ;
}

?>
mhradek AT gmail.com
16 年前
此函式和 call_user_func_array 函式的一個特別有用的改編

// $params 以 array($val=>'i', $val=>'d', etc...) 的形式傳送

function db_stmt_bind_params($stmt, $params)
{
$funcArg[] = $stmt;
foreach($params as $val=>$type)
{
$funcArg['type'] .= $type;
$funcArg[] = $val;
}
return call_user_func_array('mysqli_stmt_bind_param', $funcArgs);
}

感謝 'sned' 提供這段程式碼。
st dot john dot johnson at gmail dot com
17 年前
關於 lachlan76 先前提到的,只要在再次執行前指示資料庫移至下一個結果,就可以透過預備語句執行預存程序。

範例(呼叫預存程序五次)

<?php
for ($i=0;$i<5;$i++) {
$statement = $mysqli->stmt_init();
$statement->prepare("CALL some_procedure( ? )");

// 綁定、執行和綁定。
$statement->bind_param("i", 1);
$statement->execute();
$statement->bind_result($results);

while(
$statement->fetch()) {
// 處理您的結果。
}

$statement->close();

// 現在將 mysqli 連線移至新的結果。
while($mysqli->next_result()) { }
}
?>

如果包含最後一個語句,此程式碼應該可以執行,而不會出現惱人的「命令不同步」錯誤。
lukaszNOSPAMPLEASE at epas dot pl
16 年前
如果各位還沒發現的話,我有一些壞消息要告訴你們。
使用 mysqli_next_result() 的技巧只能防止在呼叫預存程序後連線斷線。
顯然您可以為預備的預存程序呼叫綁定參數,但在 mysqli_stmt_bind_result() 之後,您會從 mysqli_stmt_fetch() 取得混亂的記錄,至少在預存程序本身包含預備語句時會如此。
避免資料損毀的方法可能是在 mysqli_real_connect() 中指定 CLIENT_MULTI_STATEMENTS 旗標,如果它沒有被完全停用(據說這是基於安全考量)。另一個選項是使用 mysqli_multi_query(),但這樣就完全無法綁定。
lachlan76 at gmail dot com
18 年前
不要嘗試透過預備語句使用預存程序。

範例

<?php
$statement
= $mysqli->stmt_init();
$statement->prepare("CALL some_procedure()");
?>

如果您嘗試這樣做,它會在下一個查詢期間斷開連線而失敗。請改用 mysqli_multi_query()。

範例

<?php
$mysqli
->multi_query("CALL some_procedure()");
do
{
$result = $mysqli->store_result();

// 在此處進行您的處理工作

$result->free();
} while(
$mysqli->next_result());
?>

然而,這表示您無法綁定參數或結果。
To Top