mysqli::prepare

mysqli_prepare

(PHP 5, PHP 7, PHP 8)

mysqli::prepare -- mysqli_prepare准备 SQL 语句以供执行

说明

面向对象风格

public mysqli::prepare(string $query): mysqli_stmt|false

过程式风格

mysqli_prepare(mysqli $mysql, string $query): mysqli_stmt|false

准备 SQL 查询,并返回一个语句句柄,用于对语句进行进一步操作。查询必须包含单个 SQL 语句。

语句模板可以包含零个或多个问号 (?) 参数标记——也称为占位符。参数标记必须使用 mysqli_stmt_bind_param() 绑定到应用程序变量,然后才能执行语句。

参数

mysql

仅限过程式风格:由 mysqli_connect()mysqli_init() 返回的 mysqli 对象

query

查询,作为字符串。它必须包含单个 SQL 语句。

SQL 语句可以包含零个或多个参数标记,这些参数标记由问号 (?) 字符在适当的位置表示。

注意:

标记仅在 SQL 语句中的某些位置合法。例如,它们在 INSERT 语句的 VALUES() 列表中是允许的(用于指定行列值),或者在与 WHERE 子句中的列进行比较以指定比较值时是允许的。但是,它们不允许用于标识符(如表名或列名)。

返回值

mysqli_prepare() 返回一个语句对象或 false 如果发生错误。

错误/异常

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

示例

示例 #1 mysqli::prepare() 示例

面向对象风格

<?php

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

$city = "Amersfoort";

/* 创建准备好的语句 */
$stmt = $mysqli->prepare("SELECT District FROM City WHERE Name=?");

/* 绑定参数以供标记 */
$stmt->bind_param("s", $city);

/* 执行查询 */
$stmt->execute();

/* 绑定结果变量 */
$stmt->bind_result($district);

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

