1. 本記事のポイント
- SQLインジェクション対策として、プレースホルダを使ったSELECT文の書き方を解説
PDO
による実装を中心に、バインド方法の違いや注意点を紹介- プレースホルダの種類と使い分けを実務視点で整理
2. PHPのプレースホルダによるSELECTとは?
PHPでSQLを扱う際、動的な値を直接クエリ文字列に埋め込むと、SQLインジェクションのリスクが生じます。これを回避する代表的な手法が「プレースホルダ」の使用です。
プレースホルダとは、SQL文内の値の代わりに記号を入れ、後から安全に値をバインドすることで、SQLの構造とデータの分離を実現する機能です。PHPではPDO
(PHP Data Objects)を用いることで、プレースホルダ付きのプリペアドステートメントを簡潔に扱えます。
特にSELECT文は外部入力に依存する機会が多いため、安全な書き方は基本中の基本です。プレースホルダの使い方は、実務でのデータ取得処理において、堅牢性・保守性の観点からも重要なポイントとなります。
3. 詳細解説
基本:名前付きプレースホルダでのSELECT
ユーザIDをもとに該当ユーザ情報を取得する例です。
<?php
// DB接続(例:MySQL)
$pdo = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'user', 'password');
// プレースホルダを含むクエリ
$sql = "SELECT * FROM users WHERE id = :id";
$stmt = $pdo->prepare($sql);
// 値のバインド
$stmt->bindValue(':id', 5, PDO::PARAM_INT);
$stmt->execute();
// 結果取得
$result = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($result);
:id
がプレースホルダ(名前付き)です。bindValue()
では型指定も可能(例:PDO::PARAM_INT
)。fetch()
で1行分の結果を取得。
番号付きプレースホルダの使用
同様のSELECT文を、番号付きで書くと以下のようになります。
$sql = "SELECT * FROM users WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([5]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
?
で順番指定する形式。execute()
に配列で値を渡す。- 配列の順序がクエリのプレースホルダ順に対応。
複数条件でのバインド
名前付きプレースホルダを複数使うケースです。
$sql = "SELECT * FROM users WHERE age > :age AND status = :status";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':age' => 20,
':status' => 'active'
]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
- バインド順が明示的で、複雑な条件式に適する。
fetchAll()
で複数行をまとめて取得。
名前付き vs 番号付きの違いと選定基準
- 名前付き:クエリが読みやすく、値の意味が明示される。条件が多い場合や順序が複雑な場合に適している。
- 番号付き:簡素なクエリに適し、配列でまとめて渡せるので短く書ける。
どちらもSQLインジェクション対策として有効ですが、実務では保守性の観点から名前付きプレースホルダが一般的です。
4. よくあるミス・誤解・落とし穴
1. プレースホルダにカラム名やテーブル名を使おうとする
プレースホルダは値のみに使うことができます。以下は誤った例です:
// テーブル名にプレースホルダを使っている
$sql = "SELECT * FROM :table WHERE id = :id"; // 動作しない
このような構造にはプレースホルダを使えないため、事前に妥当性チェックをしたうえで文字列連結する必要があります。
2. bindValueとbindParamの違いを理解していない
bindValue()
:その時点の値をバインド(通常はこちらを使用)bindParam()
:変数への参照をバインド(変数の値が後で変わる場合)
誤用すると、意図しない値が送信される可能性があります。
3. execute前のprepare忘れ
以下のように直接execute()
のみを呼ぶと、prepare
が省略されたと誤認されがちですが、必ずprepare()
は必要です。
// 未定義の$stmtを実行しようとする
$stmt->execute([':id' => 5]);<br>
5. まとめ
PHPで安全にSELECTを実行するには、PDO
とプレースホルダの併用が基本です。
名前付きと番号付きのプレースホルダにはそれぞれ特長がありますが、可読性と保守性の点からは名前付きの利用が推奨されます。
誤用しやすいポイントとしては、構造要素に対するプレースホルダの誤適用、bindParam()
とbindValue()
の混同などがあります。
基本的な使い方を理解し、SQLインジェクション対策として適切な実装を心がけましょう。