PHP

セッションでフォームの多重送信を防ぐ

  1. 最終更新日:
  2. 公開日:

ワークショップ「お問い合わせフォームを作成する」の最終回となる第10回目は、完了ページのリロード(再読み込み)で発生するフォームの多重送信を防ぐ機能を実装します。

この記事のポイント

  • セッションを使ってリロード(再読み込み)や送信ボタンの連打によるフォームの多重送信を防ぐ
  • セッションを使うときは必ずsession_start関数を実行する

フォームの多重送信を防ぐ機能を追加する

前回の「アップロードしたファイルをメールに添付する」では、フォームにアップロードしたファイルを自動返信メールに添付して送信する機能理を実装しました。

ワークショップの最終回となる今回はセッションを使って多重送信を防ぐ機能を実装します。
多重送信とは、完了ページでリロード(再読み込み)をしたり、確認ページで「送信」ボタンを連打することによって発生する、同じ内容のフォームが立て続けに送信される現象です。

今回使用しているコードは、前回までに作成したものを使うことを前提としています。
前回のページ:vol.9 アップロードしたファイルをメールに添付する
前回作成したコードはこちら:vol.9のサンプルコード - GitHub

実装の概要

まずは、今回実装する内容を簡単に説明します。

フォームは「入力ページ」「確認ページ」「完了ページ」の3つに分かれていますが、今回は「多重送信を防ぐ」という部分にフォーカスするため実装するページは「確認ページ」「完了ページ」の2ページになります。
「入力ページ」と「確認ページ」の間は「戻る」ボタンなどで何度か行き来する可能性があるため、入力ページでは実装しません。

セッションの実装イメージ
セッションの実装イメージ

セッションを作る処理は「確認ページを通過したよ」という証になります。
そして「完了ページ」の「セッションを確認&削除」では、確認ページを通過してきたことを確認し、正常な遷移を確認できた時点でセッションを削除します。

確認できたら削除することで、もしリロードして再度セッションを確認してもすでにセッションはありません。
セッションが無い場合は入力ページを表示し、セッションがある場合は通常の送信処理を実行するようにしていきます。
こちらの仕組みで多重送信を防ぎます。

それでは実際に、これらの機能を実装していきましょう。

確認ページにセッションを実装する

まずは確認ページからセッションを作る処理を記述します。
index.phpに次のようにコードを記述していきましょう。

Note

赤い箇所は追加するコードを、オレンジの箇所は修正しているコードを示します。

index.php

<?php

define( "FILE_DIR", "images/test/");

---- 省略 ----