printf("%s is in district %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_prepare($link, "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

参见

添加笔记

用户贡献的笔记 23 笔记

210
timchampion dot NOSPAM at gmail dot com
12 年前
只是想确保大家知道 get_result。

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

<?php

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

/* 绑定标记参数 */
$stmt->bind_param("s", $city);

/* 执行查询 */
$stmt->execute();

/* 不使用 bind_result: */
$result = $stmt->get_result();

/* 现在可以将结果获取到数组中 - 很棒 */
while ($myrow = $result->fetch_assoc()) {

// 使用 $myrow 数组,就像其他 fetch 一样
printf("%s is in district %s\n", $city, $myrow['district']);

}
?>

当您从查询中获取十多个字段时,这会方便很多。希望这有帮助。
61
Darren
12 年前
我为个人使用编写了这个函数,并决定分享出来。我不确定这个论坛是否合适,但如果我在使用 mysqli::prepare 时有这个函数,就好了。这个函数是我之前发布的函数的更新版本。之前的函数无法处理多个查询。

对于查询
单个查询的结果以数组形式给出 [行号] [关联数据数组]
多个查询的结果以数组形式给出 [查询号] [行号] [关联数据数组]

对于返回受影响行号的查询,将返回受影响的行号,而不是 (数组[行号] [关联数据数组])

代码和示例如下

<?php
function mysqli_prepared_query($link,$sql,$typeDef = FALSE,$params = FALSE){
if(
$stmt = mysqli_prepare($link,$sql)){
if(
count($params) == count($params,1)){
$params = array($params);
$multiQuery = FALSE;
} else {
$multiQuery = TRUE;
}

if(
$typeDef){
$bindParams = array();
$bindParamsReferences = array();
$bindParams = array_pad($bindParams,(count($params,1)-count($params))/count($params),"");
foreach(
$bindParams as $key => $value){
$bindParamsReferences[$key] = &$bindParams[$key];
}
array_unshift($bindParamsReferences,$typeDef);
$bindParamsMethod = new ReflectionMethod('mysqli_stmt', 'bind_param');
$bindParamsMethod->invokeArgs($stmt,$bindParamsReferences);
}

$result = array();
foreach(
$params as $queryKey => $query){
foreach(
$bindParams as $paramKey => $value){
$bindParams[$paramKey] = $query[$paramKey];
}
$queryResult = array();
if(
mysqli_stmt_execute($stmt)){
$resultMetaData = mysqli_stmt_result_metadata($stmt);
if(
$resultMetaData){
$stmtRow = array();
$rowReferences = array();
while (
$field = mysqli_fetch_field($resultMetaData)) {
$rowReferences[] = &$stmtRow[$field->name];
}
mysqli_free_result($resultMetaData);
$bindResultMethod = new ReflectionMethod('mysqli_stmt', 'bind_result');
$bindResultMethod->invokeArgs($stmt, $rowReferences);
while(
mysqli_stmt_fetch($stmt)){
$row = array();
foreach(
$stmtRow as $key => $value){
$row[$key] = $value;
}
$queryResult[] = $row;
}
mysqli_stmt_free_result($stmt);
} else {
$queryResult[] = mysqli_stmt_affected_rows($stmt);
}
} else {
$queryResult[] = FALSE;
}
$result[$queryKey] = $queryResult;
}
mysqli_stmt_close($stmt);
} else {
$result = FALSE;
}

if(
$multiQuery){
return
$result;
} else {
return
$result[0];
}
}
?>

示例
对于一个包含 firstName 和 lastName 的表格
John Smith
Mark Smith
Jack Johnson
Bob Johnson

<?php
// 单个查询,单个结果
$query = "SELECT * FROM names WHERE firstName=? AND lastName=?";
$params = array("Bob","Johnson");

mysqli_prepared_query($link,$query,"ss",$params)
/*
返回数组 (
0=> array('firstName' => 'Bob', 'lastName' => 'Johnson')
)
*/

// 单个查询,多个结果
$query = "SELECT * FROM names WHERE lastName=?";
$params = array("Smith");

mysqli_prepared_query($link,$query,"s",$params)
/*
返回数组 (
0=> array('firstName' => 'John', 'lastName' => 'Smith')
1=> array('firstName' => 'Mark', 'lastName' => 'Smith')
)
*/

// 多个查询,多个结果
$query = "SELECT * FROM names WHERE lastName=?";
$params = array(array("Smith"),array("Johnson"));

mysqli_prepared_query($link,$query,"s",$params)
/*
返回数组 (
0=>
array(
0=> array('firstName' => 'John', 'lastName' => 'Smith')
1=> array('firstName' => 'Mark', 'lastName' => 'Smith')
)
1=>
array(
0=> array('firstName' => 'Jack', 'lastName' => 'Johnson')
1=> array('firstName' => 'Bob', 'lastName' => 'Johnson')
)
)
*/
?>

希望这有帮助 =)
2
urso at email dot cz
一年前
不幸的是,使用 "/* bind result variables */ $stmt->bind_result($district);" 已过时且不建议使用。

