PHPからデータベースへの接続が完了したら、次はいよいよSQL文を実行してデータを操作します。PHPでデータベースを扱う際には、セキュリティを確保し、効率的に処理を行うための決まった「作法」があります。
それが、**「1. 準備 (Prepare) → 2. 実行 (Execute) → 3. 結果取得 (Fetch)」**という3ステップの流れです。
この記事では、PDOを使ってこの一連の操作を行う方法と、結果を配列として受け取る方法について詳しく解説します。
1. Step 1: SQL文を「準備」する (prepare
)
データベースを操作する上で最も注意すべきことの一つがSQLインジェクションという攻撃です。これを防ぐための現代的な手法が「プリペアドステートメント (Prepared Statement)」であり、PDOではprepare()
メソッドがその役割を担います。
これは、SQL文のテンプレート(雛形)を先にデータベースに送って「こういう命令を実行するよ」と準備させるイメージです。このとき、ユーザーからの入力値など、変動するデータ部分は?
(プレースホルダ)としておきます。
<?php
// ($pdo はデータベース接続済みのPDOオブジェクトとする)
// 評価が ? 以上のレビューを取得するSQL文のテンプレートを準備
$sql = "SELECT * FROM reviews WHERE rating >= ?";
$stmt = $pdo->prepare($sql);
?>
prepare()
メソッドは、準備されたSQL文を格納したPDOStatement
オブジェクト(ここでは変数$stmt
)を返します。
2. Step 2: SQLを「実行」する (execute
)
SQL文の準備ができたら、execute()
メソッドを使って実際に実行します。このとき、prepare()
で用意したプレースホルダ (?
) に、実際の値を配列の形で渡します。
<?php
// プレースホルダに渡したい値
$rating_threshold = 4;
// 配列で値を渡してSQLを実行
$stmt->execute([$rating_threshold]);
?>
PDOは内部で、渡された値を安全な形に処理してからSQL文にはめ込んでくれるため、開発者はSQLインジェクションのリスクを心配することなく、安全にクエリを実行できます。
3. Step 3: 結果を「取得」する (fetch
, fetchAll
)
SELECT
文を実行した場合、その結果をデータベースからPHPの変数に取り出す必要があります。この処理を「フェッチ (Fetch)」と呼びます。
fetchAll()
: 全件をまとめて取得
fetchAll()
は、SQLの実行結果をすべて取得し、多次元配列として返します。結果の件数が少ない場合に便利です。
// 結果をすべて連想配列として取得
$reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 取得したデータをループで表示
foreach ($reviews as $review) {
echo htmlspecialchars($review['book_title'], ENT_QUOTES, 'UTF-8') . '<br>';
}
fetchAll()
には、どのような形式の配列で結果を受け取るかを指定する「フェッチモード」があります。
PDO::FETCH_ASSOC
: カラム名をキーとする連想配列で返します。最もよく使われます。PDO::FETCH_NUM
: 0から始まる添字の配列で返します。PDO::FETCH_BOTH
: 上記2つの形式を両方含んだ配列を返します。(デフォルトですが、冗長なので通常は使いません)
fetch()
: 1件ずつ取得
fetch()
は、結果を1行(1レコード)ずつ取得します。while
ループと組み合わせて使うことで、メモリを効率的に使いながら大量のデータを処理できます。
// 結果を1行ずつ連想配列として取得し、データがなくなるまでループ
while ($review = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo htmlspecialchars($review['book_title'], ENT_QUOTES, 'UTF-8') . '<br>';
}
INSERT, UPDATE, DELETEの場合
データを登録・更新・削除する場合、**「1. 準備」→「2. 実行」**の流れは同じです。これらのSQLは結果セットを返さないため、「3. 結果取得」のステップは不要です。
代わりに、rowCount()
メソッドを使って、処理の影響を受けた行数を取得することができます。
<?php
// 新しいレビューを登録するSQL
$sql = "INSERT INTO reviews (book_title, rating) VALUES (?, ?)";
$stmt = $pdo->prepare($sql);
// 実行
$stmt->execute(['PHP実践ガイド', 5]);
// 影響を受けた行数を表示
echo $stmt->rowCount() . "件のレビューを登録しました。"; // 「1件のレビューを登録しました。」
?>
PDOStatementのメソッド一覧
$pdo->prepare()
が返す$stmt
(PDOStatementオブジェクト)には、データベース操作に利用できる様々なメソッドが用意されています。以下にその一覧をまとめます。
メソッド | 説明 |
$stmt->bindValue() | 値をプレースホルダに紐付け(バインド)します。 |
$stmt->bindParam() | 変数をプレースホルダに紐付け(バインド)します。変数の値がexecute() 時に評価されます。 |
$stmt->bindColumn() | 結果セットのカラムを変数に紐付けます。 |
$stmt->execute() | 準備されたプリペアドステートメントを実行します。 |
$stmt->fetch() | 結果セットから次の1行を取得します。 |
$stmt->fetchAll() | 結果セットのすべての行を含む配列を返します。 |
$stmt->fetchColumn() | 結果セットの次の行から単一のカラムを返します。 |
$stmt->rowCount() | 直前のDELETE, INSERT, UPDATE文によって作用した行数を返します。 |
$stmt->columnCount() | 結果セットに含まれるカラムの数を返します。 |
$stmt->setFetchMode() | このステートメントのデフォルトのフェッチモードを設定します。 |
$stmt->closeCursor() | サーバーへの接続を維持したまま、カーソルを閉じ、ステートメントを再度実行できるようにします。 |
$stmt->debugDumpParams() | 準備されたSQLコマンド文字列をデバッグ目的で出力します。 |
$stmt->errorCode() | 直近の操作で発生したSQLSTATEエラーコードを取得します。 |
$stmt->errorInfo() | 直近の操作で発生した拡張エラー情報を取得します。 |
$stmt->getAttribute() | PDOStatementの属性を取得します。 |
$stmt->setAttribute() | PDOStatementの属性を設定します。 |
$stmt->nextRowset() | ストアドプロシージャから複数の結果セットを返す場合に、次の結果セットへカーソルを進めます。 |
まとめ
PHPとPDOでデータベースを操作する際の、安全で基本的な流れを学びました。
- SQL文は直接実行せず、**
prepare()
**で準備する。 - 変動する値は**プレースホルダ (
?
)にし、execute()
**に配列で渡す。 SELECT
文の結果は**fetch()
またはfetchAll()
**で取得する。
この**「Prepare -> Execute -> Fetch」**の作法は、SQLインジェクションを防ぎ、堅牢なWebアプリケーションを構築するための基本中の基本です。必ずこの流れをマスターしましょう。