我正在寻找一种SQL注入安全技术,用PHP和MySQLi一次插入大量行(约2000)。 我有一个包含所有必须包含的值的数组。 目前我正在这样做:
<?php
$array = array("array", "with", "about", "2000", "values");
foreach ($array as $one)
{
$query = "INSERT INTO table (link) VALUES ( ?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);
$stmt->execute();
$stmt->close();
}
?>
我尝试了call_user_func_array(),但它导致了堆栈溢出。
更快的方法是什么(比如一次插入所有这些?),但仍然可以防止SQL注入(如准备好的语句)和stackoverflows?
谢谢!
答案 0 :(得分:30)
通过将插入内容放入事务中,您应该能够大大提高速度。您还可以在循环之外移动prepare和bind语句。
$array = array("array", "with", "about", "2000", "values");
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt ->bind_param("s", $one);
$mysqli->query("START TRANSACTION");
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
$mysqli->query("COMMIT");
编辑:
我在我的网络服务器上测试了10,000次迭代的代码。
没有交易:226 seconds.
使用交易:2 seconds.
或two order of magnitude speed increase
,至少为该测试。
答案 1 :(得分:6)
再次尝试这一点,我不明白为什么原始代码不能进行微小修改:
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $one);
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
答案 2 :(得分:2)
是的,您可以手动构建一个大型查询,例如:
$query = "";
foreach ($array as $curvalue) {
if ($query)
$query .= ",";
$query .= "('" . $mysqli->real_escape_string($curvalue) . "')";
}
if ($query) {
$query = "INSERT INTO table (link) VALUES " . $query;
$mysqli->query($query);
}
答案 3 :(得分:0)
您应该首先将数组转换为字符串。鉴于它是一个字符串数组(不是二维数组),您可以使用implode函数。
请注意,每个值都应括在括号中并正确转义以确保正确的INSERT
语句并避免SQL注入的风险。为了正确转义,您可以使用PDOConnection
的{{3}}方法 - 假设您通过PDO连接到MySQL。要对阵列的每个条目执行此操作,可以使用quote。
在转义每个值并将它们插入单个字符串后,您需要将它们放入INSERT
语句中。这可以使用array_map完成。
示例:
<?php
$connection = new PDO(/*...*/);
$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$dataToBeSaved = [
'some',
'data',
'with "quotes"',
'and statements\'); DROP DATABASE facebook_main; --'
];
$connection->query(
sprintf(
'INSERT INTO table (link) VALUES %s',
implode(',',
// for each entry of the array
array_map(function($entry) use ($connection) {
// escape it and wrap it in parenthesis
return sprintf('(%s)', $connection->quote($entry));
}, $dataToBeSaved)
)
)
);
注意:根据您愿意插入数据库的记录数量,您可能希望将它们拆分为多个INSERT
语句。