導入:なぜ今、PDOでの安全な実装が重要なのか
現代のPHP開発において、データベース接続の標準は「PDO(PHP Data Objects)」です。かつて利用されていた mysql_query 関数は、SQLインジェクション脆弱性の温床となりやすく、現在は非推奨となっています。実務の現場では、セキュリティを担保するために「プリペアドステートメント」を利用することが絶対条件です。本記事では、PDOを活用した堅牢なデータベース接続と、実務で頻出するCRUD操作のポイントを解説します。
基礎知識:PDOとプリペアドステートメント
PDOは、異なるデータベースを統一的なインターフェースで操作するための抽象化レイヤーです。最大の特徴は、SQL文とデータを分離して実行する「プリペアドステートメント」にあります。SQLをデータベースへ送信する前に「テンプレート」として解析させることで、悪意のある入力値がSQLコマンドとして解釈されるリスクを根本から排除します。
実装:堅牢な接続設定
データベース接続時に設定するオプションは、セキュリティとパフォーマンスに直結します。特にエミュレート機能の無効化は重要です。
サンプルプログラム:安全なPDO接続とSQL実行
PDO::ERRMODE_EXCEPTION,
// 取得結果を連想配列形式に固定
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
// プリペアドステートメントをデータベース側で処理させる(セキュリティ向上)
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$db = new PDO($dsn, DB_USER, DB_PASS, $option);
// データの挿入例
$sql = “INSERT INTO users (user_name, age) VALUES (:name, :age)”;
$stmt = $db->prepare($sql);
// bindValueで値をバインド(型指定を忘れずに行う)
$stmt->bindValue(‘:name’, ‘田中太郎’, PDO::PARAM_STR);
$stmt->bindValue(‘:age’, 25, PDO::PARAM_INT);
$stmt->execute();
// データの取得例
$sql = “SELECT FROM users WHERE age >= :age”;
$stmt = $db->prepare($sql);
$stmt->bindValue(‘:age’, 20, PDO::PARAM_INT);
$stmt->execute();
$users = $stmt->fetchAll();
foreach ($users as $user) {
echo $user[‘user_name’] . PHP_EOL;
}
} catch (PDOException $e) {
// ログに記録し、ユーザーには適切なエラーメッセージを表示
error_log($e->getMessage());
exit(‘データベース接続エラーが発生しました。’);
}
?>
応用・注意点:現場で陥りやすい罠
1. 文字コードの罠
MySQLの `utf8` 指定は、4バイトの絵文字をサポートしていません。実務では必ず `utf8mb4` を使用してください。
2. LIKE検索時のエスケープ
LIKE句でワイルドカード(%や_)を検索対象に含める場合、ユーザー入力値をそのまま埋め込むと予期せぬ検索結果になります。`preg_replace` でメタ文字をエスケープし、`ESCAPE` 句を活用する習慣をつけましょう。
3. bindValueとbindParamの違い
似た関数に `bindParam` がありますが、これは「参照渡し」です。ループ内で使い回すとバグの元になるため、特に理由がない限り、値のコピーを渡す `bindValue` を使用するのが無難です。
4. NULLの扱い
`bindValue` で `null` を渡す際は、第3引数に `PDO::PARAM_NULL` を明示しないと、予期せぬ挙動になることがあります。動的に値をセットする場合は、型判定を組み合わせて記述してください。