2

I have an email form that checks three fields, name, valid email and comments. But the way it's set up now, since name and comments are in one function it first checks name and comments even if email is not valid, how can I re-write it so it checks the fields in order. Also, I would like to re-display the fields that have no errors, so the user doesn't have to type again. Please help. Thanks

<?php
$myemail = "comments@myemail.com";
$yourname = check_input($_POST['yourname'], "Enter your name!");
$email = check_input($_POST['email']);
$phone = check_input($_POST['phone']);
$subject = check_input($_POST['subject']);
$comments = check_input($_POST['comments'], "Write your comments!");

if (!preg_match("/([\w\-]+\@[\w\-]+\.[\w\-]+)/", $email))
  {
    show_error("Enter a valid E-mail address!");
  }

exit();

function check_input($data, $problem='')
 {
$data = trim($data);
$data = stripslashes($data);
$data = htmlspecialchars($data);
if ($problem && strlen($data) == 0)
{
    show_error($problem);
}
return $data;
}

function show_error($myError)
{
?>
<!doctype html>
<html>
<body>
<form action="myform.php" method="post">
 <p style="color: red;"><b>Please correct the following error:</b><br />
 <?php echo $myError; ?></p>
 <p>Name: <input type="text" name="yourname" /></P>
 <P>Email: <input type="text" name="email" /></p>
 <P>Phone: <input type="text" name="phone" /></p><br />
 <P>Subject: <input type="text" style="width:75%;" name="subject" /></p>
 <p>Comments:<br />
 <textarea name="comments" rows="10" cols="50" style="width: 100%;"></textarea></p>
 <p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
<?php
exit();
}
?>
Mosaic
  • 348
  • 3
  • 4
  • 16
  • I tried to put the regex into the check_input function but then it the email error showed regardless. – Mosaic Apr 18 '12 at 23:11

4 Answers4

6

First off, I would suggest you validate ALL of the fields at once, and display all appropriate error messages on the form. The primary reason is that it can be bad user experience if they have to submit your form a whole bunch of times because they have to address one error at a time. I'd rather correct my email address, password, comments, and selection in one try instead of fixing one at a time just to reveal what the next error is.

That said, here are some pointers on validating the form like you want. This is typically how I approach a form doing what you want to do. This assumes your form HTML and form processor (PHP) are together in the same file (which is what you have now). You can split the two, but the methods for doing that can be a bit different.

  • Have one function or code block that outputs the form and is aware of your error messages and has access to the previous form input (if any). Typically, this can be left outside of a function and can be the last block of code in your PHP script.
  • Set up an array for error messages (e.g. $errors = array()). When this array is empty, you know there were no errors with the submission
  • Check to see if the form was submitted near the top of your script before the form is output.
  • If the form was submitted, validate each field one at a time, if a field contained an error, add the error message to the $errors array (e.g. $errors['password'] = 'Passwords must be at least 8 characters long';)
  • To re-populate the form inputs with the previous values, you have to store the entered values somewhere (you can either just use the $_POST array, or sanitize and assign the $_POST values to individual variables or an array.
  • Once all the processing is done, you can check for any errors to decide whether the form can be processed at this point, or needs new input from the user.
  • To do this, I typically do something like if (sizeof($errors) > 0) { // show messages } else { // process form }
  • If you are re-displaying the form, you simply need to add a value="" attribute to each form element and echo the value that was submitted by the user. It is very important to escape the output using htmlspecialchars() or similar functions

With those things in place, here is some re-work of your form to do that:

<?php
$myemail = "comments@myemail.com";
$errors  = [];
$values  = ['yourname' => '','email' => '','phone' => '','subject' => '','comments' => '']
$errmsg  = '';

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    foreach($_POST as $key => $value) {
        $values[$key] = trim($value); // basic input filter
    }
    
    if (check_input($values['yourname']) == false) { 
        $errors['yourname'] = 'Enter your name!';
    }
    
    if (check_input($values['email']) == false) {
        $errors['email'] = 'Please enter your email address.';
    } else if (!preg_match('/([\w\-]+\@[\w\-]+\.[\w\-]+)/', $values['email'])) {
        $errors['email'] = 'Invalid email address format.';
    }
    
    if (check_input($values['comments']) == false) {
        $errors['comments'] = 'Write your comments!';
    }
    
    if (sizeof($errors) == 0) {
        // you can process your for here and redirect or show a success message
        $values = array(); // empty values array
        echo "Form was OK!  Good to process...<br />";
    } else {
        // one or more errors
        foreach($errors as $error) {
            $errmsg .= $error . '<br />';
        }
    }
}

function check_input($input) {
    if (strlen($input) == 0) {
        return false;
    } else {
        // TODO: other checks?
        
        return true;
    }
}
?>
<!doctype html>
<html>
<body>
<form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="post">
 <?php if ($errmsg != ''): ?>
 <p style="color: red;"><b>Please correct the following errors:</b><br />
 <?php echo $errmsg; ?>
 </p>
 <?php endif; ?>
 
 <p>Name: <input type="text" name="yourname" value="<?= htmlspecialchars($values['yourname']) ?>" /></P>
 <P>Email: <input type="text" name="email" value="<?= htmlspecialchars($values['email']) ?>" /></p>
 <P>Phone: <input type="text" name="phone" value="<?= htmlspecialchars($values['phone']) ?>"/></p><br />
 <P>Subject: <input type="text" style="width:75%;" name="subject" value="<?= htmlspecialchars($values['subject']) ?>" /></p>
 <p>Comments:<br />
 <textarea name="comments" rows="10" cols="50" style="width: 100%;"><?= htmlspecialchars($values['comments']) ?></textarea></p>
 <p><input type="submit" value="Submit"></p>
</form>
</body>
</html>

I have a more advanced example which you can see here that may give you some guidance as well.

Hope that helps.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
drew010
  • 68,777
  • 11
  • 134
  • 162
2

Use this structure of script:

<?php
$errors = array();

if (isset($_POST['send'])) {
    // check data validity
    if (!mailValid($_POST['email']))
        $errors[] = 'Mail is not valid';
    ...

    // send data by email
    if (!$errors) {
        // send mail and redirect
    }
}
?>
<html>
    ...
    <?php
    if ($errors) {
        // display errors
        foreach ($errors as $error) {
            echo "$error<br />";
        }
    }
    ?>
    <form ...>
        ...
        Email: <input type="text" name="email" value="<?php echo isset($_POST['email']) ? htmlspecialchars($_POST['email']) : '' ?>" />
        ...
    </form>
    ...
</html>
Fanda
  • 3,760
  • 5
  • 37
  • 56
1

The simplest option is to use a form validation library. PHP's filter extension, for example, offers validation and sanitization for some types, though it's not a complete solution.

If you insist on implementing it yourself, one issue you'll have to consider is what counts as the order: the order of the elements in the form or the order of the user input in $_POST. On most browsers, these should be the same, but there's no standard that enforces this. If you want to go off of form order, you'll need to define the form structure in one place, and use that information to do things like generating or validating the form (a consequence of the Don't Repeat Yourself (DRY) principle). Iterating over the appropriate structure will give you the order you desire: looping over the form gives you form order, whereas looping over $_POST gives you user input order.

