防止多个非预期的PHP表单提交

时间:2015-09-20 19:59:23

标签: javascript php mysql forms form-submit

我为一个青少年体育项目运营一个网站,该网站使用简单的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);

?>

2 个答案:

答案 0 :(得分:2)

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

示例:

<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';
    }

  }

答案 1 :(得分:2)

我不想阅读你的代码所以我会提出一个策略。

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

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

如果这会导致冲突,您可以引入限制,强制每个条目对于表中其他字段的组合是唯一的。这称为复合键(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);