フォームの再送信を防ぎたい - PHPプロ!Q&A掲示板

4917

  • 募集中!! 0P

フォームの再送信を防ぎたい

質問日時 / 2018年1月30日 12:25    回答数 / 1件

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

キーワード / 掲示板   

掲示板で投稿した直後にheader関数を使って防ごうと思ったんですがうまくいきません
どうしてでしょうか
  1. <?php
  2. $err_msg1 = "";
  3. $err_msg2 = "";
  4. $name = ( isset( $_POST["name"] ) === true ) ?$_POST["name"]"";
  5. $comment  = ( isset( $_POST["comment"] )  === true ) ?  trim($_POST["comment"] )  : "";
  6.  
  7. //投稿がある場合のみ処理を行う
  8. if (  isset($_POST["send"] ) ===  true ) {
  9.     if ( $name   === "" ) $err_msg1 = "※名前を入力してください"
  10.  
  11.     if ( $comment  === "" )  $err_msg2 = "※コメントを入力してください";
  12.  
  13.     if( $err_msg1 === "" && $err_msg2 ==="" ){
  14.         if ( get_magic_quotes_gpc() ) {
  15.             $name = stripslashes( $name );
  16.             $comment = stripslashes( $comment );
  17.         }
  18.         
  19.         $name = htmlspecialchars ($name)//HTMLタグ禁止
  20.         $comment = htmlspecialchars ($comment)//HTMLタグ禁止
  21.         
  22.         $comment = str_replace(array("\r""\n")""nl2br($comment));
  23.         
  24.         $fp = fopen( "datas/commentdata.txt" ,"a" );
  25.         fwrite( $fp ,  $name."\t".$comment."\t".date("Y/n/j H:i")."\n");
  26.         fclose( $fp );
  27.         header('');
  28.     };
  29.  
  30. };
  31.  
  32. $fp = fopen("datas/commentdata.txt","r");
  33.  
  34. $dataArrarray();
  35. while( $res = fgets( $fp)){
  36.     $tmp = explode("\t",$res);
  37.     $arr = array(
  38.         "name"=>$tmp[0],
  39.         "comment"=>$tmp[1],
  40.         "date"=>$tmp[2]
  41.     );
  42.     $dataArr[]$arr;
  43. };
  44.  
  45.  
  46. ?>
  47. <!DOCTYPE>
  48. <html lang="ja">
  49.     <head>
  50.         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  51.         <link rel="stylesheet" type ="text/css" href="bbs.css">
  52.         <title>掲示板</title>
  53.     </head>
  54.     <body>
  55.        <div class="wepper">
  56.             <div class="commentWrite">
  57.                 <form method="post" action="">
  58.                     <div>
  59.                         <p>名前<input type="text" name="name" autocomplete="off" value="<?php echo $name?>" ></p>
  60.                     </div>
  61.                     <div>
  62.                         <textarea  name="comment" placeholder="コメントの内容を入力してください"><?php echo $comment?></textarea>
  63.                     </div>
  64.                     <div>
  65.                         <input type="submit" name="send" value="コメントする" >
  66.                     </div>
  67.                 </form>
  68.             </div>
  69.             <div class="err">
  70.                 <p><?php echo $err_msg1?></p>
  71.                 <p><?php echo $err_msg2?></p>
  72.             </div>
  73.             <div class="comments">
  74.                 <h2>コメント</h2>
  75.                 <?php foreach( $dataArr as $data ):?>
  76.                     <div class="comment">
  77.                         <div>
  78.                             <p><b><?php echo $data["name"]?></b><span class="date"><?php echo $data["date"]?></span></p>
  79.                         </div>
  80.                         <div>
  81.                             <p class="commentText"><?php echo $data["comment"]?></p>
  82.                         </div>
  83.                     </div>
  84.                 <?php endforeach;?>
  85.             </div>
  86.         </div>
  87.     </body>
  88. </html>
教えてください


ツリー一覧

┗A01shimix>うまくいきません どう「うまくいかない」のでし

回答一覧

並び替え:

A01
answerershimix [1月30日 18:40] (最終編集:1月30日 18:42)

>うまくいきません

どう「うまくいかない」のでしょうか?提示されているソースではheader関数はありますが、与えるヘッダ内容が全部省略されていますのでレスポンスヘッダの種類すらわかりません。
現象の説明もなくこちらで再現することもできないソースではどういう現象で困っているのかわかりません。

#おそらくはLocationヘッダだとは推測しますが、そういうことでいいんでしょうか

Locationヘッダが有効なのはF5キーなどでのリロードに伴う再POSTですが、それは確認されていますでしょうか?