It looks like you want to more than simply validate the data; you also want to prepare it for use, a process called "sanitization".

When it comes to sanitization, define different kinds of sanitizers, rather than a single check_input function. Specific sanitizers could be functions, or objects with an __invoke method. Create a map of form fields to sanitizers (for example, an array of input name to sanitizer callbacks). The order of the elements in the mapping sets the order of the sanitization; if you use a single structure to define the form information, the display order and sanitization order will thus be the same.

Here's a very broad outline:

# $fields could be form structure or user input
foreach ($fields as $name => $data) {
    # sanitize dispatches to the appropriate sanitizer for the given field name
    $form->sanitize($name, $data);
    # or:
    //sanitize($name, $data);
    # or however you choose to structure your sanitization dispatch mechanism
}

As for setting an input's value to the user-supplied data, simply output the element value when outputting the element. As with all user input (really, all formatted output), properly escape the data when outputting it. For HTML attributes, this means using (e.g.) htmlspecialchars. Note you should only escape outgoing data. This means your sanitization functions shouldn't call htmlspecialchars.

You can improve usability by placing each error next to the corresponding input, adding an "error" class to the element and styling the "error" class to make it stand out. Improve accessibility by wrapping <label> elements around the label text.

Community
  • 1
  • 1
outis
  • 75,655
  • 22
  • 151
  • 221
1

You could always do it like this, using filter_var and in_array checks:

<?php
$myemail = "comments@myemail.com";

//Pre made errors array
$errors=array('name'=>'Enter Your name',
              'email'=>'Please enter valid email',
              'phone'=>'Please enter valid phone number',
              'subject'=>'Please enter valid subject, more then 10 chars',
              'comment'=>'Please enter valid comment, more then 10 chars');

//Allowed post params and its validation type
$types = array('name'=>'string',
               'email'=>'email',
               'phone'=>'phone',
               'subject'=>'string',
               'comment'=>'string');

//A simple validation function using filter_var
function validate($value,$type){
    switch ($type){
        case "email":
            return ((filter_var($value, FILTER_VALIDATE_EMAIL))?true:false);
            break;
        case "phone":
            return ((preg_match("/^[0-9]{3}-[0-9]{4}-[0-9]{4}$/", $value))?true:false);
            break;
        case "string":
            return ((strlen($value) >=10 )?true:false);
            break;

        default:
            return false;
            break;
    }
}


//If forms been posted
if(!empty($_POST) && $_SERVER['REQUEST_METHOD'] == 'POST'){
    //Assign true, if all is good then this will still be true
    $cont=true;
    $error=array();
    foreach($_POST as $key=>$value){
      //if key is in $types array
      if(in_array($key,$types)){
        //If validation true
        if(validate($value, $types[$key])==true){
            $$key=filter_var($value, FILTER_SANITIZE_STRING);
        }else{
            //Validation failed assign error and swithc cont to false
            $error[$key]=$errors[$key];
            $cont=false;
        }
      }
    }
}

if($cont==true && empty($error)){
    //Send mail / do insert ect
}else{
    //Default to form
?>
<!doctype html>
<html>
<body>
<form action="" method="post">
 <p>Name: <input type="text" name="name" value="<?=@htmlentities($name);?>"/> <?=@$error['name'];?></P>
 <P>Email: <input type="text" name="email" value="<?=@htmlentities($email);?>" /> <?=@$error['email'];?></p>
 <P>Phone: <input type="text" name="phone" value="<?=@htmlentities($phone);?>"/> <?=@$error['phone'];?></p><br />
 <P>Subject: <input type="text" style="width:75%;" name="subject" /> <?=@$error['subject'];?></p>
 <p>Comments: <?=@$error['comment'];?><br />
 <textarea name="comment" rows="10" cols="50" style="width: 100%;"><?=@htmlentities($comment);?></textarea></p>
 <p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
<?php
}?>
Lawrence Cherone
  • 46,049
  • 7
  • 62
  • 106