PHP: mysqli_stmt->close() 影响 stmt 的早期副本

PHP: mysqli_stmt->close() affecting earlier copy of stmt

我正在制作一个函数,以便更轻松地在 sql 查询中使用准备好的语句。但是在处理它时,我 运行 遇到了一个 st运行ge 错误。 当你 运行 $response 的第一个 var_dump 下面的代码像我期望的那样打印 $stmt 变量,但是在关闭 $stmt 之后它给出了很多的警告并打印 $stmt 的 NULL 填充版本。我只将 $stmt 复制为 $response,所以我不希望 $response 在关闭 $stmt 时发生变化。 有人可以解释为什么会发生这种情况以及我该如何预防吗?

function sql_bug() {
    global $dbc; // Database connection
    $sql = "UPDATE users SET username = 'J0R1AN' WHERE id = 1"; // Test query
    if ($stmt = $dbc->prepare($sql)) {
        if ($stmt->execute()) {
            $response = $stmt->get_result();
            if ($response === false) {
                // Checks for e.g. UPDATE queries that return bool(false) instead of a mysqli_result, 
                // and sets $response to $stmt instead, to return more valuable information when using UPDATE
                $response = $stmt;
            }
            var_dump($response); // Prints expected $stmt variable
            $stmt->close(); // Somehow changes $response?
            var_dump($response); // Prints $stmt variable filled with NULLS
            return $response;
        }
    }
    return false;
}

变量赋值不会在 PHP 中创建对象的新副本。要创建副本,您需要使用 clone。简单示例:

$obj  = (object) ['a'=>42];
$new_obj = $obj;
$obj->a = 100;

var_dump($new_obj); 
// outputs 
// stdClass Object
// (
//     [a] => 100
// )

您已成为多种谬误的受害者。

  1. 您不需要close任何东西。 PHP 会为您完成。使用 close 只会使很多事情复杂化。
  2. 不要检查 prepareexecute 函数的 return 值。而是启用 mysqli 错误报告。 How to get the error message in MySQLi?
  3. 不要使用全局对象或将其使用限制在绝对最低限度。
  4. 您不需要在函数外部公开 mysqli_stmtmysqli_result 对象。执行语句并获取数据后,您可以丢弃它们。

如果我们想正确修复这个函数,我们可以这样做:

function sql_bug(\mysqli $dbc, string $sql, array $params = []): ?array {
    $stmt = $dbc->prepare($sql);
    if ($params) {
        // bind optional parameters if the query has variable input
        $stmt->bind_param(str_repeat("s", count($params)), ...$params);
    }
    $stmt->execute();
    $response = $stmt->get_result();
    if ($response) {
        // If the query returned results then fetch them into an array and return it
        return $response->fetch_all(MYSQLI_BOTH);
    }
    // return nothing if the query was successfully executed and it didn't produce results
    return null;
}

上述函数是一个通用函数,可以处理任何带参数和不带参数的 SQL 语句。如果查询是 INSERT 或 UPDATE,它不会 return 任何东西,如果它是 SELECT 它会 return 数组中的数据。无需复制或 return 内部对象。您正在编写一个函数来从 mysqli 内部抽象出来。