如何在 PHP 中使用 multi_query 制作准备好的语句

How to make prepared statement with multi_query in PHP

我目前正在尝试 post 使用 php 和 mysqli 将有关用户的一些元数据发送到 mysql 数据库。我检索了一个数组,其中 key/value 对对应于 key/value 对,它们应该存储在数据库中。

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = mysqli_connect("127.0.0.1", "root", "", "test");

    foreach($metaData as $key => $value) {
        $metaDataSql .= "insert into usermeta (user_id, meta_key, meta_value) values ($user_id, '$key', '$value');";
    }

    if(!$conn->multi_query($metaDataSql)) {
        echo("Failed to execute the queries." . $conn->error);
    }

    mysqli_close($conn);
}

我的下一步是避免 SQL 注入。我已经尝试过,但失败了,用准备好的语句在上面制作了这个方法。这是我的尝试:

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = mysqli_connect("127.0.0.1", "root", "", "test");

    foreach($metaData as $key => $value) {
        $metaDataSql .= $conn->prepare("insert into usermeta (user_id, meta_key, meta_value) values (?, '?', '?');");
        $metaDataSql->bind_param("iss", $user_id, $key, $value);
    }

    if(!$conn->multi_query($metaDataSql)) {
        echo("Failed to execute the queries." . $conn->error);
    }

    mysqli_close($conn);
}

这样可以吗?如果不是 - 处理多次插入数据库同时保持安全免受 SQL 注入的最佳方法是什么。

您无法准备 mysqli::multi_query - 但使用准备好的语句,您可以迭代相同的查询,只是使用不同的值。这比迭代和 运行 标准 query().

更优化

mysqli_stmt::bind_param()是引用,所以你只需要绑定一次,然后每次迭代都执行它。

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = new mysqli("127.0.0.1", "root", "", "test");
    $stmt = $conn->prepare("INSERT INTO usermeta (user_id, meta_key, meta_value) VALUES (?, ?, ?");
    $stmt->bind_param("iss", $user_id, $key, $value);
    foreach($metaData as $key => $value) {
        $stmt->execute();
    }
    $stmt->close();
    $conn->close();
}

如果您只想执行一次,也可以构建单个查询。这不一定比上面快很多,但可以稍微快一点。使用解包运算符(或 "splat operator")... 在绑定它的位置解包整个数组。

function insert_metadata_to_db ($user_id, $metaData) {
    $conn = new mysqli("127.0.0.1", "root", "", "test");
    $query = "INSERT INTO usermeta (user_id, meta_key, meta_value) VALUES ";
    $data = [];

    // Build the query, by appending values and inserting into the $data array
    foreach($metaData as $key => $value) {
        $query .= "(?, ?, ?), ";
        $data[] = $user_id;
        $data[] = $key;
        $data[] = $value;
    }

    // Trim away trailing comma
    $query = rtrim($query, ",");

    // If there are any data to bind, we can execute the query
    if (count($data)) {
        $stmt = $conn->prepare($query);
        $stmt->bind_param(str_repeat("iss", count($metaData)), ...$data);
        $stmt->execute();
        $stmt->close();
    }
    $conn->close();
}

旁注
在函数内初始化连接通常是一个非常糟糕的主意!您应该改为将全局连接作为参数传递给该函数。现在,每次使用该功能时都会建立一个新连接。