PDOで接続する
PDOはPHPからデータベースへ接続するための標準的な仕組みです。接続情報は環境変数や設定ファイルで管理し、公開リポジトリへパスワードを置きません。
db.phpPHP
<?php
$dsn = 'mysql:host=127.0.0.1;dbname=study_mysql;charset=utf8mb4';
$pdo = new PDO($dsn, $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);接続に失敗したときは、ホスト、ポート、DB名、ユーザー、パスワード、PDO MySQLドライバーの有無を順に確認します。
プレースホルダーで検索する
products.phpPHP
<?php
require __DIR__ . '/db.php';
$minPrice = 500;
$stmt = $pdo->prepare(
'SELECT name, price FROM products
WHERE price >= :min_price
ORDER BY price DESC'
);
$stmt->execute(['min_price' => $minPrice]);
foreach ($stmt as $product) {
echo htmlspecialchars($product['name'], ENT_QUOTES, 'UTF-8');
echo ': ' . $product['price'] . '円<br>';
}ブラウザーの表示例OUTPUT
MySQL入門書: 2800.00円
マグカップ: 900.00円DECIMAL(10, 2)の値は、この例のPDO設定では文字列"2800.00"のように取得されます。整数表示にしたい場合は、表示側で明示的に書式を指定します。SQLへ値を文字列連結しないことがSQLインジェクション対策の基本です。HTMLへ出力するときは、SQLとは別にHTMLエスケープも行います。
フォームの値を追加する
入力値を検証して追加するPHP
$name = trim($_POST['name'] ?? '');
$price = filter_input(INPUT_POST, 'price', FILTER_VALIDATE_FLOAT);
if ($name === '' || $price === false || $price < 0) {
throw new InvalidArgumentException('商品名と価格を確認してください。');
}
$stmt = $pdo->prepare(
'INSERT INTO products (name, price, stock)
VALUES (:name, :price, :stock)'
);
$stmt->execute([
'name' => $name,
'price' => $price,
'stock' => 0,
]);入力が不正な場合の表示例OUTPUT
商品名と価格を確認してください。入力検証は利用者向けのエラー表示、プレースホルダーはSQLの安全性、出力エスケープはHTMLの安全性を守ります。役割を混ぜないようにします。
PHPからトランザクションを使う
注文と在庫を一緒に更新するPHP
try {
$pdo->beginTransaction();
$stmt = $pdo->prepare(
'UPDATE products
SET stock = stock - :quantity
WHERE id = :id AND stock >= :quantity'
);
$stmt->execute(['quantity' => 1, 'id' => 1]);
if ($stmt->rowCount() !== 1) {
throw new RuntimeException('在庫がありません。');
}
$stmt = $pdo->prepare(
'INSERT INTO orders (product_id, quantity, ordered_at)
VALUES (:product_id, :quantity, CURRENT_DATE)'
);
$stmt->execute(['product_id' => 1, 'quantity' => 1]);
$pdo->commit();
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
throw $e;
}どちらかのSQLが失敗したら、在庫だけ減る状態を残さず、全体を取り消します。利用者へ見せるメッセージと、ログへ記録する詳細な例外を分けることも大切です。
安全に運用する
- DBユーザーへ必要最小限の権限だけを与える
- 接続情報を環境変数や秘密管理へ分ける
- エラーメッセージへSQLやパスワードを表示しない
- 入力検証、プレースホルダー、出力エスケープを別々に行う
- テスト用と本番用のデータベースを分ける
PHPの書き方だけで安全性が決まるわけではありません。MySQLの権限、サーバーのネットワーク、バックアップと復元まで含めて確認します。