<?php
$mysqli
= new mysqli("localhost", "test", "test", "test");
if (
$mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$secondname = "Ma%";
$types = "s";
$parameters = array($secondname);
$myquery = "select * from users where secondname like ?";
if (
$stmt = $mysqli->prepare($myquery)) {
$stmt->bind_param($types, ...$parameters);
$stmt->execute();
$result = $stmt->get_result();
$stmt->close();
$numrows = $result->num_rows;
while(
$row = $result->fetch_assoc()) {
echo
$row['firstname']." ".$row['secondname']."<br />";
}
}
$mysqli->close();
?>

此外,不要使用 `$stmt->bind_param("s", $city);`,而是使用 `$stmt->bind_param($types, ...$parameters);` 和数组。 这里使用数组 (`$parameters`) 的优势已经很明显了, 可以使用一个包含 5 个元素的数组,而不是 5 个变量。

<?php
$mysqli
= new mysqli("localhost", "test", "test", "test");
if (
$mysqli->character_set_name()!="utf8mb4") { $mysqli->set_charset("utf8mb4"); }
$uid = intval($_POST['uid']);
$length=15; $account = mb_substr(trim($_POST['account']),0,$length,"utf-8"); $account=strip_tags($account);
$length=50; $password = mb_substr(trim($_POST['password']),0,$length,"utf-8"); $password = password_hash($password, PASSWORD_DEFAULT);
$length=25; $prijmeni = mb_substr(trim($_POST['prijmeni']),0,$length,"utf-8"); $prijmeni=strip_tags($prijmeni);
$length=25; $firstname = mb_substr(trim($_POST['firstname']),0,$length,"utf-8"); $firstname=strip_tags($firstname); $firstname = str_replace(array(">","<",'"'), array("","",""), $firstname);
$dotaz = "UPDATE users SET account = ?, password = ?, secname = ?, firstname = ? WHERE uid = ?";
$types = "ssssi";
$parameters = array($account,$password,$prijmeni,$firstname,$uid);
if (
$stmt = $mysqli->prepare($dotaz)) {
$stmt->bind_param($types, ...$parameters);
$stmt->execute();
echo
$stmt->affected_rows;
$stmt->close();
}
$mysqli->close();
?>
7
kritz at hrz dot tu-chemnitz dot de
7 年前
我无法完全测试以下内容,因为我当前正在使用的服务器缺少允许我在 mysqli_stmt 上调用 get_result 的 PHP 模块,但也许这对于其他人会有用。

<?php

/**
* 带有附加函数的自定义 {@link \mysqli} 类。
*/
class CustomMysqli extends \mysqli
{
/**
* 创建一个预处理查询,绑定给定的参数并返回已执行的 {@link \mysqli_stmt} 的结果。
* @param string $query
* @param array $args
* @return bool|\mysqli_result
*/
public function queryPrepared($query, array $args)
{
$stmt = $this->prepare($query);
$params = [];
$types = array_reduce($args, function ($string, &$arg) use (&$params) {
$params[] = &$arg;
if (
is_float($arg)) $string .= 'd';
elseif (
is_integer($arg)) $string .= 'i';
elseif (
is_string($arg)) $string .= 's';
else
$string .= 'b';
return
$string;
},
'');
array_unshift($params, $types);

call_user_func_array([$stmt, 'bind_param'], $params);

$result = $stmt->execute() ? $stmt->get_result() : false;

$stmt->close();

return
$result;
}
}

$db = new CustomMysqli('host', 'user', 'password', 'database', 3306);
$result = $db->queryPrepared(
'SELECT * FROM table WHERE something = ? AND someotherthing = ? AND elsewhat = ?',
[
'dunno',
1,
'dontcare'
]
);

if (isset(
$result) && $result instanceof \mysqli_result) {
while (
null !== ($row = $result->fetch_assoc())) {
echo
'<pre>'.var_debug($row, true).'</pre>';
}
}

?>

注意:如果您想在低于 5.4 的 PHP 版本中使用它,则必须使用旧的丑陋的 `array()` 语法来表示数组,而不是简短的 `[]` 语法。
10
admin at xorath dot com
16 年前
对于那些好奇的人来说,这是一个性能提示。我进行了一项测试,首先插入了大约 30,000 个帖子,其中包含一个主键:id 和一个 varchar(20),其中 varchar 数据是当前迭代器值的 md5 哈希值,只是为了填充一些数据。

该测试是在一台配备了 Athlon 64 - 3000+ 处理器和 512MB 内存的专用 Ubuntu 7.04 服务器上进行的,该服务器运行着 Apache2/PHP5/MySQL5.0。使用从 0 到 30000 的 for 循环对查询进行测试,首先使用

<?php
for ( $i = 0; $i <= 30000; ++$i )
{
$result = $mysqli->query("SELECT * FROM test WHERE id = $i");
$row = $result->fetch_row();
echo
$row[0]; //打印 id
}
?>

这导致页面加载时间平均约为 3.3 秒,然后使用此循环

<?php
$stmt
= $mysqli->prepare("SELECT * FROM test WHERE id = ?");
for (
$i = 0; $i <= 30000; ++$i )
{
$stmt->bind_param("i", $i);
$stmt->execute();
$stmt->bind_result($id, $md5);
$stmt->fetch();
echo
$id;
}
$stmt->close();
?>

平均页面加载时间降低了 1.3 秒,这意味着平均约为 2.0 秒!可以推测,对于更复杂/更大的表格和更复杂的 SQL 查询,性能差异可能更大。
8
Codeguy
13 年前
在 SQL 中使用预处理语句的实际目的是降低处理查询的成本;而不是将数据与查询分离。这就是现在使用 PHP 的方式,而不是最初设计的方式。使用 SQL,您可以通过使用预处理语句来降低执行多个类似查询的成本。这样做会消除解析和验证,并且通常会预先为该查询生成一个执行计划。这就是为什么它们在循环中运行速度比它们的 IMMEDIATE Query 同类快的原因。不要假设仅仅因为有人以这种方式使用 PHP 和此函数就意味着它是 THE 方法,或者只是唯一的方法。虽然它比一般查询更安全,但它们在功能或更准确地说是在执行方式方面也更受限制。
6
omidbahrami1990 at gmail dot com
6 年前
这是一种安全使用 mysqli::prepare 的方法
--------------------------------------------------------
<?php
function secured_signup($username,$password)
{
$connection = new mysqli($dbhost,$dbusername,$dbpassword,$dbname);
if (
$connection->connect_error)
die(
"Secured");

$prepared = $connection->prepare("INSERT INTO `users` ( `username` , `password` ) VALUES ( ? , ? ) ; ");
if(
$prepared==false)
die(
"Secured");

$result=$prepared->bind_param("ss",$username,$password);
if(
$result==false)
die(
"Secured");

$result=$prepared->execute();
if(
$result==false)
die(
"Secured");

$prepared->close();
$connection->close();
}
/*
$dbhost ---> 数据库 IP 地址
$dbusername ---> 数据库用户名
$dbpassword ---> 数据库密码
$dbname ---> 数据库名称
*/
?>
7
cdtreeks at gmail dot com
9 年前
执行预处理的 MySQL 时,默认情况下,如果出现错误,您将从对 prepare() 的调用中简单地获得 FALSE 返回值。

要获取完整的 MySQL 错误,请在准备查询之前创建一个语句对象,如下所示

<?php
$mysqli
= new mysqli("localhost", "my_user", "my_password", "world");

/* 检查连接 */
if (mysqli_connect_errno()) {
printf("连接失败: %s\n", mysqli_connect_error());
exit();
}

$city = "Amersfoort";

/* 创建一个预处理语句 */
$statement = $mysqli->stmt_init();
if (
$statement->prepare("SELECT District FROM City WHERE Name=?")) {

/* 为标记绑定参数 */
$statement->bind_param("s", $city);

/* 执行查询 */
if (!$statement->execute()) {
trigger_error('执行 MySQL 查询时出错: ' . $statement->error);
}

/* 绑定结果变量 */
$statement->bind_result($district);

/* 获取值 */
$statement->fetch();

printf("%s 在 %s 区\n", $city, $district);

/* 关闭语句 */
$statement->close();
}

/* 关闭连接 */
$mysqli->close();
?>
5
David Kramer
18 年前
我认为这些不是好例子,因为预处理查询的主要用途是在您要在一个循环中调用同一个查询,每次插入不同的值时。例如,如果您要生成报告并需要为每一行运行相同的查询,调整 WHERE 子句中的值,或从另一个系统导入数据。
3
Darren
12 年前
对于那些第一次学习 mysqli::prepare 和 mysqli_stmt::bind_params 的人,这里有一段带注释的代码块,它执行预处理查询并以与 mysqli_query 返回值类似的格式返回数据。我尝试将不必要的类、对象或开销降到最低,原因有两个
1) 方便学习
2) 允许与 mysqli_query 相对互换使用

