15

我正在寻找一种使用 PHP 和 MySQLi 一次插入大量行(约 2000 年)的 SQL 注入安全技术。
我有一个包含所有必须包含的值的数组。目前我正在这样做:

<?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 注入(比如准备好的语句)和堆栈溢出?

4

4 回答 4

35

You should be able to greatly increase the speed by putting your inserts inside a transaction. You can also move your prepare and bind statements outside of your loop.

$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");

I tested this code with 10,000 iterations on my web server.

Without transaction: 226 seconds. With transaction: 2 seconds. Or a two order of magnitude speed increase, at least for that test.

于 2013-03-01T02:47:25.360 回答
5

再试一次,我不明白为什么你的原始代码在稍作修改后就不能工作:

$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $one);

foreach ($array as $one) {
    $stmt->execute();
}
$stmt->close();
于 2013-03-01T02:07:47.963 回答
1

是的,您可以手动构建一个大查询,例如:

$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);
}
于 2013-03-01T02:15:08.137 回答
-1

You should first convert your array into a string. Given that it is an array of strings (not a two-dimentional array), you can use the implode function.

Please be aware that each value should be enclosed into parenthesis and properly escaped to ensure a correct INSERT statement and to avoid the risk of an SQL injection. For proper escaping you can use the quote method of the PDOConnection -- assuming you're connecting to MySQL through PDO. To perform this operation on every entry of your array, you can use array_map.

After escaping each value and imploding them into a single string, you need to put them into the INSERT statement. This can be done with sprintf.

Example:

<?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)
        )
    )
);

Note: depending on the amount of records you're willing to insert into the database, you may want to split them into several INSERT statements.

于 2018-05-09T19:17:12.830 回答