PHPでデータベースに安全なデータ登録!PDOプリペアドステートメント完全ガイド

フォームデータの検証、データ型の変換といった準備を経て、いよいよ最終ステップである「データベースへのデータ登録」です。この処理は、ユーザーの情報をアプリケーションに「記憶」させる、動的なWebサイトの心臓部と言えます。

最も重要なのは、この登録処理を安全に行うことです。悪意のあるデータがデータベースに登録されたり、システムを攻撃されたりするのを防ぐため、PHPではPDOのプリペアドステートメントを用いるのが現代の常識です。

この記事では、これまでの知識を総動員し、フォームから受け取ったデータを安全にデータベースへINSERTする、完成版のPHPスクリプトを解説します。


目次

1. INSERT文とプレースホルダのおさらい

まず、新しいデータを登録するためのSQL文INSERTと、セキュリティの要であるプレースホルダの関係を確認しましょう。

変動する値(ユーザーの入力内容)は、SQL文に直接埋め込むのではなく、すべて?(プレースホルダ)に置き換えます。これにより、データベースは命令(SQL)と値(データ)を明確に区別でき、SQLインジェクション攻撃を根本的に防ぐことができます。

prepare()で準備するSQL文の例:

<?php
// 登録するデータ項目に対応する数の?を用意する
$sql = "INSERT INTO reviews (book_title, rating, review_text) VALUES (?, ?, ?)";
$stmt = $pdo->prepare($sql);
?>

2. 複数のプレースホルダに値を渡して実行する

prepare()で準備したSQL文の?に、実際の値を渡して実行します。この「値を渡す(バインドする)」方法には、主に2つの書き方があります。

方法1(推奨): execute()に配列を渡す

最もシンプルで、現代のPHPで広く使われている方法です。execute()メソッドの引数に、?の順番通りに値を格納した配列を渡します。

<?php
// $book_title, $rating_int, $review_text はフォームから受け取り、
// バリデーションと型キャストが済んだ変数
$stmt->execute([$book_title, $rating_int, $review_text]);
?>

この一行で、安全な値のバインドとSQLの実行が完了します。コードが簡潔で、非常に分かりやすいのが特徴です。

方法2(明示的): bindValue()で個別に指定する

bindValue()メソッドを使い、各プレースホルダに値を一つずつ、より明示的に紐付ける方法もあります。

<?php
// 1番目の?に$book_titleを文字列としてバインド
$stmt->bindValue(1, $book_title, PDO::PARAM_STR);
// 2番目の?に$rating_intを整数としてバインド
$stmt->bindValue(2, $rating_int, PDO::PARAM_INT);
// 3番目の?に$review_textを文字列としてバインド
$stmt->bindValue(3, $review_text, PDO::PARAM_STR);

$stmt->execute(); // executeの引数は空にする
?>

この方法は、値のデータ型(PDO::PARAM_STRPDO::PARAM_INT)を厳密に指定したい場合に有効です。


3. 完成版コード:フォームデータ登録処理のすべて

それでは、バリデーションから型変換、そしてデータベース登録までの一連の流れを実装した、完全なadd.phpのコードです。このスクリプトは、これまでの学習内容の集大成となります。

add.phpのサンプルコード:

<?php
// --- データベース接続設定 ---
$dsn = 'mysql:host=localhost;dbname=my_first_db;charset=utf8mb4';
$user = 'root';
$password = 'root'; // ご自身の環境に合わせて変更

// --- フォームからのPOST送信でのみ処理を実行 ---
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit('不正なリクエストです。');
}

try {
    // --- 1. バリデーション ---
    if (empty($_POST['book_title'])) {
        throw new Exception('書籍名は必須です。');
    }
    if (empty($_POST['rating']) || !ctype_digit($_POST['rating'])) {
        throw new Exception('評価は半角数字で入力してください。');
    }
    
    // --- 2. データを変数に格納 & 型キャスト ---
    $book_title = $_POST['book_title'];
    $rating = (int)$_POST['rating']; // 整数型に変換
    $review_text = $_POST['review_text'];

    // --- 3. データベースへの接続 ---
    $pdo = new PDO($dsn, $user, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

    // --- 4. INSERT文の準備と実行 ---
    $sql = "INSERT INTO reviews (book_title, rating, review_text) VALUES (?, ?, ?)";
    $stmt = $pdo->prepare($sql);
    
    // execute()の引数に、プレースホルダに渡す値を配列で指定
    $stmt->execute([$book_title, $rating, $review_text]);

    // --- 5. 成功メッセージの表示 ---
    echo "レビューの登録が完了しました。";

} catch (Exception $e) {
    // --- 6. エラー発生時の処理 ---
    echo "エラー発生: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
} finally {
    // --- 7. データベース接続の解放 ---
    // finallyブロックは、tryやcatchの処理が終わった後、必ず実行される
    $pdo = null;
}
?>
<br>
<a href="form.html">入力フォームに戻る</a>

まとめ

今回は、PHPアプリケーションの根幹となる、安全なデータベース登録処理の全貌を学びました。

  • INSERT文では、変動する値を必ずプレースホルダ(?)にする。
  • prepare()でSQLを準備し、execute()に配列を渡して安全に実行する。
  • **「バリデーション → 型キャスト → DB接続 → prepare → execute」**という一連の流れが、堅牢な登録処理の基本形。
  • try...catch構文で処理全体を囲むことで、予期せぬエラーにも適切に対処できる。

このパターンは、あらゆるデータの登録処理に応用できる、非常に重要なものです。この安全な「型」を身につけて、信頼性の高いWebアプリケーションを構築していきましょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次