Have a project where a user fills out a succession of forms in a multi-step process. Each of the form screens have a next/back button that does an AJAX call and does a POST to a PHP controller. The PHP Controller then writes the form data to SESSION.
I need to insert a test for a certain state. Let's say the user selects 'California' as a state. If so, then they are taken OUT of the flow and shown another page that is an error/info page. Part of the functionality of this page is that it needs to take the stored Session values and write to a MySQL table (first_name, last_name, email)
So far so good. Part of my confusion is WHERE is the proper place to test for this value. Since we are MVC, I could test for it at the AJAX/JS level, or do the test in the controller. I chose to just test directly after the POST:
// If the customer has chosen CA as a state we must stop process
if ($('#employee_state_origin').val() == 'CA') {
PAGE_STATUS.destination = 'error-page';
}
The code uses PAGE_STATUS.destination as a way to keep track of what page is coming up next, what page is behind, etc. etc. Basically a keep-alive sort of history.
The problem is this. Once I do this test, of course the POST has already happened, and the controller puts the values in SESSION like so:
private function storeUserData(&$posted_data, &$user_input)
{
if ($this->response_body->has_error) {
return;
}
//If we just got data posted from employee entrance and it already exists - we need to reset all the data
if ($posted_data->step === 'employee_entrance' && !empty($_SESSION[SELF::SESSION_KEY][$posted_data->step])) {
$_SESSION[SELF::SESSION_KEY] = array();
}
if ($posted_data->step === 'member_information' || $posted_data->step === 'physician_information') {
$user_input->createCustomObject($posted_data);
$_SESSION[SELF::SESSION_KEY][$posted_data->step][] = $posted_data;
} else {
$_SESSION[SELF::SESSION_KEY][$posted_data->step] = $posted_data;
}
}
After the data is posted and written into SESSION the page redirects and shows the error page. On the error page, I am writing procedural code to dump into a MySQL table.
The issue is when I first start the browser, of course the Session is empty. I go to the form, put in first_name, last_name and email and then hit Next... the Ajax call happens, the POST values are good, the Session gets written....
But after the redirect, on the error page I have to start the session...
session_start();
$first_name = $_SESSION['wonderousBooksProject']['employee_entrance']->employee_first_name;
The problem is $first_name is empty when echo-ing it out as well as the other values. If I go to the page again, and put in different information and then go to the error page again.... it prints out the PREVIOUS ENTRY.
So, it's saving the session information, but my target page will not retrieve the latest entry...it retrieves the one previous.
Anyone have a thought about what is happening. I have used session_destroy() to end session, session_write_close(), and lots of other things, but cannot understand why it writes the session value the first time, shows it as blank, then the next entry is persona non grata and it displays the previous one?
Related
I want to stop duplicate form submissions to my webpage. Everywhere I look for help, I see use a $_SESSION variable; but I fail to see how sessions and forms could possibly connect to each other outside the context of multiple user accounts.
Can someone show me exactly how this process works?
Edit: I don't want to stop the form being submitted multiple times; only to prevent resubmitting the form with the same values (on page refresh for example).
After the form is processed, the user should see a success/error message and then the form again.
Basically you need to use the unique SESSION value twice. Once somewhere in the form you are trying to submit and once stored in the $_SESSION variable. When the form is POSTED and both values are a match then we have a successful submission, when the form is POSTED but the values are different, no submission occurs.
I.e.:
somewhere on the top of your PHP page:
<?php
session_start(); // first line of your PHP page
$_SESSION['secretFormValue'] = isset($_SESSION["secretFormValue"]) ? $_SESSION["secretFormValue"] : md5(microtime()); // generate some unique session value
// assign submitted **secretFormValue** from your form to a local variable
$secretFormValue = isset($_POST["secretFormValue"])? filter_var($_POST["secretFormValue"], FILTER_SANITIZE_STRING) : '';
// check if the value is present in the **secretFormValue** variable
if($secretFormValue != '') {
// check if both values are the same
if($_SESSION["secretFormValue"] == $secretFormValue) {
// Process form values & submission ...
// add your own code...
unset($_SESSION["secretFormValue"]);
} else {
echo "Duplicate submission";
}
} else {
// do some other thing
}
somewhere below in your form:
<input type="hidden" name="secretFormValue" value"<?php echo $_SESSION['secretFormValue']; ?>">
***I did not test this so please comment in case there is a bug, thx.*
Edit:
If you need to prevent submit on page-refresh you may include clearing all the POST values on successful submit so the refresh would fail because of the empty POST i.e.:
unset($_POST); // place it right before unset($_SESSION["secretFormValue"]);
or
Include a redirect to a different page (i.e. Thank You Page) after submission i.e.:
header("Location:ThankYouPage.php"); // place it right after unset($_SESSION["secretFormValue"]);
or just found this one in other SO post:
https://en.wikipedia.org/wiki/Post/Redirect/Get
You could simply set a $_SESSION['submittedForm'] = true; variable once the form is submitted by that visitor. You could then check for that session data next time they visit the form page and either not show the form, or throw an error message if they try to submit it again.
I suggest an alternative approach. The problem you're trying to solve comes in two variants:
How can I prevent/detect a form from being submitted multiple times (usually accidentally) when the user refreshes the page after submitting?
How can I prevent the submission (or at least make it harder) of form data not coming from an actual form shown on my page?
The solution by Milan solves to both problems, but creates a problem on its own: if the user has the same page open in multiple tabs/windows (remember, they share the same session), this will break.
If you don't care about problem #2, it would be easier to do a "passive" approach, by keeping a list of form IDs in your session data that have already be submitted:
$formID = (isset($_POST["__form_id"]) ? $_POST["__form_id"] : false);
$submittedforms = (isset($_SESSION["submittedforms"]) ? $_SESSION["submittedforms"] : array());
// Check whether this form ID has been submitted before
if (in_array($formID, $submittedforms)) {
printf("Duplicate submission.");
exit;
}
// Store the ID of this form submission
$submittedforms[] = $formID;
$_SESSION["submittedforms"] = $submittedforms;
// Continue form processing...
If you need to prevent unauthorized (automated) form submission too, an active approach is needed. I would extend Milan's solution then to store multiple form IDs in your session data (in an array), one for each form generated; and then remove them one-by-one as they are submitted.
I have a login form which sends 3 post values from username, password and submit button. But my form processor has 3 pages one is validation.php which validates the field second is read.php which checks the posted values against db and third is login.php which is a result of login success. All redirect to each other respectively on success. Problem here is that when I try to access the user posted values from form in read.php (redirected page) not validate.php (action page) I get an error of undefined index.
I really don't see why you are doing all those redirects, but if you want to make the data more persistent you could use a session variable, because the $_POST superglobal is only set for the current request.
firstfile.php
<?php
session_start();
$_SESSION['posted_data'] = $_POST;
other file
<?php
session_start();
var_dump($_SESSION['posted_data']);
However as already stated you may really want to reconsider doing all the requests.
UPDATE
Besides the fact that you will loose your data you are also doing multiple (unneeded) requests to simply sumbit the form. The only redirect that should happen is to the successpage when you have done all you work. See this for more information: http://en.wikipedia.org/wiki/Post/Redirect/Get
If you are look to keep you code clean you could always just include the other files or go for an OOP approach.
You should do one page only that will do all the work. That doesn't seem too complicated of a script, so I would advise putting everthing together on one page.
You did not provide any code so I'll show you a general example. I just typed it without rereading so it's not pure PHP syntax, just the spirit:
<?php
$login=$_POST['login'];
$pwd=$_POST['pwd'];
$dbcheck = mysql_fetch_array(mysql_query("SELECT COUNT(1) FROM table WHERE user =$login and pwd = $pwd"))
if($dbcheck[0] > 0) {
//Login success
//Setup your session variables, cookies, etc
//Then you can do your redirect here
} else {
//Page for wrong login
}
Im having a strange problem.
I have a registration script for a car show's website I manage and for no reason the older code is broken. I know it worked before, but now does not.
I am setting session variables correctly. I am saving an entire Array of variables to the session and I know the session works once the form is submitted as the form reloads to a new page and I display the registration info for confirmation. However, the script then continues to a PayPal page where it takes the user through a payment process. It then redirects back to the site where the registration info is submitted to the MySQL database upon the completion of the payment process.
HOWEVER, when the users return to the confirmation page where the data is submitted, the session variable is completely blank. I have echo'ed all the variables and there's nothing there.
To test where its breaking, I put a link on the page you're taken to after filling out the form linking directly to the confirmation page where the info is dumped to the SQL server. When you link the link, the info is still there. However, if you were to take that same URL and paste it into the address bar (aka NOT using the link), the variables are blank.
So at this point, I am lost as to why the variables are fine when you link and click to the page, but not when you past the same url into the address bar and not click the link. Session is started on EVERY page and all are on the same domain name.
Any help?!
===== EDIT =====
Test Code (to give a rough idea)
How I am setting the session variables. The session_start(); function is called at the loading of every page on this site for continuity across other pages:
$storedVars = array(1 => $name, $email, $state, $year, $make, $model, $generation, $color, $now, $paymentType);
$_SESSION['registration']=$storedVars;
On the confirmation page after PayPal where the information gets sent to the database, the following code is run (again, session_start(); already called):
$storedVars = $_SESSION['registration'];
$name = $storedVars[1];
$email = $storedVars[2];
$state = $storedVars[3];
$year = $storedVars[4];
$make = $storedVars[5];
$model = $storedVars[6];
$generation = $storedVars[7];
$color = $storedVars[8];
$now = $storedVars[9];
The 10th array variable, paymentType, is not needed at this point as its only used for the page after the form is submitted)
When those variables are echo'ed on the confirm page after following through the steps on paypal OR pasting the right URL into the address bar after filling out the form, nothing is there.
HOWEVER..
When you click the link on the data validation page that follows after the form is submitted, the variables are still there.
Your last comment makes me believe that your session was set at http://yoursite.com/ and the return url you are sending to PayPal is http://www.yoursite.com/ (or vice versa). "www" is really a subdomain to your base domain and considered to be a different host as far as your browser is concerned.
Long story short, use the following for the return url when setting up your token with paypal:
$_SERVER["HTTP_HOST"] . "/Path/To/PaypalReturnPage.php"
acitvity.php
//Form start
<form action=''>
</form>
//Form End
//Get POST Values
<?php
$_POST[''];
?>
//End
if i refresh the page after form is submitted, all the posted values are resubmitted, reason because all values are in browser so they are resubmitted. When i was searching solution for this, i got info that if the form & post operation done in separate php file then no more issue in posting values on refresh.
Is this the solutions? but now i have to do both in single file & POST values should not be submitted again on refresh.. is there any way to do this???
Learn PRG Pattern so that you can do this properly :)
http://en.wikipedia.org/wiki/Post/Redirect/Get
For example, you are trying to handle a user registration form, so what you do is you get a bunch of POSTed values, and save it into your database.
if(!empty($_POST)) {
// validate and save to db
// get last inserted user_id
}
After you do that, instead of returning the same page with the previously POSTed values, you redirect the new user, for example to his profile page (assuming you have no activation requirement in place)
if(!empty($_POST)) {
// validate and save to db
// get last inserted user_id, say in $user_id
header("Location: /users/$user_id");
}
That way, the browser redirects and you won't have problem with say, double registration, whenever the user hits refresh.
After saving to your database, reload your page:
if ($_POST) {
// Save $_POST to database and other stuffs
// Reload current page to discard $_POST
header('Location: my_page.php');
}
That's called PRG or Post/Reload/Get
You can use unset($var) to unset a variable. However, I think the issue is with the browsers; some of them try to be smart and will remember form data regardless when you refresh the page. If you hit "go" or "enter" on the URL bar it does a "true" refresh though.
I might not have known what to search for to get this answer so please point me to the correct post if this has been dealt with already.
Now then, I have a little custom CMS and I want to ensure users don't re-submit their $_POST data by refreshing the page. So I've done something like this:
<?
//Start a session to hold variables I want after my redirect
session_start();
if($_POST){
//Do some php stuff and if I'm happy with the results...
$_SESSION['some_vars'] = $whatever;
//Bring me back here but without the $_POST data
header('Location: '.THIS_PAGE);
exit;
}
?>
When the script reloads I use my session variables and trash the session.
I'm wondering if anybody has a better way to handle this. It's fairly non cumbersome but I'm always looking for more efficiency.
Thanks.
EDIT: By the way, stackoverflow.com does this somehow when you post a question if what I'm doing seems unclear, but they also make a permalink while they're at it.
You have actually implemented what is called the Post-Redirect-Get pattern, and it is absolutely a correct way to do this. I do this myself. I use it so often, I usually implement some minor helper functions into my base controller classes to help me use it:
public function prgRedirect($url = null, $sessionData = null)
{
if ($sessionData !== null) {
if (! isset($_SESSION)) session_start();
$_SESSION['_PRG'] = $sessionData;
}
if ($url === null) $url = $_SERVER['HTTP_REFERER'];
header("Location: ".$url);
}
public function getPrgData()
{
if (! isset($_SESSION)) session_start();
if (isset($_SESSION['_PRG'])) {
$data = $_SESSION['_PRG'];
unset($_SESSION['_PRG']);
}
else {
$data = null;
}
return $data;
}
I usually use it with REST-style URLs, so a POST request will do whatever it has to do, save some data to session using prgRedirect(), which then redirects back to the GET url for the same resource/page. The handler for the GET method will call getPrgData() at the top of the execution and see if there's anything in the session data.
if its important that user dont insert the same data I'm sure there must be some unique column in your database (like headline maybe?). so just check if this headline already exists. you wont need to use any sessions that way
What about:
1. Generate random string (uniqid or md5)
2. Store it in session and put in into hidden input at the form
3. Check form value and session value - if it matches - process the form. Clear the session value.
There are actually two problems here:
users hitting the refresh button in their browser when the data is already saved and you are in a page which saves the data
user hits the "back" button in the browser and clicks "submit" button once again.
In the first scenario, the best scheme is to follow the GET-after-POST pattern where you use the header("location:somewhere_else.php") call to redirect the user. This way you do not have to worry about being called two times consecutively, since the page where you posted the data, is not in the browser's history list (because the server had returned 302 header).
The second scenario hurts a bit more, because the GET-after-POST does not help. If the user submits the form twice, the you may save the data twice. In this case, there may be several solutions:
put a "form identifier" (a random string) in every form you send to the client. When client submits the form, check if you already have such identifier in session data. If you don't have it, save form data and remember the identifier in user's session as "already used". If you find the identifier in session data, don't save anything - it's a duplicate.
check the database for exactly the same values that are submitted. If they match, do not save a duplicate. However, the user may have clicked "back" button, changed some piece of data and then re-submitted the form.