I am working on a PHP project where the client wants multipage form data submitted. For instance, here is the process the form follows:
Create new entity.
Determine entity type.
Fill in entity-specific fields.
On each page, the form is POSTed to the current page. Validation is performed server-side, and if validation is successful the user is redirected to the next step.
I've determined that, in order to keep track of the user's progress, session data should be used. However, my concern is that, if the user opens two tabs and goes through the form in parallel, how can I keep track of what entity is being processed in each tab? Is this a scenario that can even be handled at all?
There are different 2 approaches.
If a user allowed to fill 2 forms at once, just add an unique identifier to each, and keep track them in the session.
If not - the things become simpler: just keep track of the steps passed and just show a warning in case of the previously passed step submitted.
This is a page flow issue. Do NOT use session to deal with it. You are correct that tabs or new windows would cause you trouble.
I don't really understand the point of this bit:
On each page, the form is POSTed to the current page. Validation is
performed server-side, and if validation is successful the user is
redirected to the next step.
Sounds like a "post-back", but why? POST the data from the page to a controller (or script) that does the necessary ss validation. After validation, deal with the data how you need and determine what the next step should contain. Then send the input form for that next step down in the response. Repeat until you are finished.
Related
I'm trying to create a form with 3 steps:
fill the form
check if data is correct (show input)
thank you
With an advice of some people here (regarding my previous question) I've changed my way of doing it from mainly PHP + js-validation to mainly js + PHP process data.
I need an advice with how to deal with this now.
Previously I've had a PHP if/else that determined which step to show and kept data in $_SESSION for 2nd step and possible corrections back in 1st step.
Now I'm wondering if I really need two ajax calls (first to process data in order to show it - 2nd form step uses $_SESSION to display data input in 1st step; second to generate e-mail and pdf with given data - same $_SESSION as step2).
Maybe a good solution would be to put data with javascript into 2nd step aswell and use $_SESSION only in the final processing and generating.
What's the common/your approach to this problem?
Here's the normal flow:
User loads page with form on it. Fills it out. Submits it.
You can validate every field as they fill it out (instant feedback which is nice from a user's perspective) or validate using the onSubmit event (in jquery $('#formID').submit). You don't allow them to submit if it doesn't pass, return false from the submit function.
In case they don't have JS enabled (you can try to prevent them from using it w/o JS but in reality they can just use curl -d "value1=foo&value2=foo2&value3=foo3" http://example.org/page/ to get around you) you have to validate the data on the server, too. JS isn't enough.
If it doesn't pass server validation, you can redirect them back to the form using the Location: http://example.org header or echo the form again in the server-side code. If it does pass, you can use it (insert it into db, echo it, email it, whatever).
You save the data in your database and add it to $_SESSION. You echo the data they just entered along with a button "Download PDF" or some such.
They click "Download PDF".
You have all the information you need to create and PDF. You don't have anything to validate but you need to use the $_SESSION information to create the PDF. You should test the $_SESSION to make sure they have valid input from the previous pages or else someone can mimic a post to the page and generate a PDF (perhaps blank though). I generally avoid using data from a $_SESSION as anything but state information -- I'll write to a db on post data (after scrubbing) but if it's in $_SESSION it usually is just stuff that tells me who they are and stores other information about configuration, etc. In your case, I'd have written to a DB in step one and now would use some ID from $_SESSION to pull that record and create the PDF to send it.
I think all of your validation can be easily done in step one and then you separate step 2 into delivery based on the validity of step one.
Further to my previous question, here's what I decided to implement; it may not be pure P-R-G, but it seems ok. Care to comment?
The form.php has an action; let's call it validate.php.
validate.php is never seen by the user; if validates all $_GET and, if valid writes it to database and generates the HTML of a confirmation page / if not valid, it generates the HTML of an error page explaining what is wrong.
Whichever HTML is generated get stored in a $_SESSION variable and then validate.php does a header('Location: <as appropriate>);
Finally a page called submitted.php of invalid_input.php (in case the user reads the URL) consists only of echo $_SESSION['form_html'];
That seems to me like is proff against both page reload and back button problems.
Or did I goof by trying to reinvent the wheel?
Firstly, you're better off storing the form data, which means you can perform the validation again. It will also be less html. The problem with the method you're employing now is that it doesn't protect against multiple tabs, since $_SESSION is universal to a browser session.
One way I've used to prevent against duplicate submission (without PRG) is to generate a unique id for every page load (where a form is involved). When I generate that unique id, I add it to a $_SESSION['form_unique_ids'] array, and I include it as a hidden field in every form I generate. Then before I take action on a form submission, I check to see if that unique id is in the session. If it is, this is the first time that form has been submitted, and I remove it from the session. That way if I try to resubmit that page, I will know because that id is not in the session not to process the results.
This could be extended so that instead of storing a single id, you use the id as the key in the array, and let the value be the result of the transaction. Then:
If there are errors, you store the $_POST data as well. Then, redirect to original_form.php?id=unique_id and display the validation results. You can either store them or recalculate them there.
If there is success, store the success message and redirect to success_page.php?id=unique_id. Display the success message prominently there. If you like, you can remove it from the page.
You have the option of removing the session data when you display it, but that would mean if they refreshed the edit page they'd lose the validation messages and saved form data. I'd rather find a way to get rid of data that is old enough that they're not likely to need it anymore.
Anyway, some of those ideas might be useful. Then again, maybe it's way too much effort for the problem. Your call :)
As long as you use a php redirect at the end of your validate you cannot reload or back button into the validate.php
I am a bit of a PHP newb
I have developed a multi-page form which works fine at the moment - each stage is on another page (I use the session to retain the data).
However I know that users don't always use these forms the way you want!
I want to control the flow of the form.
I would like the user to be able to use the browser back & forward button for ease of use.
They should not be able to skip a part of the form by entering a form stage URL directly into the address bar to get the a later stage in the form (essentially skipping a part of the form).
The form also does not flow the same path every time, it is dependant on the users choices what stage is displayed next.
I was wondering if anyone had any ideas of ways to control the flow of this multi-page form thank you!
store form results in SESSIONS (encrypt them if sensitive)
then just check on each form if the value is set and show it as necessary.
use another session to check the "progress" of the form, to prevent the user from skipping ahead.
for example...
<?php
/* on form 3 */
if(isset($_SESSION['progress'] && $_SESSION['progress']==2)
{
//the second form has been filled out and validates
}
else
{
// the 2nd form hasn't been finished, redirect
}
?>
you could also use like a percentage based system in the session - a value of 90 means that 90% of the form fields have been completed - for displaying "progress" in a visual means to the user.
basically on every form submission, check whats been submitted, if its expected, then set appropiate sessions to redirect to the next stage.
check every set session on every form to determine if the user should be here yet.
Push the data for the non-current fields into a hidden field in the browser (to save time and effort - just serialize an array/object).
I would like the user to be able to use the browser back & forward button
If users are allowed to re-enter previous stages, just let them and rewrite current stage in the session.
If not, make form fields read-only and do not process submitted forms for the previous stages.
That's the only problem I can see here.
You can either use session data to retain the state between multiple pages, or you can transfer all data on each page. Typically you would do the latter with hidden fields or you will create one humonguous form, and use javascript to make it appear as if it was multiple pages, when - in fact - it's not.
There are pros and cons to each solution.
Im trying my first form validation with PHP.
I need some guidance with the logic.
I have purchase.php (which has the form) and review-purchase.php(which sets SESSION variables and displays the user data inputted)
If any of the fields fail validation I don't want the user to get to review-purchase.php
Should I be sending the user to the review-purchase.php script, checking validation there and then redirecting back the purchase.php with an error message?
or
should I be using an if/else statement with $_SERVER['php_self'] etc in the form action="" and keep all the validation on the purchase.php page itself and only letting purchase-review run if everything passes validation?
Sorry for the confusing question but i myself am very confused...
That's a question many people ask themselves, and there is probably not one right answer...
What I generally do, in your case, is :
purchase.php displays the form
that form posts on itself (ie, purchase.php)
when data has been submitted, it is dealt with -- still in purchase.php
if there is an error (like something not OK in the input), you can re-display the form really easily, this way : you already have every values that were typed in by the user
if there is no error, you can do whatever you have to with the data ; like set it in session, if that's what you need, or save it to database, for instance.
only when everything was OK (data validation OK and storage OK), you redirect to "confirm.php"
that confirmation page does nothing except display a message saying "thanks for your purchase", or something like that.
It means putting more stuff in your purchase.php, yes :
(re-)displaying of the form
dealing with the input
But, this way, it is really easier to re-display the form, pre-filled with what the use first typed, when there's a validation error.
You can use functions/classes/methods or even some included files, though, to not end up with one big chunk of un-readable / un-maintenable code...
If your form posts to another page, it'll be really harder to re-display the form... If you are using redirections, you'll to pass everything in the URL, and it'll be a mess (And there's a size limit, too)
Here, it means I would totaly remove your review-purchase.php file ; and transform it to a confirmation page, so the user knows everything was OK and his purchase is being take care of.
I suppose it's quite what you meant in your last paragraph, actually :-)
Just beware : you have to think about escaping data before injecting it back into the form (see htmlspecialchars and/or htmlentities) ; that is true for everything you get from the user (And, probably, for PHP_SELF too, I'd say) ;-)
Well, it seems you have a misconception about where and when PHP code is executed. If you want to validate user input on the server side - with PHP (and you should because any JavaScript validation on the client can be worked around by a nefarious user) - the PHP validation can only occur after the user has posted data. That is no matter to which page the user posts the data - be it the original form or a different page.
So, in your situation if you want users to go to a page if validation is successful and to a different page is validation fails yo will need to do a redirect anyway.
In this case you have two paths:
user requests Purchase.php and fills out the form
user posts data to validation page
if data is valid -> display purchase review information
else -> re-display form page and have user re-enter data
So if Purchase.php posts to itself, you can validate there and redirect to review.php only if data is valid. Which means that in the successful case you do 2 redirects and in the failed case you do only 1 post.
On the other hand, if you post directly to review.php and you validate there, you have 1 post in the successful case, and 2 in the failed case.
The above is true no matter how you spin it - unless you use the same URL for the form and the review, in which case you can put logic in the same place to do the form, validation and purchase review in the successful case.
I hope this helps.
The most common way of doing this would be to do all your validation checks in purchase.php. This way, if there are validation errors, it's easier to re-display the form with all of the information that the user has already entered.
If the validation passes, you can do a redirect to review-purchase.php with the necessary purchase information set in a database, or possibly $_SESSION if you're not using a database.
If you can separate the validation code into functions, and the display code into templates to be included, you can achieve a nice separation of logic that would allow you to use them from whichever file you go with. You might be able to avoid a redirect in that way, ie. in purchase.php you could check if there's $_POST input, validate it, and either re-display the form template, or display the purchase review template.
What is the best way to stop a form from being reprocessed when a user hits a Back button?
I'm following the Post/Redirect/Get pattern, so I don't have a problem if F5 is pushed, but the back button still provides the opportunity to resubmit the form. If this form is a credit card processing page, this is bad.
This question has been asked somewhat here, but the phrasing and answers were of poor quality and not specific to this problem exactly.
I have form.php which submits to itself. If there were no errors in input data upon submission, the user is redirected to form_thanks.php. Hitting back (and "Send" or "Resubmit") once resubmits form.php (BAD!) and then brings them back to form_thanks.php.
Please also include solutions that do not involve using Sessions, if possible.
I would do it a different way. Put up a hidden input with a random string as the value, and when it's submitted store that random string in a session. Set up a simple check to see if they've already posted it and if they have don't accept the input.
This should be done with a single-use token, or a nonce. The php/server should include this token in a hidden form field.
It should store a list of all the recent tokens and each time a form is submitted, it should check to see if the token is in the list of recent valid tokens.
If it's not in the list, then don't reprocess the form.
If it is in the list, then process the form and remove the token from the list.
The tokens can be handled within sessions or just a simple database table without sessions. The tokens should be user-specific though.
This is also the recommended way to avoid CSRF attacks.
Just thinking out loud here. But what about a variation on post/redirect/get where the final get is not actually the final get ;) But rather, it in turn always automatically forwards to the truly final page, so that should the user hit the back button, they return right back whence they came?
EDIT:
Ok, taking into consideration the OP's comment, here's another idea. The URL for the form submission could be made to require a parameter that is good for only one use. That token would be generated (using MD5 or some such) before the form was submitted and could be stored in a database (in response to somebody else's suggestion you requested a solution without using sessions). After the form is processed, this token would then be flagged in the database as having already been used. So that when the page is returned to with the same token, steps can be taken to prevent the resubmission of the form data to the backend.
Late answer, but one could avoid the processing altogether by using an AJAX-based solution; there wouldn't be an issue with including a nonce with this processing scheme, but by using an asynchronous query which, on success, redirects the user, the requests are not repeated by refreshing, pressing back, or anything other than clicking the button.
It is also easy to implement a mechanism that prevents the button from either being pressed twice or being "locked up" if something happened during the request by embedding into the handler for the request (whether high level with PrototypeJS or jQuery or low level with your handrolled function) the mechanisms to enable and disable the button when the request completes and first fires, respectively.
I find that back will bring the form to the state it was before the page was redirected, if that is the case, have a hidden input/variable or something which starts with value say true, then once the form is submitted, and if the value is true, change it to false and then submit, else return false
Try this :
<SCRIPT type="text/javascript">
window.history.forward();
</SCRIPT>