防止多个意想不到的PHP表单提交



我为一个青少年体育项目运行一个网站,该网站使用简单的PHP脚本来操作存储在MySQL数据库中的数据,该脚本的功能包括时间表、排名和分数报告。

比赛结束后,获胜教练将访问该特定比赛的分数报告表格,输入信息,并单击提交以相应地更新赛程和排名。然后它们被自动重定向到它们来自的调度页面。

然而,一个赛季中有几次,教练会无意中重复提交分数(有时会创建多达三到四个实例),这并不影响在赛程表上发布的结果,但确实会使排名中的数据不正常。我不确定这是如何完成的,但我正在努力解决这个问题。

我一直在尽可能多地在这里和网络上阅读,并相信我需要实现某种令牌系统的报告脚本,但我不确定如何确切地编写代码?任何建议都将非常感激。下面是脚本本身:

<?php
// Connect to the database:
require ('../mysqli_connect.php');
// Validate the school:
if (empty($_POST['school'])) {
echo "You forgot to enter your school.<br>";
$validate = 'false';
} elseif ($_POST['school'] != $_POST['away_team'] && $_POST['school'] != $_POST['home_team']) {
echo "Your school does not match one of the two on file for this game.<br>";
$validate = 'false';
} else {
$school = mysqli_real_escape_string($db, trim($_POST['school']));
$validate = 'true';
}
// Validate the password:
if (empty($_POST['pass'])) {
echo "You forgot to enter your password.<br>";
$validate = 'false';
} else {
$pass = mysqli_real_escape_string($db, trim($_POST['pass']));
$validate = 'true';
}
// Validate the away score:
if (!isset($_POST['away_score'])) {
echo "You forgot to enter the away score.<br>";
$validate = 'false';
} elseif (!is_numeric($_POST['away_score'])) {
echo "You entered an invalid score for the away team.<br>";
$validate = 'false';
} else {
$away_score_confirm = mysqli_real_escape_string($db, trim($_POST['away_score']));
$validate = 'true';
}
// Validate the home score:
if (!isset($_POST['away_score'])) {
echo "You forgot to enter the home score.<br>";
$validate = 'false';
} elseif (!is_numeric($_POST['$home_score']) && $_POST['$home_score'] < 0 ) {
echo "You entered an invalid score for the home team.<br>";
$validate = 'false';
} else {
$home_score_confirm = mysqli_real_escape_string($db, trim($_POST['home_score']));
$validate = 'true';
}
// Determine the winner and loser, and set variables:
if ($_POST['away_score'] > $_POST['home_score']) {
$winner = mysqli_real_escape_string($db, trim($_POST['away_team']));
$winner_score = mysqli_real_escape_string($db, trim($_POST['away_score']));
$loser = mysqli_real_escape_string($db, trim($_POST['home_team']));
$loser_score = mysqli_real_escape_string($db, trim($_POST['home_score']));
$tie = 'no';
} else if ($_POST['away_score'] < $_POST['home_score']) {
$winner = mysqli_real_escape_string($db, trim($_POST['home_team']));
$winner_score = mysqli_real_escape_string($db, trim($_POST['home_score']));
$loser = mysqli_real_escape_string($db, trim($_POST['away_team']));
$loser_score = mysqli_real_escape_string($db, trim($_POST['away_score']));
$tie = 'no';
} else if ($_POST['away_score'] == $_POST['home_score']) {
$tie = 'yes';
$tie1 = mysqli_real_escape_string($db, trim($_POST['away_team']));
$tie2 = mysqli_real_escape_string($db, trim($_POST['home_team']));
$tie_score = mysqli_real_escape_string($db, trim($_POST['away_score']));
}
// Declare remaining hidden inputs as variables:
$league = $_POST['league'];
$table = mysqli_real_escape_string($db, $_POST['table']);
$game_id = mysqli_real_escape_string($db, $_POST['game_id']);
$sport = $_POST['sport'];
// Declare remaining hidden inputs as variables:
$standings_league = $table . "_standings";
// If all conditions are met, process the form:
if ($validate != 'false') {
$q1 = "SELECT school_id FROM user_schools WHERE (school_name='$school' AND pass='$pass')";
$r1 = mysqli_query($db, $q1);
$num = mysqli_num_rows($r1);
if ($num == 1) {
    // Get the game ID:
    $q2 = "SELECT $game_id FROM $table";
    $r2 = mysqli_query($db, $q2);
    // Get the row for the game ID:
    $row = mysqli_fetch_array($r2, MYSQLI_NUM);
    // Perform an UPDATE query to modify the game scores:
    $q3 = "UPDATE $table SET home_score='$home_score_confirm', away_score='$away_score_confirm' WHERE game_id=$row[0]";        
    $r3 = mysqli_query($db, $q3);
    if (mysqli_affected_rows($db) == 1) {
        $confirm = 'true';
    } else {
        $confirm = 'false';
    }
    // Update the winning team in the standings:
    $q4 = "SELECT school_id FROM $standings_league WHERE school_name='$winner'";
    $r4 = mysqli_query($db, $q4);
    // Get the row for the school:
    $row2 = mysqli_fetch_array($r4, MYSQLI_NUM);
    $q5 = "UPDATE $standings_league SET games=games + 1, win=win + 1, pts_for=pts_for + '$winner_score', pts_against=pts_against + '$loser_score' WHERE school_id=$row2[0]";
    $r5 = mysqli_query($db, $q5);
    $q6 = "UPDATE $standings_league SET pct=(win / games), avg_for=(pts_for / games), avg_against=(pts_against / games) WHERE school_id=$row2[0]";
    $r6 = mysqli_query($db, $q6);        
    // Update the losing team in the standings:
    $q7 = "SELECT school_id FROM $standings_league WHERE school_name='$loser'";
    $r7 = mysqli_query($db, $q7);
    // Get the row for the school:
    $row3 = mysqli_fetch_array($r7, MYSQLI_NUM);
    $q8 = "UPDATE $standings_league SET games=games + 1, loss=loss+1, pts_for=pts_for + '$loser_score', pts_against=pts_against + '$winner_score' WHERE school_id=$row3[0]";
    $r8 = mysqli_query($db, $q8);
    $q9 = "UPDATE $standings_league SET pct=(win / games), avg_for=(pts_for / games), avg_against=(pts_against / games) WHERE school_id=$row3[0]";
    $r9 = mysqli_query($db, $q9);
    if ($confirm != 'false') {
        header('Location: schedules_' . $sport . '_' . $league . '.html?league=' . $league .'&table=' . $table);
    } else {
        echo "The scores could not be reported due to a system error. Apologies for the inconvenience. If this problem continues, please contact us directly.";
    }
} else {
    echo "Your school and password combination do not match those on file for this game.";
}       
}
mysqli_close($db);
?>