我的目标是降低任何开始使用这些函数系列的人的学习曲线。我绝不是一个经验丰富的编码器/脚本编写者,因此我相信有一些改进和可能的一些错误,但我希望没有=)

<?php
/*
函数: mysqli_prepared_query()
执行准备好的查询,并绑定参数
以数组格式返回数据

参数:
mysqli_link
mysqli_prepare 查询语句
mysqli_stmt_bind_param 参数列表,以数组形式:array($typeDefinitinonString, $var1 [, mixed $... ])

返回值:
当执行 SELECT、SHOW、DESCRIBE 或 EXPLAIN 语句时:以 resultArray[行号][关联字段名] 的形式返回表格数据
当执行其他查询时,返回受影响的行数
错误时返回 FALSE
*/
function mysqli_prepared_query($link,$sql,$bindParams = FALSE){
if(
$stmt = mysqli_prepare($link,$sql)){
if (
$bindParams){
$bindParamsMethod = new ReflectionMethod('mysqli_stmt', 'bind_param'); //允许使用可变参数列表调用 mysqli_stmt->bind_param
$bindParamsReferences = array(); //将用作 mysqli_stmt->bind_param 的参数列表

$typeDefinitionString = array_shift($bindParams);
foreach(
$bindParams as $key => $value){
$bindParamsReferences[$key] = &$bindParams[$key];
}

array_unshift($bindParamsReferences,$typeDefinitionString); //将 typeDefinition 作为字符串的第一个元素返回
$bindParamsMethod->invokeArgs($stmt,$bindParamsReferences); //使用 $bindParamsRereferences 作为参数列表调用 mysqli_stmt->bind_param
}
if(
mysqli_stmt_execute($stmt)){
$resultMetaData = mysqli_stmt_result_metadata($stmt);
if(
$resultMetaData){
$stmtRow = array(); //这将是从 mysqli_stmt_fetch($stmt) 返回的结果行
$rowReferences = array(); //这将引用 $stmtRow 并传递给 mysqli_bind_results
while ($field = mysqli_fetch_field($resultMetaData)) {
$rowReferences[] = &$stmtRow[$field->name];
}
mysqli_free_result($resultMetaData);
$bindResultMethod = new ReflectionMethod('mysqli_stmt', 'bind_result');
$bindResultMethod->invokeArgs($stmt, $rowReferences); //使用面向对象风格调用 mysqli_stmt_bind_result($stmt,[$rowReferences])
$result = array();
while(
mysqli_stmt_fetch($stmt)){
foreach(
$stmtRow as $key => $value){ //变量必须按值赋值,因此 $result[] = $stmtRow 不起作用(不太确定为什么,与 $stmtRow 中的引用有关)
$row[$key] = $value;
}
$result[] = $row;
}
mysqli_stmt_free_result($stmt);
} else {
$result = mysqli_stmt_affected_rows($stmt);
}
mysqli_stmt_close($stmt);
} else {
$result = FALSE;
}
} else {
$result = FALSE;
}
return
$result;
}

