PHPとデータベース - INSERT

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動

概要



DATETIME型

SQLインジェクション対策のため、必ず、プリペアドステートメントを使用する。
これは、パフォーマンスの向上にも寄与する。

タイムゾーンの設定

 // PHPのタイムゾーン設定
 date_default_timezone_set('Asia/Tokyo');
 
 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    $pdo->exec("SET time_zone = '+09:00'");   // MySQLのタイムゾーン設定
 }
 catch (PDOException $e) {
    echo "エラー: " . $e->getMessage();
 }


NOW関数の使用

MySQLのNOW関数を使用して現在時刻を挿入する。 サーバ側の時刻が使用されるため、最も正確である。

なお、タイムゾーンはMySQLの設定に依存することに注意する。

 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    // 現在時刻をMySQLのNOW関数で挿入
    // MySQLのカラムがDATETIME型の必要がある
    $sql  = "INSERT INTO events (event_name, created_at) VALUES (:name, NOW())";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([':name' => 'イベント1']);
 }
 catch (PDOException $e) {
    echo "エラー: " . $e->getMessage();
 }


DateTimeオブジェクトの使用

PHPのDateTimeクラスを使用して日時を管理する。
また、formatメソッドでMySQLが理解できる形式に変換する。

これは、より柔軟な日時の操作が可能である。

 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    // PHPのDateTimeオブジェクトを使用
    $sql  = "INSERT INTO events (event_name, created_at) VALUES (:name, :datetime)";
    $stmt = $pdo->prepare($sql2);
    $datetime = new DateTime();
    $stmt2->execute([':name' => 'イベント',
                     ':datetime' => $datetime->format('Y-m-d H:i:s')
    ]);
 }
 catch (PDOException $e) {
    echo "エラー : " . $e->getMessage();
 }


文字列での直接指定

特定の日時を文字列として指定する。 ただし、フォーマットはYYYY-MM-DD HH:MM:SS形式である必要がある。
また、入力値の検証が重要となる。

 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    // 特定の日時を文字列で指定
    $sql  = "INSERT INTO events (event_name, created_at) VALUES (:name, :datetime)";
    $stmt = $pdo->prepare($sql);
    $specificDate = '2024-12-31 23:59:59';
    $stmt->execute([':name' => 'イベント3',
                    ':datetime' => $specificDate]);
 }
 catch (PDOException $e) {
    echo "エラー : " . $e->getMessage();
 }


明示的な型指定

PDO::bindValueメソッドを使用してパラメータの型を明示的に指定する。
これは、より厳密な型チェックが可能となる。

PDO::PARAM_STRを使用してSTRING型として扱う必要がある。

 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    // バインドパラメータの型を明示的に指定
    $sql      = "INSERT INTO events (event_name, created_at) VALUES (:name, :datetime)";
    $stmt     = $pdo->prepare($sql);
    $datetime = new DateTime('+1 week');
    $stmt->bindValue(':name', 'イベント', PDO::PARAM_STR);
    $stmt->bindValue(':datetime', $datetime->format('Y-m-d H:i:s'), PDO::PARAM_STR);
    $stmt->execute();
 }
 catch (PDOException $e) {
    echo "エラー: " . $e->getMessage();
 }


複数のレコードの一括挿入

配列を使用して、繰り返し文で複数のレコードを効率的に挿入する。
これは、メモリ効率が良い。

 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    // 複数のレコードの一括挿入
    $sql  = "INSERT INTO events (event_name, created_at) VALUES (?, ?)";
    $stmt = $pdo->prepare($sql5);
 
    $events = [
       ['イベント5', '2024-01-01 00:00:00'],
       ['イベント6', '2024-01-02 00:00:00'],
    ];
 
    foreach ($events as $event) {
       $stmt->execute($event);
    }
 }
 catch (PDOException $e) {
    echo "エラー: " . $e->getMessage();
 }


トランザクションを使用した安全な挿入

 // データベース接続の設定
 $dsn = 'mysql:host=<ホスト名またはIPアドレス>;dbname=<データベース名>;charset=utf8mb4';
 $username = 'username';
 $password = 'password';
 
 try {
    $pdo = new PDO($dsn, $username, $password, [
       PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
       PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
 
    // トランザクションを使用した安全な挿入
    $pdo->beginTransaction();
    try {
       $sql  = "INSERT INTO events (event_name, created_at) VALUES (:name, :datetime)";
       $stmt = $pdo->prepare($sql6);
 
       $datetime = new DateTime();
       $stmt->execute([':name' => 'トランザクションイベント',
                       ':datetime' => $datetime->format('Y-m-d H:i:s')]);
       $pdo->commit();
    }
    catch (Exception $e) {
       $pdo->rollBack();
       throw $e;
    }
 }
 catch (PDOException $e) {
    echo "エラー: " . $e->getMessage();
 }


日付のバリデーション

DateTime::createFromFormatメソッドを使用した日付バリデーションにおいて、以下に示す検証を組み合わせることにより、堅牢な日付バリデーションが実現できる。
必ず、タイムゾーンを考慮する必要がある。

DateTimeクラスの書式指定子

  • Y
    4桁の年 (例: 2024)
  • m
    2桁の月 (01-12)
  • d
    2桁の日 (01-31)
  • H
    24時間形式の時 (00-23)
  • i
    分 (00-59)
  • s
    秒 (00-59)


常にタイムゾーンを明示的に設定する。

 date_default_timezone_set('Asia/Tokyo');


 // 使用例 1
 
 try {
    $datetime = DateTime::createFromFormat('Y-m-d H:i:s', $dateString);
    $errors   = DateTime::getLastErrors();  // 戻り値は連想配列となることに注意
 
    if ($errors['warning_count'] > 0 || $errors['error_count'] > 0) {
       // エラーがある場合
    }
 }
 catch (Exception $e) {
    // エラー処理
    log_error($e->getMessage());
    return false;
 }
 
 // 正常に処理ができた場合は、処理を以下に記述...
 // ...略


 // 使用例 2
 
 try {
    $date = new DateTime($dateString);
    if ($date === false) {
       throw new Exception("無効な日付フォーマットです");
    }
 }
 catch (Exception $e) {
    // エラー処理
    log_error($e->getMessage());
    return false;
 }


また、日付の範囲をチェックする必要もある。

 $minDate = new DateTime('2000-01-01');
 $maxDate = new DateTime('2099-12-31');
 
 if ($date >= $minDate && $date <= $maxDate) {
    // 有効な範囲の場合
 }


ユーザ入力が伴う場合は、XSS攻撃の可能性があるため、正規化が必要となる。

 $date = trim($dateString);
 $date = filter_var($date, FILTER_SANITIZE_STRING);