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インジェクション(プリペアドステートメントなど)やスタックオーバーフローに対して安全な方法は何ですか?
ありがとうございました!
インサートをトランザクション内に配置することで、速度を大幅に向上させることができるはずです。また、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");
編集:
このコードをWebサーバーで10,000回繰り返してテストしました。
トランザクションなし:226 seconds.
トランザクションあり:2 seconds.
またはtwo order of magnitude speed increase
、少なくともそのテストでは。
これをもう一度試してみると、元のコードが小さな変更で機能しない理由がわかりません。
$query = "INSERT INTO table (link) VALUES (?)";
$stmt = $mysqli->prepare($query);
$stmt->bind_param("s", $one);
foreach ($array as $one) {
$stmt->execute();
}
$stmt->close();
はい、次のような方法で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);
}
まず、配列を文字列に変換する必要があります。文字列の配列(2次元配列ではない)である場合、 implode 関数を使用できます。
正しいINSERT
ステートメントを確保し、SQLインジェクションのリスクを回避するために、各値を括弧で囲み、適切にエスケープする必要があることに注意してください。適切にエスケープするには、PDOを介してMySQLに接続していると仮定して、PDOConnection
の- quote メソッドを使用できます。配列のすべてのエントリでこの操作を実行するには、 array_map を使用できます。
各値をエスケープして単一の文字列に内破した後、それらをINSERT
ステートメントに入れる必要があります。これは sprintf で実行できます。
例:
<?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
に分割することをお勧めします。ステートメント。