?>

希望 PHP 神不会惩罚我。
1
Bernie van't Hof
12 年前
准备好的语句在开始时很令人困惑..

mysqli->prepare() 返回一个所谓的语句对象,用于后续操作,例如执行、绑定参数、存储结果、绑定结果、获取等。

语句对象具有私有属性,这些属性在执行每个语句操作时会更新。我发现这些属性对于理解编写准备好的语句函数时正在发生的事情很有用。

affected_rows
insert_id
num_rows
param_count
field_count
errno
error
sqlstate
id

但我花了些时间才理解如何访问它们。

<?php
$stmt
= $mysqli->prepare($query);

// .. $stmt-> 操作 ..

var_dump($stmt); // 显示空值

var_dump($stmt->errno); // 注意字面量,显示值

// .. $stmt-> 操作 ..

// 要保留副本 ..
// get_object_properties() 不起作用
// clone() 不起作用
$properties = array();
foreach (
$stmt as $name => $priv){
$properties[$name] = $stmt->$name; // 起作用
// $properties[$name] = $priv; // 不起作用,foreach 无法访问私有属性
}

$stmt->close();
// var_dump($stmt->errno) // 不起作用,$stmt 已关闭
?>
3
REz
9 年前
没有参考资料表明所有数据都必须在新的 prepare 调用到 msqli 之前被获取,唯一的帮助是在一个 6 年前的评论中!
您必须调用 myslqi_stmt::fetch() 获取数据,直到返回 NULL,然后才能再次调用 mysqli::prepare(),而不会在 mysqli::$errno 和 mysqli::$error 中出现 FALSE 和任何错误。
3
Adam
18 年前
准备好的语句的目的是不将数据包含在您的 SQL 语句中。将数据包含在您的 SQL 语句中是不安全的。始终使用准备好的语句。它们使用起来更简洁(代码更易于阅读)且不易受到 SQL 注入的攻击。

