当前位置: 首页> 最新文章列表> 使用 mysqli::stmt_init 后如果忘记调用 prepare() 会带来哪些问题?

使用 mysqli::stmt_init 后如果忘记调用 prepare() 会带来哪些问题?

M66 2025-05-29

在使用 PHP 的 mysqli 扩展进行数据库操作时,预处理语句是一种常见且安全的做法。开发者通常会使用 mysqli::stmt_init() 来初始化一个语句对象,然后通过 prepare() 方法准备 SQL 语句。然而,如果在调用 stmt_init() 后忘记执行 prepare(),将会导致一些隐性问题,本文将对此进行分析和举例说明。

一、stmt_init()prepare() 的基本用法

通常,我们的代码结构是这样的:

$mysqli = new mysqli("localhost", "user", "password", "database");
$stmt = $mysqli->stmt_init();
$stmt->prepare("SELECT * FROM users WHERE id = ?");

其中:

  • stmt_init() 初始化一个 mysqli_stmt 对象;

  • prepare() 准备一个 SQL 语句,并与该对象绑定。

二、如果忘记调用 prepare() 会发生什么?

如果你只调用了 stmt_init() 而没有调用 prepare(),程序在后续使用该语句对象时将出现以下几类问题:

1. 方法调用错误

在未调用 prepare() 的情况下直接调用 bind_param()execute() 等方法,会导致抛出警告甚至致命错误:

$mysqli = new mysqli("localhost", "user", "password", "database");
$stmt = $mysqli->stmt_init();
// 忘记调用 prepare()

$stmt->bind_param("i", $userId); // 这里将报错
$stmt->execute();                // 这里也将失败

错误提示可能是:

Fatal error: Uncaught Error: Call to a member function bind_param() on bool

或者:

Warning: mysqli_stmt::bind_param(): invalid object or not initialized

2. 无法发现语法错误

如果你只是初始化了语句对象却没有 prepare(),那么原本可以在 prepare() 阶段发现的 SQL 错误将被隐藏,直到执行时才可能导致不明确的错误。这会增加调试成本。

3. 安全风险

忘记使用 prepare() 意味着你可能直接拼接 SQL 语句,这将绕过参数绑定的机制,从而增加 SQL 注入风险。例如:

// 错误示例(没有 prepare)
$userId = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $userId";
$result = $mysqli->query($query);

如果没有 prepare() 和参数绑定,用户输入将直接嵌入 SQL 中,攻击者可以构造如下 URL:

https://m66.net/get_user.php?id=1 OR 1=1

将导致数据泄露。

三、正确的做法

始终配对使用 stmt_init()prepare(),如下所示:

$mysqli = new mysqli("localhost", "user", "password", "database");

$stmt = $mysqli->stmt_init();
if ($stmt->prepare("SELECT * FROM users WHERE id = ?")) {
    $stmt->bind_param("i", $userId);
    $stmt->execute();
    $result = $stmt->get_result();
    while ($row = $result->fetch_assoc()) {
        echo $row['username'] . "<br>";
    }
    $stmt->close();
} else {
    echo "SQL prepare 错误: " . $mysqli->error;
}

四、总结

忘记调用 prepare() 会导致以下问题:

  • 调用其他语句方法时出现错误;

  • SQL 错误无法提前被捕捉;

  • 增加 SQL 注入等安全风险。

建议在使用 stmt_init() 后总是紧接着调用 prepare(),并对其返回值进行判断,确保程序的稳定性与安全性。