PHPプログラミング

ベストプラクティス

フォームを作る vol.10

フォームの連続した多重送信を防ぐ

  • このエントリーをはてなブックマークに追加

今回は完了ページでリロード(再読み込み)されたりしたときなどに発生する、フォームの多重送信を防ぐ機能を実装します。

目次

  1. #  フォームの多重送信を防ぐ機能を追加する
  2. #  実装の概要
  3. #  確認ページへの実装
  4. #  完了ページへの実装

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

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

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

前回までに作成したコードはこちらからダウンロードできます:ダウンロード(約3.5KB)

実装の概要

コードを書く前に、今回実装する内容を簡単に説明します。

フォームは「入力ページ」「確認ページ」「完了ページ」の3つに分かれていますが、今回実装するページは「確認ページ」「完了ページ」の2ページです。
「入力ページ」と「確認ページ」の間は「戻る」ボタンなどで何度か行き来する可能性があるため、入力ページの時点ではセッションは不要。

もし完全に「入力ページ」から「完了ページ」までの移動過程を追いたいとなると話しが変わりますが、今回はあくまで「2重送信を防ぐ」という部分にだけフォーカスしていきます。

実装内容のイメージ

「セッションを作る」では「確認ページを通過したよ」という印。
そして「完了ページ」の「セッションを確認&削除」は確認ページを通過してきたことを確認しつつ、確認できた時点で削除します。
確認できたら削除することで、もしリロードして再度セッションを確認してもすでにセッションはありません。
セッションが無い場合は入力ページを表示し、ある場合は通常の送信処理を実行。
これで2重送信は防ぐことができます。

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

確認ページへの実装

まずは確認ページから「セッションを作る」という処理を記述します。
赤い箇所が追記したコードです。

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」を実行しています。

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

フォームらしい形になってきたところで、全10回にわたる「フォームを作る」シリーズは終了となります。
その他、フォームに追加して欲しい機能、フィードバックにつきましては、お問い合わせページよりご連絡ください。

今回作成したファイル:ダウンロード(約3.6KB)

  • このエントリーをはてなブックマークに追加