转义字符串以包含在 SQL 语句中在某些语言环境中效果不佳,因此不安全。
5
codeFiend <aeontech at gmail dot com>
18 年前
请注意,参数标记周围的单引号将阻止您的语句正确准备。
例如

<?php
$stmt
= $mysqli->prepare("INSERT INTO City (District) VALUES ('?')");
echo
$stmt->param_count." parameters\n";
?>
将打印 0,并在尝试将变量绑定到它时失败,并显示“准备好的语句中的变量数量与参数数量不匹配”警告。

但是

<?php
$stmt
= $mysqli->prepare("INSERT INTO City (District) VALUES (?)");
echo
$stmt->param_count." parameters\n";
?>
将打印 1,并正常运行。

非常令人恼火,我花了一个小时才弄清楚。
1
wapharshitsingh at gmail dot com
3 年前
只是想在这里分享一下,如何有效地结合使用 ... 操作符来使用准备好的语句。
<?php
class Database{
private function
getTypeofValues($string, $value){
if(
is_float($value)){
$string .= "d";
}elseif(
is_integer($value)){
$string .= "i";
}elseif(
is_string($value)){
$string .= "s";
}else{
$string .= "b";
}
return
$string;
}
public function
makeQuery($query, array $values){
$stmt = $this->connection->prepare($query);
$type = array_reduce($values, array($this, "getTypeOfValues"));
$stmt->bind_param($type, ...$values);
$stmt->execute();
$result = $stmt->get_result();
return
$result;
}
}
?>
-1
rafael at stiod dot com
15 年前
所有数据都必须在新的语句准备之前被获取。
-2
@runspired
11 年前
我认为这不是一个错误,而是一个意外的行为。在构建 API 时,我发现将 INT 0 传递给预处理语句而不是字符串 '0' 会导致我的脚本内存不足并在网页上产生 500 错误。

下面是该问题的简化示例:($_DB 是对 mysqli 连接的全局引用)

<?php
function getItem( $ID ) {

$_STATEMENT = $_DB->prepare("SELECT item_user, item_name, item_description FROM item WHERE item_id = ?;");

$_STATEMENT->bind_param( 'i' , $ID );

$_STATEMENT->execute();
$_STATEMENT->store_result();

$_STATEMENT->bind_result( $user , $name , $description);
$result = $_STATEMENT->fetch();

$_STATEMENT->free_result();
$_STATEMENT->close();

return
$result;
}

getItem(0); //失败!
getItem('0'); //成功!

?>

我最好的猜测是 INT 0 被翻译成 BOOLEAN,如果真是这样,应该在上面记录,但所有获取错误信息(通过 php 脚本)的努力都失败了。
-2
sdepouw at NOSPAM dot com
15 年前
我不知道这对其他人来说有多明显,但如果你尝试为数据库当前指向的数据库中不存在的表准备一个查询(或者你的查询在其他方面无效,我认为),不会返回对象。我只有在深入挖掘时才注意到这一点,当时我一直收到一个致命错误,说我的语句变量没有设置为对象的实例(它可能为空)。

将 NOSPAM 替换为 nimblepros 以给我发邮件。
-4
Zeebuck
13 年前
我认为它最初的构建目的和人们今天使用的目的已经背离了。但为什么要纠结于最初的目的呢?显然,今天在预处理语句中投入了更多代码,使其可以用来防止 SQL 注入,因此它现在是今天的设计目的的一部分,以及对可重复语句的性能。
-4
marmstro at gmail dot com
10 年前
如果你的 IDE 在你使用传统的 prepare 时无法识别 $stmt 作为 mysqli_stmt 类型的对象

$stmt = mysqli_prepare($link, $query);

