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.
Related
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?
i did this little website to convert values to md5. but i want to save all the words entered in my database to create a sort of dictionary that i could use later. when the user clicks on the convert button the page redirects to insert the data in the table and when it redirects back to the index page the md5 value is gone and the user has to click the back button to see it. how can i make the value come back in the text box after the page's redirection?
This is just to understand one method of doing it:
In your process.php file:
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['mott'])) {
$value = $_POST['mott'];
$md5 = md5($value);
// code to insert the value and the md5 in database. remember to escape the string
//redirect the user
header('Location: showmd5.php?md5=' . $md5);
exit;
}
The showmd5.php file:
<?php
$md5 = isset($_GET['md5']) ? $_GET['md5'] : '';
// check this md5 in database and show the value
You have several methods to do this
Keep the original string and md5 hash in a session variable. It will persist even after redirects.
Instead of using the process.php, use the index.php as the target for your form. You can check the $_POST superglobal to see if you are loading the page for the first time, or displaying previously sent values.
Put the values passed to process.php in the query string of the redirection url, so instead of
header("Location: index.php");
you do
header("Location: index.php?string=$mystring&md5=$mymd5");
Be careful though, you need to sanitize $mystring or your form would be an easy XSS bait. Thanks #machineaddict.
Since you are generating the md5 hash on the front, use js and ajax to send values to process.php without ever leaving the index. This one is the most elegant if you know your way around simple js libraries like jQuery.
PD: c'mon S.O., lists break code highlighting?
im using a script from here: http://www.php-login.net
I altered it to suit my own needs but i see this part in the script:
// if user has an active session on the server
elseif (!empty($_SESSION['user_name']) && ($_SESSION['user_logged_in'] == 1)) {
$this->loginWithSessionData();
// checking for form submit from editing screen
if (isset($_POST["user_edit_submit_name"])) {
$this->editUserName();
} elseif (isset($_POST["user_edit_submit_email"])) {
$this->editUserEmail();
} elseif (isset($_POST["user_edit_submit_password"])) {
$this->editUserPassword();
}
I am not too sure how session variables work since there on the server and technically they cant be altered directly however this part of the code shows it can be altered indirectly if someone messed with cookies.
private function loginWithSessionData() {
$this->user_name = $_SESSION['user_name'];
$this->user_email = $_SESSION['user_email'];
// set logged in status to true, because we just checked for this:
// !empty($_SESSION['user_name']) && ($_SESSION['user_logged_in'] == 1)
// when we called this method (in the constructor)
$this->user_is_logged_in = true;
}
Im not sure if its possible but if i messed with the cookies and set username=x and got lucky and set is_logged_in as 1 could that give the user access? Im sure there is a much safer method of validating a session or do cookies themselves also come with there own type of validation like checking the machine hash and that hash must also match with the hash we stored on the server? Instead of something as simple as user_logged_in should i use a random string called it iftodaywasarainyday and just comment it internally so i know what that value corresponds with my is_logged_in or does it even matter.
I will do some more reading up on the subject but i guess i took the authors word for it since the first 3 words on the page are "A simple, clean and secure" and the site does look quite good but as i was refactoring the code there is lots of todo statements which got me worried its work in progress rather than a finished script
Session data is stored server-side. The actual data isn't in the cookie at all. The cookie is just an ID that let's the server know which session data to load. Users cannot modify this session data unless you allow them to by writing code that does it.
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
}
Basically I wish to write my registration page in such a way that errors (such as invalid username, short password, too long username, invalid email, etc.) are returned (at the same time) when user is redirected back to the same page.
I have often seen a lot of websites that have this:
There are X errors in the data you submitted:
Username is too short
Password is too short
Etc. etc.
And these errors are returned when the user submits the form and is redirected back.
I thought of returning the errors via numbers in $_GET however I'd like to avoid this. And I also would like to avoid using JS/AJAX and $_SESSION.
It would be typically done using $_SESSION, which you don't want to use for some unexplained reason. They are typically called flash session as once the information has been showed to the user, that portion of the session is destroyed.
You could use cookies and manage it yourself, but I'd stick to using the session. Be sure to remember cookies can be easily tampered with from the end user.
Make the page submit the form to itself, and you won't have to return anything. You just need to make the code handles both the case where the user comes to the page for the first time and the case where data has been submitted.
With a library:
if ($_POST)
{
$form_helper = new FormHelper();
$form_helper->validate($_POST["username"], "username");
$form_helper->validate($_POST["password"], "password");
if (! $form_helper->notifications())
{
// Process form.
}
}
// Show errors if necessary when rendering the form.
Without a library:
$errors = array();
if ($_POST)
{
// Validate username by hand (let's pretend it passes).
// Validate password by hand (let's pretend it fails).
array_push($errors, "The username or password is incorrect.");
if (! $errors)
{
// Process form.
}
}
// Show errors if necessary when rendering the form.
There aren't a lot of options here.
I'd pass it in $_SESSION.
I don't know if this is good practice (or even possible with php), but I've had good results by returning error messages in a table and then letting some JavaScript code display them.