当然ですが、Locationヘッダではサーバ側での処理に時間がかかっている間(Locationヘッダでブラウザが画面遷移する前)の再POSTには対応できません。ワンタイムトークンなどで対策します。


(蛇足)
そもそも提示されたスクリプトではファイル書き込み時にロックすらしていません。そのあたりを改善するのが先だと思います。
またCSVデータの読み書きにはfgetcsv/fputcsvを使うべきです。そうすればデータ書き込み前にhtmlspecialcharsせずに(定石どおり)表示の直前で行えます。

(例)
  1. <?php
  2. session_start();
  3. $err_msg = array();
  4. $name = isset( $_POST["name"]) ? trim($_POST["name"]) : "";
  5. $comment  = isset( $_POST["comment"]) ?  trim($_POST["comment"] ) : "";
  6.  
  7. if (isset($_POST["send"])) {
  8.     // トークンのチェック
  9.     $token = isset($_POST['token']) ? $_POST['token'] : '';
  10.     $session_token = isset($_SESSION['token']) ? $_SESSION['token'] : '';
  11.     if ($token === '') { die('illegal access')}
  12.     if ($token !== $session_token) { die('abort')}
  13.     unset($_SESSION['token']);
  14.  
  15.     if ($name  === '') $err_msg[] = '名前を入力してください';
  16.     if ($comment  === '')  $err_msg[] = 'コメントを入力してください';
  17.  
  18.     if(count($err_msg) === 0){
  19.         $fp = fopen('datas/commentdata.txt''a');
  20.         flock($fpLOCK_EX);
  21.         fputcsv($fp[$name$commentdate('Y/n/j H:i')]);
  22.         // 処理に時間がかかる場合を想定してみる
  23.         sleep(3);
  24.         flock($fpLOCK_UN);
  25.         fclose($fp);
  26.         // 自分自身にLocationヘッダで遷移
  27.         header('Location: http://xxxx/xxxxx.php');
  28.         exit;
  29.     }
  30. }
  31. // データ読込み
  32. $fp = fopen('datas/commentdata.txt''r');
  33. flock($fpLOCK_SH);
  34. $dataArrarray();
  35. while($res = fgetcsv($fp)){
  36.     if (count($res) < 3) { continue}
  37.     $dataArr[]['name'=>$res[0]'comment'=>$res[1]'date'=>$res[2]];
  38. };
  39. flock($fpLOCK_UN);
  40. fclose($fp);
  41.  
  42. // トークンの準備
  43. $token = md5(uniqid(rand()true));
  44. $_SESSION['token'] = $token;
  45. ?>
  46. <!DOCTYPE>
  47. <html lang="ja">
  48.     <head>
  49.         <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  50.         <link rel="stylesheet" type ="text/css" href="bbs.css">
  51.         <title>掲示板</title>
  52.     </head>
  53.     <body>
  54.        <div class="wepper">
  55.             <div class="commentWrite">
  56.                 <form method="post" action="">
  57.                     <input type="hidden" name="token" value="<?= $token?>">
  58.                     <div>
  59.                         <p>名前<input type="text" name="name" autocomplete="off" value="" ></p>
  60.                     </div>
  61.                     <div>
  62.                         <textarea  name="comment" placeholder="コメントの内容を入力してください"></textarea>
  63.                     </div>
  64.                     <div>
  65.                         <input type="submit" name="send" value="コメントする" >
  66.                     </div>
  67.                 </form>
  68.             </div>
  69. <?php
  70. if (count($err_msg) !== 0) {
  71.   printf('            <div class="err"><p>%s</p></div>'implode("</p><p>"$err_msg));
  72. }
  73. ?>
  74.             <div class="comments">
  75.                 <h2>コメント</h2>
  76. <?php
  77. foreach ($dataArr as $data) {
  78.     printf('<div class="comment"><div><p><b>%s</b><span class="date">%s</span></p></div><div><p class="commentText">%s</p></div></div>'htmlspecialchars($data['name']ENT_QUOTES)$data['date']nl2br(htmlspecialchars($data['comment']ENT_QUOTES)));
  79. }
  80. ?>
  81.             </div>
  82.         </div>
  83.     </body>
  84. </html>

この意見に回答する

ツリーへ TOPへ

<<質問一覧へ



Pick Up Q&A

Q
ログファイルの中の空のデータ行を削除したい
 このエントリーをはてなブックマークに追加 
A
ログのデータ個数(列数)が固定で、空のログが"<><><>"だと既知であれば if ($line === "<><><>") { continue; } で読み飛ばしてもいいのでは? ...

>>続きを読む

まずは配列や文字列の扱いから、じっくり勉強して行きましょう。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)