【问题标题】:MySQL prepared statement with bulk insert带有批量插入的 MySQL 准备好的语句
【发布时间】:2015-10-12 23:16:33
【问题描述】:

我有一个模块允许管理员将一些用户(可能是多个)添加到一个组中。

PHP

$user_id  = $_POST["user_id"]; //this can be an array
$group_id = $_POST["group_id"];

$sql = "INSERT IGNORE INTO users_groups (user_id, group_id) 
        VALUES ($user_id[0],$group_id)";

for ( $i = 1; $i < count($user_id); $i++) 
{
    $sql .= ", ($user_id[$i],$group_id)"
}

如您所见,sql-query 取决于管理员选择了多少用户。

如何将这段代码用于准备好的语句,因为后变量来自用户(SQL 注入...)

更新

我有两个选择框:

  • 一个供用户选择(可以多选->数组):$_POST["user_id"]
  • 一个用于组选择(只能选择一个):$_POST["group_id"]

现在我想要一个准备好的 SQL 语句,用于将 user_idgroup_id 插入到多对多表 (users_groups)。问题是必须插入的值的数量可能会改变(取决于管理员在选择框中选择了多少用户)。

我想根据管理员选择的用户数量来更改准备好的查询。

例如:

  • 管理员选择了两个用户 -> sql:INSERT IGNORE INTO users_groups (user_id, group_id) VALUES ($user_id[0],$group_id), ($user_id[1],$group_id)
  • 管理员选择了四个用户 -> sql:INSERT IGNORE INTO users_groups (user_id, group_id) VALUES ($user_id[0],$group_id), ($user_id[1],$group_id), ($user_id[2],$group_id), ($user_id[3],$group_id)

我的问题:我怎样才能自动执行此操作并使用准备好的 sql 语句,因为我不想拥有 10 次 if(count($user_id) == number) {...

更新 2 如果我手动执行此操作,代码将如下所示:

$sql = $db->prepare("INSERT IGNORE INTO users_groups (user_id, group_id) VALUES (?, ?)"); 
$sql->bind_param('ii', $user_id[0], $group_id);

更新 3 检查post变量中是否只有整数:

$user_id = filter_input_array(INPUT_POST, 'user_id', FILTER_SANITIZE_NUMBER_INT);
$user_id = abs($user_id);

$group_id = filter_input(INPUT_POST, 'group_id', FILTER_SANITIZE_NUMBER_INT);
$group_id = abs($group_id);

【问题讨论】:

  • 您使用的是什么驱动程序?您是否查看过有关此主题的任何主题?示例 #1:stackoverflow.com/questions/60174/…
  • 这是准备好的语句不能很好地工作的情况之一。也就是说,看起来您只使用整数(或整数数组)。在POST 输入上运行PHP 的filter_input_array() 应该可以有效地清理它,然后您根本不需要准备好的语句。见filter_input_array()
  • 您可以定义$sql 语句,然后在for 循环中为每个用户/组对进行绑定。
  • 这是 PDO 吗?你用什么来和 MySQL 对话?

标签: php mysql


【解决方案1】:

要创建 SQL 语句,您可以使用它来为所有 user_id 值生成所需数量的占位符。

$user_ids  = $_POST["user_id"]; //this can be an array
$group_id = $_POST["group_id"];
$placeholders = implode(', ', array_fill(0, count($user_ids), "(?, ?)"));    
$sql = "INSERT IGNORE INTO users_groups (user_id, group_id) VALUES $placeholders";

然后绑定值,如果你使用 pdo,这样的东西应该可以工作:

$stmt = $pdo->prepare($sql);
$i = 1;
foreach ($user_ids as $user_id) {
    $stmt->bindValue($i++, $user_id);
    $stmt->bindValue($i++, $group_id);
}

或者,如果您使用的是 mysqli,您可以尝试使用反射的这种方法(取自 php 文档 here 中的用户注释),注释确实指出这需要 PHP 5.3+,希望您确实拥有。

$stmt = $mysqli->prepare($sql);
$values[] = str_repeat('ii', count($user_ids));
foreach ($user_ids as $user_id) {
    $values[] = $user_id;
    $values[] = $group_id;
}
$ref = new ReflectionClass('mysqli_stmt');
$method = $ref->getMethod("bind_param");
$method->invokeArgs($stmt, $values);
$stmt->execute(); 

或者,因为我很固执,而且 php docs 用户注释中的内容似乎不起作用,在阅读更多内容后,我看不出它是如何工作的,考虑到 ReflectionMethod::invokeArgs 的 php 文档特别说

注意:如果函数有参数需要引用,那么它们必须是传递参数列表中的引用。

那么使用call_user_func_array 可能会起作用。

$stmt = $mysqli->prepare($sql);
$type = str_repeat('ii', count($user_ids));
foreach ($user_ids as $user_id) {
    $values[] = $user_id;
    $values[] = $group_id;
}
foreach ($values as $key => $value) {
    $value_references[$key] = &$values[$key];
}
call_user_func_array('mysqli_stmt_bind_param', 
    array_merge(array($stmt, $type), $value_references));

但是多么痛苦啊。我真的很喜欢 pdo。

【讨论】:

  • 我现在将你的部分与$placeholder 一起使用,但我在下面的部分中遇到了问题。我通常像这样使用 bind_param:$sql-&gt;bind_param('ii', $user_id[0], $group_id); 和多个 user_id,它看起来像这样:$sql-&gt;bind_param('iiii', $user_id[0], $group_id, $user_id[1], $group_id); 现在我不知道如何在 foreach 循环中实现它。
  • @KlippOhei 好吧,我在知道您使用 mysqli 之前就写了这个答案。不幸的是,这在使用 mysqli 时不起作用,因为它绑定参数的方法不同。我一直在使用 pdo 而不是 mysqli 一段时间(部分原因是我认为由于此类问题在 pdo 中使用准备好的语句要容易得多)所以我现在想不出如何在 mysqli 中处理这个问题。我认为这仍然是可能的,但有点痛苦。我会看看我是否可以找到我这样做的一些旧代码并更新答案。
  • 也许我可以尝试不使用准备好的语句,因为用户必须发布到脚本的值只是数字,所以我可以检查发布的变量是否是数字(使用is_numeric),如果不,退出脚本。这对 SQL 注入安全吗?
  • 听起来应该没问题。在对您的问题的评论中建议使用 filter_input_array 作为 Hobo Sapiens 将对此有所帮助。如果您仍想尝试使用准备好的语句,this answer 中的方法可能会起作用,但需要进行一些修改。
  • 我会在几分钟后试用您的新版本。 :) 我的更新 3(第一篇文章)是否可以对抗 SQL 注入?
猜你喜欢
  • 1970-01-01
  • 2011-11-03
  • 1970-01-01
  • 2015-03-02
  • 2013-08-10
  • 1970-01-01
  • 2015-02-26
  • 1970-01-01
  • 2016-03-05
相关资源
最近更新 更多