以下代码有效且对 IDE 友好

$stmt = new mysqli_stmt($link, $query);
-6
prwexler at earthlink dot net
13 年前
实际上,准备语句的目的是将用户输入与 SQL 命令分离。这通过防止用户执行自己的 SQL 代码并破坏数据库的完整性来提高安全性。
-9
nom0ny at yahoo dot com
17 年前
在描述中必须说明开发人员是否应该在对同一个语句变量再次执行 mysqli_prepare 之前调用 mysqli_stmt_close。

例如,脚本 A 调用 mysqli_stmt_close 两次
<?php
/* 脚本 A -- 我们已经连接到数据库 */

$stmt = mysqli_prepare($link, "INSERT INTO table VALUES (?, ?, 100)"); /* 查询 1 */
mysqli_stmt_bind_param($stmt, "si", $string, $integer);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt); // 关闭 $stmt

$stmt = mysqli_prepare($link, "INSERT INTO table VALUES ('PHP', ?, ?)"); /* 查询 2 */
mysqli_stmt_bind_param($stmt, "ii", $integer, $code);
mysqli_stmt_execute($stmt);
mysqli_stmt_close($stmt); // 关闭 $stmt

/* 脚本 A -- 继续... */
?>

接下来,我们有脚本 B,它在发出对先前语句的 mysqli_stmt_close 之前再次调用 mysqli_prepare。
<?php
/* 脚本 B -- 我们已经连接到数据库 */

$stmt = mysqli_prepare($link, "INSERT INTO table VALUES (?, ?, 100)"); /* 查询 1 */
mysqli_stmt_bind_param($stmt, "si", $string, $integer);
mysqli_stmt_execute($stmt);

$stmt = mysqli_prepare($link, "INSERT INTO table VALUES ('PHP', ?, ?)"); /* 查询 2 */
mysqli_stmt_bind_param($stmt, "ii", $integer, $code);
mysqli_stmt_execute($stmt);

mysqli_stmt_close($stmt); // 关闭 $stmt

/* 脚本 B -- 继续... */
?>

哪种方法更有效率,应该由开发人员使用?
-9
Ulf Wostner
18 年前
以下是一个使用 bind_param 和 bind_result 的示例,展示了对城市列表的迭代。

请注意,在查询对某些参数值返回 NULL 的情况下存在一些潜在的错误,
但 bind_result 变量可能仍然绑定。因此,我们使用条件语句来首先喷洒该位置。

$mysqli->select_db("world");

$template = "SELECT District, CountryCode FROM City WHERE Name=?";
printf("从模板准备语句:%s\n", $template);

$cities = array('San Francisco', 'Lisbon', 'Lisboa', 'Marrakech', 'Madrid');
printf("城市:%s\n", join(':', $cities));

if ($stmt = $mysqli->prepare($template)) {

foreach($cities as $city) {
// 将字符串 $city 绑定到 '?'
$stmt->bind_param("s", $city);
$stmt->execute();
// 绑定结果变量
$stmt->bind_result($d,$cc);
// 'Lisbon' 在 world.City 表中没有找到,但 'Lisboa' 找到了。
// 使用条件语句,我们避免将里斯本放在加州。
if($stmt->fetch()) {
printf("%s 位于 %s, %s\n", $city, $d, $cc);
}

}
$stmt->close();
}

使用条件语句,我们得到了期望的结果

从模板准备语句:SELECT District,CountryCode FROM City WHERE Name=?
城市:San Francisco:Lisbon:Lisboa:Marrakech:Madrid

旧金山位于加利福尼亚州,美国
里斯本位于里斯本,葡萄牙
马拉喀什位于马拉喀什-坦西夫特-阿尔,摩洛哥
马德里位于马德里,西班牙

但是,如果没有条件语句,我们将把里斯本放在加利福尼亚州

旧金山位于加利福尼亚州,美国
里斯本位于加利福尼亚州,美国
里斯本位于里斯本,葡萄牙
马拉喀什位于马拉喀什-坦西夫特-阿尔,摩洛哥
马德里位于马德里,西班牙
To Top