我猜这些教练只是在表单等待服务器响应时多次点击提交按钮。你可以使用JS在第一次点击后禁用(或隐藏)按钮:

var button = document.querySelector('input[type=submit]'); // Use whatever selector is appropriate here
button.addEventListener('click', function(ev) {
    if (!button.classList.contains('submitting')) { // If this is our first click...
        button.className += ' submitting';
    } else { // Otherwise prevent submission
        ev.preventDefault();
    }
});

如果你有jQuery可用,你也可以通过JS处理整个提交过程,并在那里阻止它。

你应该意识到在屏幕上呈现某种反馈,让用户知道提交目前正在进行中,这也有助于减轻一些按钮的混乱。

一种解决方案是向表单添加唯一的值,并在表单提交时将该值添加到会话中。如果他们点击提交按钮超过一次(可能正在发生的事情),它将只接受一个提交

的例子:

<form>
  <input type="hidden" name="submit_id" value="<?php echo mt_rand(); ?>">
  // rest of the form
</form>

Php文件接收:

<?php
  session_start();
  if ( isset( $_POST['submit_id'] ) ) {
    if ( !isset( $_SESSION['submit_id'] ) ) {
      $_SESSION['submit_id'] = array();
    } 
    if ( !in_array( $_POST['submit_id'], $_SESSION['submit_id'] ) ) {
      // validate posted values
      // when data is valid, register form as submitted
      $_SESSION['submit_id'][] = $_POST['submit_id'];
      // add the submitted form data to database
    }
    else {
      echo 'Your data has already been submitted';
    }
  }

我不想读你的代码,所以我建议一个策略。

我同意@relic。你的教练可能正在双击按钮。

如果你可以假设不同的用户永远不会在同一秒内提交两个表单,那么你可以"过滤"你的表,在任何给定的秒内只接受一个条目。为(新)seconds列创建索引,并使其唯一。如果一个表项在那一秒内已经退出,这将防止向该表插入行。

如果这导致冲突,您可以引入限制,强制每个条目对于表中其他字段的组合是唯一的。这被称为复合键(SQL)。你可以为这款游戏和用户设置一个分数注册。

MySQL:

create table scores (game_id int, user_id int, score int );
alter table scores add unique index uniq_gus (game_id, user_id, score);
insert into scores (game_id, user_id, score) values (1, 1, 10);
insert into scores (game_id, user_id, score) values (1, 1, 10);
ERROR 1062 (23000): Duplicate entry '1-1-10' for key 'uniq_gus'

此外,您可能希望防止重复提交(假设使用jQuery):

(function($){
var btn =  $('button[type="submit"]');
btn.click(function(event){
    event.preventDefault();
    btn.attr('disabled','disabled');
    $.ajax({
        url: 'http://foo.bar/form-endpoint.php',
        success: function (data, status, xhr) {
           btn.removeAttr('disabled');
        },
    })
})
})(jQuery);

最新更新