PHPでデータベースを扱うとき、ユーザー入力をSQL文字列へ直接つなげるのは危険です。検索フォーム、ログイン、管理画面、APIなどでは、PDOのプリペアドステートメントを使って値をバインドするのが基本です。
この記事では、検索キーワードを受け取って記事一覧を取得する例で、PDOの安全な書き方をまとめます。
避けたい書き方
<?php
$keyword = $_GET['keyword'] ?? '';
$sql = "SELECT * FROM posts WHERE title LIKE '%{$keyword}%'";
$posts = $pdo->query($sql)->fetchAll();
PDOで安全に書く例
<?php
$pdo = new PDO(
'mysql:host=localhost;dbname=app;charset=utf8mb4',
'user',
'password',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$keyword = $_GET['keyword'] ?? '';
$stmt = $pdo->prepare(
'SELECT id, title, slug
FROM posts
WHERE title LIKE :keyword
ORDER BY id DESC
LIMIT 20'
);
$stmt->execute([
':keyword' => '%' . $keyword . '%',
]);
$posts = $stmt->fetchAll();
foreach ($posts as $post) {
echo htmlspecialchars($post['title'], ENT_QUOTES, 'UTF-8') . PHP_EOL;
}
設定の意味
| 設定 | 意味 |
|---|---|
PDO::ATTR_ERRMODE | エラー時に例外を投げて原因を追いやすくする |
PDO::ATTR_DEFAULT_FETCH_MODE | 取得結果を連想配列で扱いやすくする |
PDO::ATTR_EMULATE_PREPARES | 可能な範囲でDB側のプリペアを使う |
charset=utf8mb4 | 絵文字や日本語を安全に扱いやすくする |
注意点
- 値はプレースホルダーへ渡し、SQL文字列へ直接連結しない
- テーブル名やカラム名はプレースホルダーで置き換えられない
- 出力時は
htmlspecialchars()でHTMLエスケープする - WordPress内では基本的に
$wpdb->prepare()を使う。独自PHPや小さな管理ツールではPDOが使いやすい