I'm using GUMP https://github.com/Wixel/GUMP for server side form validation and have a question regarding showing messages after a redirect.
I want to validate the form data after submission, then redirect to the form if there was an error but I don't know the best way to pass the errors to the form after the redirect.
I've read this question Header Redirect after form Validation in PHP which suggests two ways of doing this:
1.
$message="Some message for the next page.";
$message=urlencode($message);
header("Location:page.php?message=".$message);
2.
$_SESSION['message']='some other message';
The author of the answer thinks method 1 is more secure, but can you tell me why that would be?
I've also had a look at how it's done by php-form-builder class https://github.com/lkorth/php-form-builder-class, and they seem to use method 2:
/*Valldation errors are saved in the session after the form submission, and will be displayed to the user
when redirected back to the form.*/
public static function setError($id, $errors, $element = "") {
if(!is_array($errors))
$errors = array($errors);
if(empty($_SESSION["pfbc"][$id]["errors"][$element]))
$_SESSION["pfbc"][$id]["errors"][$element] = array();
foreach($errors as $error)
$_SESSION["pfbc"][$id]["errors"][$element][] = $error;
}
So, my question is, which is the best way to go about this? Pass the errors with $_GET or in session variables?
p.s. If I've missed something, and there is a way to do it that's easier/built into GUMP, please point it out!
Two files, one of them contains all the PHP business logic and the other the form (which you include in the first file). The first file does two things: it checks to see if the form was submitted and displays the form. On the first run, there are no error messages because the form has yet to be submitted. If the form is submitted and it does not validate, have the form display the error message(s) (i.e.; <?php echo $gump->get_readable_errors(true) ?>). No need to store the error messages in session. You could also re-populate the form with the previously submitted data.
form.php
<?php
$_error_messages = '';
if (isset($_POST) && !empty($_POST)) :
$gump = new GUMP();
// Let's sanitize the POST data
$_POST = $gump->sanitize($_POST);
$gump->validation_rules(array(
// your validationm rules here
));
$gump->filter_rules(array(
// your filter rules here
));
// Validate the form data
$validated_data = $gump->run($_POST);
if ($validated_data === false) :
// The submitted data did not validate,
// display the errors in the form
$_error_messages = $gump->get_readable_errors(true);
else :
// The submitted data validated successfully
. . .
endif;
endif;
// Display your form
include 'form-view.php';
form-view.php
<!DOCTYPE html>
<html>
<head>
// . . .
</head>
<body>
<section>
<?php echo $_error_messages ?>
<form
action = '<?php echo htmlentities('form.php'); ?>'
method = 'post'
name = 'my_form'
>
// The rest of your form here
</form>
</section>
</body>
</html>
Related
I have a form and I don't do client-side validation other than required attribute. I'm doing the validation on the server side.
lyrics/add.php
<form action="../scripts/lyrics/submit_lyrics.php" id="lyricsForm" method="post" autocomplete="off" enctype="multipart/form-data">
Form data is processed in a seperate php file. Retrieving and validating the form data in the following way:
scripts/lyrics/submit_lyrics.php
$form_data = new FormData(["artist", "album", "song", "year", "track_no", "lyrics"], "sssiis");
$form_data->validate();
What the validate method does is
public function validate() {
$valid = $this->check_form_data($fields);
$fields = implode(",", $fields);
if (!$valid) {
header("location: ".BASE."lyrics/add?status=error&problem=input&specifics=$fields");
exit;
}
}
check the form data and redirect the page (scripts/lyrics/submit_lyrics.php) to the form page (lyrics/add.php) with the information on validation (if it failed). Then I output an error message indicating that there's something wrong with the input using GET method.
I'm curious if I can do this using POST. I would need to modify this line
header("location: ".BASE."lyrics/add?status=error&problem=input&specifics=$fields");
make it redirect the page to BASE."lyrics/add" and also send the validation information using POST to that page. So I'd still be able output the validation error, but using POST instead of GET.
Is this possible?
No, this is not possible because you are rewriting the request. The browser will always send a "GET" request in response to a redirect request, unless it receives a 307 request in which it will repeat the request using the same method and parameters. See this related question on Programmers.
The workaround is to redirect to a new page with an embedded form that you generate, and have javascript POST that form for the user. It's an extra request but it's the only way to have the client make a second POST.
Using u_mulder's suggesion (storing values in a session), I've solved the problem in the following way:
scripts/lyrics/submit_lyrics.php
public function validate() {
$valid = $this->check_form_data($fields); // check if input fields are valid
$fields = implode(";", $fields); // invalid input fields
if (!$valid) { // not all fields are valid, so we return
session_start(); // start session
if (!isset($_SESSION["problem"])) { // making sure session variable doesn't exist
$_SESSION["problem"] = "input"; // error type: input, file_upload, db_insert, etc.
$_SESSION["specifics"] = $fields; // artist, album, etc. (for input)
}
header("location: ".BASE."lyrics/add?status=error"); // redirect to form page
exit;
}
}
lyrics/add.php
if (isset($_GET["status"])) {
$status = $_GET["status"];
switch ($status) {
case "error":
session_start();
if (isset($_SESSION["problem"])) {
$problem = $_SESSION["problem"];
$specifics = explode(";", $_SESSION["specifics"]);
$error_message = "There was an error.";
switch ($problem) {
case "input":
$error_message = "Invalid input on the following fields:";
break;
// other cases ...
}
session_destroy();
output("Error!", $error_message, $specifics);
} else {
// if the user reloads the page, session data is lost
// but we're still in the error page "lyrics/add?status=error" and have no error to show
// we either show an appropriate message or redirect to "lyrics/add"
session_destroy();
// output("Error!", "You're in the form validation error page, but there aren't any errors. Did you reload the page?");
// header("location: ".BASE."lyrics/add");
// exit;
}
break;
case "success":
// form submitted successfully
break;
}
} else {
// show form
}
I had few forms in my project, they were submitted to public function's like site.com/email => site.com/validate_email, but then I realized that that's not what I want.
Now I need to make them submit to themselfs ,check and display validation errors.
What is the appropriate way to do this? Check for emptyness of $_POST and then call my new _validate_email(//that will return true or false) if post isn't empty?
Or something else not that noobish?:)
for example:
public function login()
{
$this->load->view('login');
}
public function login_validation()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('email', 'Email', 'required|valid_email|trim|xss_clean|callback_validate_credentials');
$this->form_validation->set_rules('password','Password','required|md5');
if($this->form_validation->run())
{
//some stuff here
}
else
{
$this->load->view('login'); //or redirect()?
}
view:
<?php $this->load->view('header'); ?>
<div class="form-container">
<?=$this->form_validation->validation_errors();?>
<?php
$form_atr = array(
'id' => 'form-set'
);
echo form_open('main/login_validation', $form_atr);//this should be 'main/login'
?>
<div class="header">
/*
here goes other parts of form
*/
</div><!--END form-container -->`
<?php $this->load->view('footer'); ?>
So, basicaly i need to combine login() and login_validation(), but make it so that when user`s input incorrect i get reloaded page of the same view with the same URL and get validation errors displayed.
I've tried to put code of validation into the same function that displays form, but I can't figure out how to redirect or reload the view to show val.errors if any.
So, I think this way is correct(It really should be):
I made my login_validation() private by adding '_' before it, like so _login_validation()
Than I added an if() statement that contains $_POST form variables and i am cheking them with php isset() function, that way the code can determine when user submitted a form. And after all that I just call _login_validation() if inputs are set or load again my login view if not.
public function login()
{
if(isset($_POST['password']) && isset($_POST['email']))
{
$this->_login_validation();
}
else
{
$this->load->view('login');
}
}
and dont forget to process your form so it would submit to the same URL:
echo form_open('', $form_atr);
Hope that will help someone someday.
First of all, validation_errors() is one kind of flash data, so whenever you redirect the page with error the validation errors will be displayed, if you reload it second time it will not be displayed.
From your question i could not understand, do you want to show the errors or not.
if you want to show the errors:
then just redirect the page to login and in the login view add a alert div.
if you don't want to show the errors:
then just remove the alert div.
*i don't see any alert div in your login view, if validation errors are still displayed then may be alert div is in the header view
I'm having problems with storing variables in a $_SESSION variable.
I'm using Zend framework and building a 3 step application form. Now, when the first step is done, I store the data in MySQL database, and store the returned insert id in a session variable. Then I forward the page to another controller (step 2). When I forward the request, everything works fine and I can read the id from the session variable. But when I submit the second form (which has the same controller of step 2 as an action) the session is lost. I try to var_dump it, and it returns NULL.
Here's the code:
public function organizationAction()
{
$this->view->vals="";
$form=$this->getOrganizationForm();
$this->aplid=$_SESSION['appid'];
var_dump($_SESSION);
$firsttime=$this->getRequest()->getParam('firsttime',0);
//if(null==$this->aplid) $this->_forward('index','index');
if ($this->getRequest()->isPost() && $firsttime==0) {
if (!$form->isValid($_POST)) {
// Failed validation; redisplay form
$this->view->form = $form;
return false;
}
var_dump($_SESSION);
$values = $form->getValues();
$db=new Util_Database();
if($db->insertOrganization($values,$this->aplid))
$this->_forward('final');
else echo "An error occured while attempting to submit data. Please try agian";
}
$this->view->form=$form;
}
What is the problem here? I tried storing the session_id in the form, and then setting it before session_start(), but it starts a whole new session. Please help!
I'm not sure if this is going to help, because I'm not sure if something else might be happening in step 2. But here goes.
You might be inadvertently overwriting your session data. Here is what I came up with that might help give some ideas.
public function organizationAction() {
$this->view->vals = "";
$form = $this->getOrganizationForm();
$db = new Util_Database();
//This will only submit the form if the is post and firsttime == 0
if ($this->getRequest()->isPost() && $this->getRequest()->getPost('firsttime') == 0) {
//if form is valid set session and save to db
if ($form->isValid($this->getRequest()->getPost())) {
//We only want to initialize the session this time, if we do it
//on the next pass we may overwrite the information.
//initialize session namespace
$session = new Zend_Session_Namespace('application');
//get values from form, validated and filtered
$values = $form->getValues();
//assign form value appid to session namespace
$session->appid = $form->getValue('appid');
//assign session variable appid to property aplid
$this->aplid = $session->appid;
if ($db->insertOrganization($values, $this->aplid))
$this->_forward('final');
else
echo "An error occured while attempting to submit data. Please try agian";
} else {
//if form is not vaild populate form for resubmission
//validation errors will display of form page
$form->populate($this->getRequest()->getPost());
}
}
//if not post display form
$this->view->form = $form;
}
P.S. If your gonna go ZF...Go ZF! :)
I have a form on a php page that is submitted to the same page.
I noticed that if I reload/refresh the page the form gets re-submitted.
How do I code to avoid this in the most easy way?
One possibility is, to implement the post-redirect-get approach.
Simply said, a POST request will be never delivered to the browser. Instead you execute all necessary actions and store the information you need in the session, and then you make a redirect with code 303.
$page = 'show_result.php';
header('Location: '.$page, true, 303);
exit;
Doing it this way, the browser will show the "show_result.php" page (a GET request) instead of the page requested with POST. This is also the page that is added to the history, so refreshing and using the back button will never do another POST request. As a nice side effect you get rid of browser warnings about resending data, normally the user cannot decide what to do then anyway.
I think the biggest problem with this approach is, that you need a session to store error messages, that means you have to rely on cookies. If you do no redirect to display errors, the browser will show the warning about resending data.
This assume a lot of things, but maybe is what you are looking for:
if ($_POST)
{
$success = false;
/*
* if all goes OK managing POST data make $success = true;
*
*/
if ($success)
{
// this will redirects to your original
// form's page but using GET method
// so re-submitting will be no possible
header("location: {$_SERVER['PHP_SELF']}");
exit;
}
}
According to HTTP standard, you ought to make browser to do a GET request after sending POST one.
Here is a sketch example to do the form handling:
<?
if ($_SERVER['REQUEST_METHOD']=='POST') {
$err = array();
//performing all validations and raising corresponding errors
if (empty($_POST['name']) $err[] = "Username field is required";
if (empty($_POST['text']) $err[] = "Comments field is required";
if (!$err) {
//if no errors - saving data and redirect
header("Location: ".$_SERVER['PHP_SELF']);
exit;
} else {
// all field values should be escaped according to HTML standard
foreach ($_POST as $key => $val) {
$form[$key] = htmlspecialchars($val);
}
} else {
$form['name'] = $form['comments'] = '';
}
include 'form.tpl.php';
?>
I have an HTML form that takes inputted data and sends it via the mail() function. I also have some validation techniques that validate the inputs, and I created an array variable $errors to log all of the errors; for example,
if the name was left empty, $errors[]="Name empty";
If the email was left empty, $errors[]="email empty";
and so on..
I was able to report the errors using the following technique:
print '<div id="formfeedback"><h3>Error!</h3><p>The following error(s) has occurred:<br />';
foreach ($errors as $msg) { //prints each error
print " - $msg<br />\n";
} // end of foreach
However, what I want is the following. I want the page to be redirected back to the original form that was used to input the information (I know the exact link location, so i can use a header() or even a <meta=http-equiv=refresh> to bring me back to the form page.
Also, on the form, I want to be able to post the errors above the form in some div (call it div=errors)
Would I be able to do the following?
<div id="errors">
<?php
print 'The following error(s) has occurred:<br />';
foreach ($_REQUEST[$errors] as $msg) { //prints each error
print " - $msg<br />\n";
} // end of foreach
?>
</div>
Thanks a lot!
Amit
I agree with #Fosco. I want to explain a little bit more-
There may be two cases-
1. You are doing raw php
2. You are coding on any php framework like CI or your own.
and this will help to identify error field and change style to make better user response. Also last input data remain as it was.
You are doing raw php
In this case you can receive the input data in same file/page.
I will do a common example later.
You are coding on any php framework like CI or your own.
In this case you load a view file to show the form page and you can pass data to view page/file when you load it.
For both of above case you can do some coding like-
/*
your input validation and verification goes here. where $error is generated too
In addition add some error status in above section,
you can do it in your $error array too. Also you store received data into $data here. index of $data should be similar as (corresponding) HTML input name.
You can do it like below
*/
$error_stat = array();
//if the input field name is "email" and email input data raises any error then
$error_stat['email'] = true;
// same for name
$error_stat['name'] = true;
// and so on
// now decide whether you will back to the form page or send the email and do other tasks
if(count($error_stat)<= 0){
// send email
// do aditional tasks
}
else{
// load the form again if its aframework or the form is in seperate file
// off course send $error,$data and $error_stat to the form page/file
}
// now here is a code segment of form page
<?php if(isset($error) && count($error)>0):?>
<div id="error-msg">
<?php
//display errors here
?>
</div>
<?php endif;?>
<form .... >
<input type="text" name="email" class="<?php echo (isset($error_stat['email'])?'error':'else'); ?>" value="<?php echo $data['email'];?>" />\
<!-- and so on ... -->
The simplest way to do this is to:
// Start the session
session_start();
// Store the errors in the session
$_SESSION['errors'] = $errors;
// Redirect to correct page
header('HTTP/1.1 303 See Other');
header('Location: http://original/page');
exit;
Then, on the form page:
// Start the session
session_start();
// extract the errors
$errors = isset($_SESSION['errors']) ? $_SESSION['errors'] : array();
// Display the form with errors
foreach ($errors as $msg) ... ;
Typically I would have the same page process the input and the submission. If the data was valid, the mail would be sent and the page would notify them of that (or redirect them elsewhere)... if the data was not valid, then the form would appear again and the errors could be displayed, without any fancy redirection.
make sure your session is started at the top of your application
include this basic class
class FormErrors
{
var $handler;
function __construct($fname)
{
$this->handler &= isset($_SESSION[$fname]) $_SESSION[$fname] : ($_SESSION[$fname] = array());
}
public function add($name, $value)
{
$this->handler[$name] = $value;
}
public function remove($name)
{
unset($this->handler[$name]);
}
public function getErrors()
{
return $this->handler;
}
}
so when your processing the errors you can go
if(isset($_POST))
{
$FormErrors = new FormErrors("registration");
if(strlen($_POST['username']) == 0)
{
$FormErrors->add('Username','Please enter a valid username');
}
//And for the rest of your checks
}
then within side your html do
foreach($FormErrors ->getErrors() as $name => $error)
{
echo sprintf("<p title=\"%s\">%s</p>",$name,$error);
}
Should work, and if you want to remove all known errors do
$FormErrors = new FormErrors("registration");
unset($FormErrors->handler,$FormErrors);