開発者は、特にMySqliのMulti-Resultセット(Multi_Query)関数を使用する場合、データベース操作にPHPを使用する場合、 next_result()関数を使用することがよくあります。ただし、ループで不適切に使用された場合、トリガーされる可能性があり、スクリプトが正常に終了しなくなります。この記事では、この発生の理由を詳細に分析し、信頼できるソリューションを提供します。
Multi_Queryを使用して複数のSQLステートメントを実行するコードがあるとします。
$mysqli = new mysqli("localhost", "user", "password", "database");
$sql = "
SELECT * FROM users;
SELECT * FROM orders;
SELECT * FROM products;
";
if ($mysqli->multi_query($sql)) {
do {
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_assoc()) {
print_r($row);
}
$result->free();
}
// これが鍵です
} while ($mysqli->next_result());
}
このコードでは、3つの選択されたステートメントを順番に実行し、それぞれの結果セットを処理する必要があります。 next_result()の目的は、次の結果セットを準備することです。
問題は、最後にnext_result()が呼び出される場合がtrueですが、 store_result()がfalseを返します(結果セットがもうないため)、デッドループに入ります。
next_result()の目的は、mysqliを次の結果セットに移動し、ブール値を返すことです。
Trueを返すことは、より多くの結果セット、または成功したスイッチがあることを意味します。
falseを返すということは、結果セットがなく、エラーが発生しないことを意味します。
ただし、最後の結果セットが空の結果セット(最後のステートメントは選択されているが結果がない、または更新などの非クエリステートメント)である場合、 store_result()がfalseを返しますが、 next_result()がtrueを返す可能性があります...実行を続ける間-出口条件はありません。
more_results()メソッドを使用して、処理できる結果セットがまだあるかどうかを判断できます。
$mysqli = new mysqli("localhost", "user", "password", "database");
$sql = "
SELECT * FROM users;
SELECT * FROM orders;
UPDATE users SET status = 'active' WHERE id = 1;
";
if ($mysqli->multi_query($sql)) {
do {
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_assoc()) {
print_r($row);
}
$result->free();
} else {
// 結果セットではありません,多分 UPDATE、INSERT 待って
if ($mysqli->errno) {
echo "MySQL 間違い:" . $mysqli->error;
}
}
} while ($mysqli->more_results() && $mysqli->next_result());
}
結果セットの数を完全に判断できない場合は、最大数のループを設定して、デッドループを防ぐことができます。
$maxLoops = 10;
$loopCounter = 0;
if ($mysqli->multi_query($sql)) {
do {
if (++$loopCounter > $maxLoops) {
echo "可能性のあるデッドループが検出されました,操作を中止します";
break;
}
if ($result = $mysqli->store_result()) {
while ($row = $result->fetch_assoc()) {
print_r($row);
}
$result->free();
}
} while ($mysqli->next_result());
}
next_result()を使用する場合、完全に信頼できない返品値に特に注意を払う必要があります。 more_results()で使用するには、実際に次の結果セットがある場合にのみ継続するようにしてください。それ以外の場合は、特定のSQL実行パスの下でデッドループを引き起こすのは簡単です。特に、空の結果セットや非クエリステートメントの混合実行がある場合。
PHPおよびデータベース操作に関するより多くのベストプラクティスを確認するには、次をご覧ください。