Resubmitting forms upon page refresh or reload in PHP

A common problem with forms is the annoying resubmit when the user reloads or refreshes a page. This can result in duplicate database records if not dealt with properly.

There are a number of simple solutions to this problem, I’ll go through a couple below.

1. Use two files (one with the form on and one to process), and redirect back to the first page when the processing is done.

Here’s some sample code for the form page (form.htm):

<form name="form1" method="post" action="process.php">
<input type="text" name="text" id="text">
<input type="submit" name="submit" id="submit" value="Submit">
</form>

and here’s some sample code for the processing page (process.php):

<?php
if (isset($_POST['submit'])) {
$text = $_POST['text'];
$query = mysql_query("Insert into our database etc");
header("Location: form.htm");
}
?>

This uses PHP header to redirect the user back to the form page when the form is processed, or if the user directly visits process.php without the form submission. Bear in mind this is only example code and so is not complete or secure.

2. Use two files (one with the form on and to process and one when finished), redirecting to the second page when finished.

Here is some sample code for the process/form page (form.php):

<?php
if (isset($_POST['submit'])) {
$text = $_POST['text'];
$query = mysql_query("Insert into our database etc");
}
header("Location: thanks.htm");
?>
<form name="form1" method="post" action="form.php">
<input type="text" name="text" id="text">
<input type="submit" name="submit" id="submit" value="Submit">
</form>

and here’s the code for the final page (thanks.htm):

<p>Thanks for filling out our form!</p>

The user can still refresh the final page as much as they want as the processing was done elsewhere.

3. Use one file, doing all the processing and then displaying a thank you message.

This uses a $_GET to tell the script we’ve finished and display a message (form.php):

<?php
if ($_GET['success']) {
echo "Your text was saved successfully!";
} elseif (isset($_POST['submit'])) {
$text = $_POST['text'];
$query = mysql_query("Insert into our database etc");
header("Location: form.php?success=1");
}
?>
<form name="form1" method="post" action="form.php">
<input type="text" name="text" id="text">
<input type="submit" name="submit" id="submit" value="Submit">
</form>

I suppose you could change the echo to a die if you didn’t want to show the form, or add in a final else like this:

<?php
if ($_GET['success']) {
echo "Your text was saved successfully!";
} elseif (isset($_POST['submit'])) {
$text = $_POST['text'];
$query = mysql_query("Insert into our database etc");
header("Location: form.php?success=1");
} else {
?>
<form name="form1" method="post" action="form.php">
<input type="text" name="text" id="text">
<input type="submit" name="submit" id="submit" value="Submit">
</form>
<?php } ?>

so that the form would not be displayed when the Success text was.

4. Involve sessions

You could also involve sessions in your form by storing the POST variables in a session before processing, eg:

$_SESSION['postdata'] = $_POST;

and clearing the session once the processing had been done, eg:

unset($_SESSION['postdata'])

You could also enter a timestamp with each posted form and store this in a database, checking before processing for identical timestamps, it depends on the nature of your application and your personal preference.

Any comments/improvements welcome!

Register_Globals and Session Side Effects in PHP

Wrote a little login script (as part of a larger project) which uses PHP to store sessions with a user’s data in and got the following error.

Warning: Unknown: Your script possibly relies on a session side-effect which existed until PHP 4.2.3. Please be advised that the session extension does not consider global variables as a source of data, unless register_globals is enabled. You can disable this functionality and this warning by setting session.bug_compat_42 or session.bug_compat_warn to off, respectively. in Unknown on line 0

Oh, well that’s no good. Looked through the code, and found I had spelt one variable wrong.

$_SESSION['user_id'] = $user;

but $user did not exist, it should have been

$_SESSION['user_id'] = $user_id;

So the error/warning appeared because I was referencing a null (or undefined) variable into a session; something which would require register_globals to work.

Register globals would allow you to call file.php?foo=bar; and the script would create a variable $foo with a value of bar automatically – which is not very secure as variables can be pushed into a script (perhaps bypassing form validation etc). Personally I would never use register_globals (set it to off in PHP.ini or your .htaccess) as mistakes like the one I made would go unnoticed & could be abused.

I googled the error after I found that mistake to confirm that was the problem and was amazed to see people simply turning the warnings off rather than fixing the problem…talk about short cuts!

Integrating your existing site into phpBB3

One way of making an existing site dynamic is to integrate it with a phpBB forum. This is actually very easy, and allows you to quickly pull data about your users; control page access by groups and more.

Page type

The page you’re integrating phpBB with needs to be a php page! (pretty obvious really seeing as phpBB is a PHP forum)

Connect to phpBB to get variables, etc.

