データベース操作にPHPのMySQLI拡張機能を使用する場合、準備されたステートメントを使用してセキュリティとパフォーマンスを向上させることがよくあります。同時に、開発者は、実行中のSQL構文エラーなどの可能なエラーを正確にキャプチャしたいことがよくあります。しかし、あなたはそのような紛らわしい問題に遭遇するかもしれません:
それで、何が起こっているのですか?この記事では、この問題の詳細な分析を実施し、実用的なソリューションを提供します。
次のサンプルコードを参照してください。
$mysqli = new mysqli("localhost", "user", "password", "testdb");
// 構文エラー:1つはありません FROM
$sql = "SELECT id name users WHERE id = ?";
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
echo "Prepare failed: " . $mysqli->error; // それを行う正しい方法
} else {
$stmt->bind_param("i", $id);
$id = 1;
$stmt->execute();
if ($stmt->error) {
echo "Execute error: " . $stmt->error; // 不会捕捉到構文エラー
}
$stmt->close();
}
$mysqli->close();
Prepare failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version...
ご覧のとおり、 SQL構文エラーは$ stmt->エラーをまったく入力しませんが、 prepare()段階で$ mysqli->エラーでキャッチされます。
PHPのMysqliとMysqli_stmtの間の分業を理解する必要があります。
mysqli :: prepare()は、データベース接続オブジェクトによって呼び出されるメソッドです。 SQLに構文エラーがある場合、 STMTオブジェクトはまったく生成されないため、 $ STMTは偽です。
mysqli_stmt :: $エラーは、ステートメントが正常に準備され、ランタイムエラーが発生した後にのみ値があります(バインディング変数タイプは、実行中に一致しません。外部キーの制約が失敗するなど)。
したがって、 SQL構文エラーは、STMTオブジェクトを作成しないため、 $ STMT->エラーでキャッチすることはなく、キャッチできません。
言い換えると:
SQL構文エラーはprepare()段階で発生し、 $ stmt-> execute()を待つのではなく、 $ mysqli-> error :: prepare()の返品値で審査する必要があります。
上記のコードを書き換えて、より安全なエラー処理ロジックを採用します。
$mysqli = new mysqli("localhost", "user", "password", "testdb");
$sql = "SELECT id, name FROM users WHERE id = ?"; // 正しい構文
$stmt = $mysqli->prepare($sql);
if (!$stmt) {
// 检查構文エラー
die("SQL prepare failed: " . $mysqli->error);
}
$stmt->bind_param("i", $id);
$id = 1;
if (!$stmt->execute()) {
// ランタイムエラーを確認してください
die("Execute failed: " . $stmt->error);
}
$result = $stmt->get_result();
$data = $result->fetch_assoc();
echo "User: " . $data['name'];
$stmt->close();
$mysqli->close();
AJAXインターフェイスを使用して、このようなアドレスにアクセスするなど、SQLパラメーターを送信している場合:
https://gitbox.net/api/get_user.php?id=1
次に、ユーザーの入力をSQLにスプライスします(危険!):
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id"; // 潜在的 SQL 注射リスク
この執筆方法は、スプライシング中にSQLを誤って書くのが簡単で、 prepare()は使用されず、エラーを見つけるのがより困難です。したがって、次の記述方法が推奨されます。
$sql = "SELECT * FROM users WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $_GET['id']);
これにより、SQLインジェクションが回避されるだけでなく、エラーチェックが明確で集中化されます。
SQL構文エラーはprepare()段階でのみ見つけることができ、 $ stmt->エラーでは取得できません。
prepare()の返品値を確認し、 $ mysqli->エラーを使用してエラーメッセージを出力してください。
注入を防ぐために前処理ステートメントを使用することをお勧めします。エラーをデバッグしてキャッチする方が簡単です。
MySQLIとMySQLI_STMTの間の責任の分割を理解することによってのみ、より堅牢で安全なデータベースインタラクションコードを記述できます。