如何在PHP中用多_查询生成预置语句

如何在PHP中用多_查询生成预置语句,php,mysql,mysqli,Php,Mysql,Mysqli,我目前正在尝试使用php和mysqli向mysql数据库发布一些关于用户的元数据。我检索一个数组,该数组中的键/值对与它们假定存储在数据库中的键/值对相对应 function insert_metadata_to_db ($user_id, $metaData) { $conn = mysqli_connect("127.0.0.1", "root", "", "test"); foreach($metaData as $key => $value) {

我目前正在尝试使用php和mysqli向mysql数据库发布一些关于用户的元数据。我检索一个数组,该数组中的键/值对与它们假定存储在数据库中的键/值对相对应

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()
优化得多

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操作符”)
解包绑定阵列的整个阵列

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();
}
旁注

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

为什么要使用多重查询?只要做尽可能多的查询就可以了。甚至
插入值(),(),()
。另外,占位符不能用引号括起来,请重新阅读手册。我想将其设置为多重查询,以避免对数据库进行10多次单独的插入查询。我想这会提高性能/减少开销。也使以后添加更多元数据变得更容易,而不必编辑此方法-并且只编辑数组。您仍然有10多个查询,但现在只有一个字符串,它们仍将逐个执行。我更喜欢您的第一种方法!虽然第二个可能会稍微快一点,但第一个更容易理解。谢谢。你的第一个方法正是我想要的。我马上上火车connection@Nick我同意!我也更喜欢第一种解决方案,但这只是为了表明它可以通过单个查询实现(如前所述,这不一定比第一种好多少…)。我还想补充一点,如果有许多插入,那么将它们包装在一个事务中是值得的。如果我可以回到sidenote@Qirel,我已经做了一些关于建立数据库连接的最佳实践的研究。好吧,你的建议是依赖注入,这似乎是最好的选择,但是你认为我应该在哪里创建全球连接呢?我在研究它时遇到了困难,因为我找到的每一个资源都是用oop编写的,而不是用过程式php编写的。