You will need to include a file containing the phpBB connection information (essentially plugs the page into the rest of phpBB). This file should contain the following

<?php
define('IN_PHPBB', true);
$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH : 'change_this_to_phpbb_dir';
$phpEx = substr(strrchr(__FILE__, '.'), 1);
include($phpbb_root_path . 'common.' . $phpEx);
// Start session management
$user->session_begin();
$auth->acl($user->data);
$user->setup();
?>

Save this file as phpbb.php, or put this code in some other file you will include in every page you want to integrate. All this code does is define where phpbb can be found, and include the common.php file from within phpbb. It also starts a user session, which we can use on our page.

Get our page to interact with phpBB

So now we go to the page we want to integrate with phpBB. In this case I’m going to use a blank file as an example but obviously you can use any file. This bit of code checks to see if the user is logged in or not and displays an appropriate message

<?php include_once("phpbb.php"); ?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>phpBB conn test</title>
</head>
<body>
<?php
// PHPBB LOGIN AUTH
if ($user->data['user_id'] == ANONYMOUS) {
?>
Welcome, anomalous!
<?php
} else {
?>
Welcome back, <?php echo $user->data['username_clean']; ?> | You have <?php echo $user->data['user_unread_privmsg']; ?> new messages
<?php } ?>
</body>
</html>

This will display “Welcome, anomalous!” if you’re not logged into phpBB or “Welcome back, username | You have 0 new messages” if you are logged in. Note the placement of the include_once ABOVE the <head> tag – this is required if you don’t want any errors.

This is a very simple example and doesn’t tell our user how to log in or log out…which are pretty critical activities.

How about a login form?

The simple way to login is to push all logins through the phpBB system. This form does just that.

<form method="POST" action="forum/ucp.php?mode=login">
Username: <input type="text" name="username" size="20"><br />
Password: <input type="password" name="password" size="20"><br />
Remember Me?: <input type="checkbox" name="autologin"><br />
<input type="submit" value="Login" name="login">
<input type="hidden" name="redirect" value="">
</form>

This collect the login data and posts it to the phpBB ucp.php. We can make phpBB redirect the user back to any page by changing the value of the redirect input field.

<input type="hidden" name="redirect" value="../somefile.php">

Once this form has been posted the user will be logged in. But how about logging out?

Logging out of phpBB

You could just navigate to the forum and click the link there, but hey – might as well do this in as few clicks as possible. So we need a link to log out…

<a href="somefile.php?cp=logout">Log Out</a>

and in the same file we could put the following at the top of the document:

<?php
// check for logout request
$cp = $_GET['cp'];
// is it a logout? then kill the session!
if ($cp == "logout") {
$user->session_kill();
$user->session_begin();
echo "Successfully Logged Out.";
}
?>

So when cp is set to logout (when the user visits somefile.php?cp=logout) the session containing the userdata is destroyed and reset. ‘Succssfully Logged Out’ is also shown for the user’s benefit.

Combining conditional, login & logout

As a summary, I’ve combined what we looked at above.

In phpbb.php:

<?php
define('IN_PHPBB', true);
$phpbb_root_path = (defined('PHPBB_ROOT_PATH')) ? PHPBB_ROOT_PATH :  'change_this_to_phpbb_dir';
$phpEx = substr(strrchr(__FILE__, '.'), 1);
include($phpbb_root_path . 'common.' . $phpEx);
// Start session management
$user->session_begin();
$auth->acl($user->data);
$user->setup();
?>

In somefile.php:

<?php include_once("phpbb.php");
// check for logout request
$cp = $_GET['cp'];
// is it a logout? then kill the session!
if ($cp == "logout") {
$user->session_kill();
$user->session_begin();
echo "Successfully Logged Out.";
}
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>phpBB conn test</title>
</head>
<body>
<?php
// Page login notice
if ($user->data['user_id'] == ANONYMOUS)
{
?>
<center>Welcome! Please login below or click <a href="forum/ucp.php?mode=register">here</a> to register.<br />
<form method="POST" action="forum/ucp.php?mode=login">
Username: <input type="text" name="username" size="20"><br />
Password: <input type="password" name="password" size="20"><br />
Remember Me?: <input type="checkbox" name="autologin"><br />
<input type="submit" value="Login" name="login">
<input type="hidden" name="redirect" value="../somefile.php">
</form>
<?php
} else { ?>
Welcome back, <?php echo $user->data['username_clean']; ?> | You have <?php echo $user->data['user_unread_privmsg']; ?> new messages | <a href="somefile.php?cp=logout">Log Out</a>
<?php } ?>
</body>
</html>

This will give you basic integration with an existing phpBB forum, we’ll look at further integration including private messenging in contact boxes soon.