if( !empty($clean['btn_confirm']) ) {

  $error = validation($clean);

  // ファイルのアップロード
  if( !empty($_FILES['attachment_file']['tmp_name']) ) {

    $upload_res = move_uploaded_file( $_FILES['attachment_file']['tmp_name'], FILE_DIR.$_FILES['attachment_file']['name']);

    if( $upload_res !== true ) {
      $error[] = 'ファイルのアップロードに失敗しました。';
    } else {
      $clean['attachment_file'] = $_FILES['attachment_file']['name'];
    }
  }

  if( empty($error) ) {

    $page_flag = 1;
    
    // セッションの書き込み
    session_start();
    $_SESSION['page'] = true;
  }

} elseif( !empty($clean['btn_submit']) ) {

---- 省略 ----

短いですが、これでセッションを作ることができるようになりました。

session_start()でセッション開始の合図を出し、$_SESSION['page']trueを格納しています。

完了ページにセッションを実装する

続いて、完了ページにセッションの確認&削除のコードを追記します。

index.php

<?php

define( "FILE_DIR", "images/test/");

---- 省略 ----

} elseif( !empty($clean['btn_submit']) ) {

  session_start();
  if( !empty($_SESSION['page']) && $_SESSION['page'] === true ) {

    // セッションの削除
    unset($_SESSION['page']);

    $page_flag = 2;

    // 変数とタイムゾーンを初期化
    $header = null;
    $body = null;
    $admin_body = null;
    $auto_reply_subject = null;
    $auto_reply_text = null;
    $admin_reply_subject = null;
    $admin_reply_text = null;
    date_default_timezone_set('Asia/Tokyo');
    
    //日本語の使用宣言
    mb_language("ja");
    mb_internal_encoding("UTF-8");
  
    $header = "MIME-Version: 1.0\n";
    $header = "Content-Type: multipart/mixed;boundary=\"__BOUNDARY__\"\n";
    $header .= "From: GRAYCODE <noreply@gray-code.com>\n";
    $header .= "Reply-To: GRAYCODE <noreply@gray-code.com>\n";
  
    // 件名を設定
    $auto_reply_subject = 'お問い合わせありがとうございます。';
  
    // 本文を設定
    $auto_reply_text = "この度は、お問い合わせ頂き誠にありがとうございます。
  下記の内容でお問い合わせを受け付けました。\n\n";
    $auto_reply_text .= "お問い合わせ日時:" . date("Y-m-d H:i") . "\n";
    $auto_reply_text .= "氏名:" . $clean['your_name'] . "\n";
    $auto_reply_text .= "メールアドレス:" . $clean['email'] . "\n";
  
    if( $clean['gender'] === "male" ) {
      $auto_reply_text .= "性別:男性\n";
    } else {
      $auto_reply_text .= "性別:女性\n";
    }

    if( $clean['age'] === "1" ){
      $auto_reply_text .= "年齢:〜19歳\n";
    } elseif ( $clean['age'] === "2" ){
      $auto_reply_text .= "年齢:20歳〜29歳\n";
    } elseif ( $clean['age'] === "3" ){
      $auto_reply_text .= "年齢:30歳〜39歳\n";
    } elseif ( $clean['age'] === "4" ){
      $auto_reply_text .= "年齢:40歳〜49歳\n";
    } elseif( $clean['age'] === "5" ){
      $auto_reply_text .= "年齢:50歳〜59歳\n";
    } elseif( $clean['age'] === "6" ){
      $auto_reply_text .= "年齢:60歳〜\n";
    }
  
    $auto_reply_text .= "お問い合わせ内容:" . nl2br($clean['contact']) . "\n\n";
    $auto_reply_text .= "GRAYCODE 事務局";
    
    // テキストメッセージをセット
    $body = "--__BOUNDARY__\n";
    $body .= "Content-Type: text/plain; charset=\"ISO-2022-JP\"\n\n";
    $body .= $auto_reply_text . "\n";
    $body .= "--__BOUNDARY__\n";
  
    // ファイルを添付
    if( !empty($clean['attachment_file']) ) {
      $body .= "Content-Type: application/octet-stream; name=\"{$clean['attachment_file']}\"\n";
      $body .= "Content-Disposition: attachment; filename=\"{$clean['attachment_file']}\"\n";
      $body .= "Content-Transfer-Encoding: base64\n";
      $body .= "\n";
      $body .= chunk_split(base64_encode(file_get_contents(FILE_DIR.$clean['attachment_file'])));
      $body .= "--__BOUNDARY__\n";
    }

    // 自動返信メール送信
    mb_send_mail( $clean['email'], $auto_reply_subject, $body, $header);
  
    // 運営側へ送るメールの件名
    $admin_reply_subject = "お問い合わせを受け付けました";
  
    // 本文を設定
    $admin_reply_text = "下記の内容でお問い合わせがありました。\n\n";
    $admin_reply_text .= "お問い合わせ日時:" . date("Y-m-d H:i") . "\n";
    $admin_reply_text .= "氏名:" . $clean['your_name'] . "\n";
    $admin_reply_text .= "メールアドレス:" . $clean['email'] . "\n";
  
    if( $clean['gender'] === "male" ) {
      $admin_reply_text .= "性別:男性\n";
    } else {
      $admin_reply_text .= "性別:女性\n";
    }
  
    if( $clean['age'] === "1" ){
      $admin_reply_text .= "年齢:〜19歳\n";
    } elseif ( $clean['age'] === "2" ){
      $admin_reply_text .= "年齢:20歳〜29歳\n";
    } elseif ( $clean['age'] === "3" ){
      $admin_reply_text .= "年齢:30歳〜39歳\n";
    } elseif ( $clean['age'] === "4" ){
      $admin_reply_text .= "年齢:40歳〜49歳\n";
    } elseif( $clean['age'] === "5" ){
      $admin_reply_text .= "年齢:50歳〜59歳\n";
    } elseif( $clean['age'] === "6" ){
      $admin_reply_text .= "年齢:60歳〜\n";
    }
  
    $admin_reply_text .= "お問い合わせ内容:" . nl2br($clean['contact']) . "\n\n";
    
    // テキストメッセージをセット
    $body = "--__BOUNDARY__\n";
    $body .= "Content-Type: text/plain; charset=\"ISO-2022-JP\"\n\n";
    $body .= $admin_reply_text . "\n";
    $body .= "--__BOUNDARY__\n";
  
    // ファイルを添付
    if( !empty($clean['attachment_file']) ) {    
      $body .= "Content-Type: application/octet-stream; name=\"{$clean['attachment_file']}\"\n";
      $body .= "Content-Disposition: attachment; filename=\"{$clean['attachment_file']}\"\n";
      $body .= "Content-Transfer-Encoding: base64\n";
      $body .= "\n";
      $body .= chunk_split(base64_encode(file_get_contents(FILE_DIR.$clean['attachment_file'])));
      $body .= "--__BOUNDARY__\n";
    }
  
    // 管理者へメール送信
    mb_send_mail( 'webmaster@gray-code.com', $admin_reply_subject, $body, $header);
    
  } else {
    $page_flag = 0;
  }
}

---- 省略 ----

追加したif文に注目してください。
$_SESSION['page']が空でなく、かつtrueの場合のみ完了ページの処理を実行するようにしています。
もしセッションが無い場合はelse文へ進み、「$page_flag = 0」を実行して入力ページが表示されます。

シンプルでしたが、以上で多重送信を防ぐセッションの実装は完了です。

今回で全10回にわたるワークショップ「フォームを作る」は終了となります。
ここまで読み進めていただきありがとうございました。
その他、フォームに追加して欲しい機能、フィードバックにつきましては、お問い合わせページよりご連絡ください。

今回作成したコードはこちら:vol.10のサンプルコード - GitHub

前のページアップロードしたファイルをメールに添付する

記事一覧

  1. お問い合わせフォームを作る
  2. お問い合わせフォームの入力ページを作成する
  3. フォームの確認ページ&完了ページを作成する
  4. 自動返信メールの実装
  5. 入力値の引き継ぎ
  6. 入力値のサニタイズ
  7. 入力値のバリデーション
  8. 入力項目に合わせた様々なバリデーション
  9. フォームにファイルアップロード機能をつける
  10. アップロードしたファイルをメールに添付する
  11. セッションでフォームの多重送信を防ぐ

前の記事

記事一覧