In the last part, the form working as intended hinges upon users entering valid data. What if they didn't enter valid data? What if they missed stuff out? Worse, what if they entered stuff that
breaks your HTML?
In this part, we implement controls to prevent that from happening. At the very basic level, we have server-side validation. For this, whatever data the user sends to the PHP server will be validated, and error messages will be displayed if data is not valid.
First, we put in placeholders for error messages. For Name, the only thing we need to worry about is missing data. So let's have a div, styled using the
error CSS class. And within it, have a span tag with the error message within.
<div class="formrow">
<label for="txtName">Name</label><br />
<input type="text" id="txtName" name="txtName" maxlength="50" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error" id="name_required">
<span>Name is required.</span>
</div>
For Email, you have
two things to worry about - missing data and invalid data. You generally want there to be an email, and you want the email to be an
actual email. So let's have
two divs and
two error messages.
<div class="formrow">
<label for="txtEmail">Email</label><br />
<input type="text" id="txtEmail" name="txtEmail" maxlength="50" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error" id="email_required">
<span>Email is required.</span>
</div>
<div class="error" id="email_format_incorrect">
<span>Email is in an incorrect format.</span>
</div>
And let's have one for Comments.
<div class="formrow">
<label for="txtComments">Comments</label><br />
<textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"></textarea>
</div>
<div class="error" id="comments_required">
<span>Comments are required.</span>
</div>
See your results!
In the PHP code, create the associative array,
errors. Then fill in the array with key-value pairs as shown, setting all values to false.
$message = "";
$errors = array();
$errors["name_required"] = false;
$errors["email_required"] = false;
$errors["comments_required"] = false;
$errors["email_format_incorrect"] = false;
$mailsent = false;
Under the part where the form values are obtained, run a
str_replace() function on each one, removing all spaces. If the result is an empty string, then the user tried to be funny somewhere. Either way, the string is blank and thus invalid.
$form_name = trim($_POST["txtName"]);
$form_email = trim($_POST["txtEmail"]);
$form_comments = trim($_POST["txtComments"]);
if (str_replace(" ", "", $form_name) == "")
{
}
if (str_replace(" ", "", $form_email) == "")
{
}
if (str_replace(" ", "", $form_comments) == "")
{
}
Set the respective values of the key-value pairs in
errors, to
true.
$form_name = trim($_POST["txtName"]);
$form_email = trim($_POST["txtEmail"]);
$form_comments = trim($_POST["txtComments"]);
if (str_replace(" ", "", $form_name) == "")
{
$errors["name_required"] = true;
}
if (str_replace(" ", "", $form_email) == "")
{
$errors["email_required"] = true;
}
if (str_replace(" ", "", $form_comments) == "")
{
$errors["comments_required"] = true;
}
For Email, include an
Else block. We only want to check for a valid email if there
is an email. Run the string through the
validateEmail() function, then set the key-value pair in
errors if the function returns
false.
if (str_replace(" ", "", $form_email) == "")
{
$errors["email_required"] = true;
}
else
{
if (!validateEmail($form_email))
{
$errors["email_format_incorrect"] = true;
}
}
And there we have the
validateEmail() function. The parameter in this function is a string,
str. It returns
true by default.
else
{
$message = "CSRF attack foiled!";
$messageclass = "message_error";
}
}
function validateEmail($str)
{
return true;
}
Generally, there is no such thing as an email with less than 5 characters, so return
false if that is true.
function validateEmail($str)
{
if (strlen($str) < 5) return false;
return true;
}
Obviously, if there is no "@" character in the string, it can't be an email.
function validateEmail($str)
{
if (strlen($str) < 5) return false;
if (strstr($str, "@") === false) return false;
return true;
}
If the first or last letter of str is "@", it's also wrong.
function validateEmail($str)
{
if (strlen($str) < 5) return false;
if (strstr($str, "@") === false) return false;
if (strpos($str, "@") == 0 || strpos($str, "@") == strlen($str) - 1) return false;
return true;
}
Hold up.... why not use Regular Expressions?
Good question. Regular Expressions are really powerful. They're also a pain in the ass when dealing with email strings. These days, there are just too many ways a string can qualify as an actual email address. So stick with the basics.
There are ways to verify an email address by sending and receiving of course... but I really don't want to go there today.
Next, we declare an If block around the emailing portion.
if ()
{
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type:text/html;charset=UTF-8\r\n";
$headers .= "From: " . $form_email . "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
$subject = "Contact request from " . $form_name;
$body = nl2br($form_comments);
$mailsent = mail("teochewthunder@gmail.com", $subject, $body, $headers);
if (!$mailsent)
{
$form_email = "";
$form_name = "";
$form_comments = "";
$message = "Email sent. Thank you!";
$messageclass = "message_success";
}
else
{
$message = "An error occured while trying to send your mail. Please try again.";
$messageclass = "message_error";
}
}
This code should only fire off if the size of the array returned by running
errors through an
array_filter() function, is 0, which means there are no errors. That's because
array_filter(), in its simplest form, filters out all values that are
false.
More on
array_filter() if you're interested!
if (sizeof(array_filter($errors)) == 0)
{
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-type:text/html;charset=UTF-8\r\n";
$headers .= "From: " . $form_email . "\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
$subject = "Contact request from " . $form_name;
$body = nl2br($form_comments);
$mailsent = mail("teochewthunder@gmail.com", $subject, $body, $headers);
if (!$mailsent)
{
$form_email = "";
$form_name = "";
$form_comments = "";
$message = "Email sent. Thank you!";
$messageclass = "message_success";
}
else
{
$message = "An error occured while trying to send your mail. Please try again.";
$messageclass = "message_error";
}
}
Now, we only want error messages to appear when input is invalid (duh), so let's add this to each div that's styled with
error. This basically means that if that particular key-value pair in errors is not
true, then style that div using
hide as well.
<div class="formrow">
<label for="txtName">Name</label><br />
<input type="text" id="txtName" name="txtName" maxlength="50" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error <?php echo $errors["name_required"] ? "" : "hide" ?>" id="name_required">
<span>Name is required.</span>
</div>
<div class="formrow">
<label for="txtEmail">Email</label><br />
<input type="text" id="txtEmail" name="txtEmail" maxlength="50" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error <?php echo $errors["email_required"] ? "" : "hide" ?>" id="email_required">
<span>Email is required.</span>
</div>
<div class="error <?php echo $errors["email_format_incorrect"] ? "" : "hide" ?>" id="email_format_incorrect">
<span>Email is in an incorrect format.</span>
</div>
<div class="formrow">
<label for="txtComments">Comments</label><br />
<textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"></textarea>
</div>
<div class="error <?php echo $errors["comments_required"] ? "" : "hide" ?>" id="comments_required">
<span>Comments are required.</span>
</div>
In the CSS, let's create the CSS class
hide. It basically sets the
display property to
none.
<style>
.hide
{
display: none;
}
</style>
Refresh and test. Try entering no value, or all spaces for some. Try an obviously invalid email. See what happens?
Yep!
We're missing something...
You'll notice that the inputs in the form go blank when the error messages are displayed. This is not helpful. What if Name and Email were valid but only Comments had an error? Then the user would have to fill all these in again.
So add the appropriate value in these inputs. If the form hasn't been submitted, the variables will be empty strings anyway.
<div class="formrow">
<label for="txtName">Name</label><br />
<input type="text" id="txtName" name="txtName" maxlength="50" value="<?php echo $form_name; ?>" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error <?php echo $errors["name_required"] ? "" : "hide" ?>" id="name_required">
<span>Name is required.</span>
</div>
<div class="formrow">
<label for="txtEmail">Email</label><br />
<input type="text" id="txtEmail" name="txtEmail" maxlength="50" value="<?php echo $form_email; ?>" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error <?php echo $errors["email_required"] ? "" : "hide" ?>" id="email_required">
<span>Email is required.</span>
</div>
<div class="error <?php echo $errors["email_format_incorrect"] ? "" : "hide" ?>" id="email_format_incorrect">
<span>Email is in an incorrect format.</span>
</div>
<div class="formrow">
<label for="txtComments">Comments</label><br />
<textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"><?php echo $form_comments; ?></textarea>
</div>
<div class="error <?php echo $errors["comments_required"] ? "" : "hide" ?>" id="comments_required">
<span>Comments are required.</span>
</div>
There you go!
But wait...
The fact that you're displaying user-input to screen not only puts you at risk of a XSS attack, but also, what if the input broke the HTML?
Try this as a name and submit.
Uh-oh!
What we should do here is sanitize the input. Create a function,
sanitize(), in the PHP. Set it to run the argument,
str, through the
htmlentities() function, and add in other arguments to handle quotes, special characters and other HTML entities. That way, even if the user enters markup, it will not break your HTML when displayed as part of your form.
else
{
$message = "CSRF attack foiled!";
$messageclass = "message_error";
}
}
function sanitize ($str)
{
return htmlentities($str, ENT_COMPAT|ENT_QUOTES, "UTF-8", true);
}
function validateEmail($str)
{
if (strlen($str) < 5) return false;
if (strstr($str, "@") === false) return false;
if (strpos($str, "@") == 0 || strpos($str, "@") == strlen($str) - 1) return false;
return true;
}
And then ensure that your displayed output is sanitized.
<div class="formrow">
<label for="txtName">Name</label><br />
<input type="text" id="txtName" name="txtName" maxlength="50" value="<?php echo sanitize($form_name); ?>" placeholder="e.g, Jose D'Cruz" />
</div>
<div class="error <?php echo $errors["name_required"] ? "" : "hide" ?>" id="name_required">
<span>Name is required.</span>
</div>
<div class="formrow">
<label for="txtEmail">Email</label><br />
<input type="text" id="txtEmail" name="txtEmail" maxlength="50" value="<?php echo sanitize($form_email); ?>" placeholder="e.g, j_dcruz208@youremail.com" />
</div>
<div class="error <?php echo $errors["email_required"] ? "" : "hide" ?>" id="email_required">
<span>Email is required.</span>
</div>
<div class="error <?php echo $errors["email_format_incorrect"] ? "" : "hide" ?>" id="email_format_incorrect">
<span>Email is in an incorrect format.</span>
</div>
<div class="formrow">
<label for="txtComments">Comments</label><br />
<textarea type="text" id="txtComments" name="txtComments" rows="5" maxlength="500" wrap="hard" placeholder="e.g, You're awesome!"><?php echo sanitize($form_comments); ?></textarea>
</div>
<div class="error <?php echo $errors["comments_required"] ? "" : "hide" ?>" id="comments_required">
<span>Comments are required.</span>
</div>
There you are, it works!
It even works with accented and foreign characters!
Client-side validation
I know what you're thinking - why client-side validation when server-side validation is perfectly fine? Well, for starters, it's always faster than validating the data after it's been sent.
The task of client-side validation is to validate the data
before it is submitted to the server. And because JavaScript can be turned off (not that I'd recommend doing so), the server-side validation, while slower, is a very dependable fallback.
So, long story short... you have server-side validation, and it's time for client-side validation.
In the form tag, add an
onsubmit attribute. This is an event handler that triggers whenever the form is submitted. If the value is
true, then it submits. If it is
false, execution halts there. The function triggered is
validateForm().
<form action="" method="POST" onsubmit="return validateForm();">
In the JavaScript, create the
validateForm() function. First, get the placholders by declaring the variable placehlders and setting it to the array returned by the
getElementsByClassName() method, and passing in "error" as an argument. This will get all elements with the CSS class of
error.
<script>
function validateForm()
{
var placeholders = document.getElementsByClassName("error");
}
</script>
Iterate through the
placeholders array. For every element whose class is "error", set it to "error hide". This means to hide all placeholders.
var placeholders = document.getElementsByClassName("error");
for (var i = 0; i < placeholders.length; i++)
{
placeholders[i].className = "error hide";
}
Create an array,
errors. Declare variables
txtName,
txtEmail and
txtComments, and get the appropriate value from the DOM.
var placeholders = document.getElementsByClassName("error");
for (var i = 0; i < placeholders.length; i++)
{
placeholders[i].className = "error hide";
}
var errors = [];
var txtName = document.getElementById("txtName");
var txtEmail = document.getElementById("txtEmail");
var txtComments = document.getElementById("txtComments");
Now, we're going to replicate in JavaScript what we did in the PHP. The JavaScript equivalent of replacing spaces with empty strings is the
replace() method using a Regular Expression and an empty string as arguments.
var txtName = document.getElementById("txtName");
var txtEmail = document.getElementById("txtEmail");
var txtComments = document.getElementById("txtComments");
if (txtName.value.replace(/\s/g, "").length == 0)
{
}
if (txtEmail.value.replace(/\s/g, "").length == 0)
{
}
else
{
if (!validateEmail(txtEmail.value))
{
}
}
if (txtComments.value.replace(/\s/g, "").length == 0)
{
}
For each error that is triggered, push the appropriate string into the
errors array.
if (txtName.value.replace(/\s/g, "").length == 0)
{
errors.push("name_required");
}
if (txtEmail.value.replace(/\s/g, "").length == 0)
{
errors.push("email_required");
}
else
{
if (!validateEmail(txtEmail.value))
{
errors.push("email_format_incorrect");
}
}
if (txtComments.value.replace(/\s/g, "").length == 0)
{
errors.push("comments_required");
}
And then let's create the
validateEmail() function in JavaScript. It's just using equivalent functions of the ones we used in the PHP function we created.
function validateForm()
{
var placeholders = document.getElementsByClassName("error");
for (var i = 0; i < placeholders.length; i++)
{
placeholders[i].className = "error hide";
}
var errors = [];
var txtName = document.getElementById("txtName");
var txtEmail = document.getElementById("txtEmail");
var txtComments = document.getElementById("txtComments");
if (txtName.value.replace(/\s/g, "").length == 0)
{
errors.push("name_required");
}
if (txtEmail.value.replace(/\s/g, "").length == 0)
{
errors.push("email_required");
}
else
{
if (!validateEmail(txtEmail.value))
{
errors.push("email_format_incorrect");
}
}
if (txtComments.value.replace(/\s/g, "").length == 0)
{
errors.push("comments_required");
}
}
function validateEmail(str)
{
if (str.length < 5) return false;
if (str.indexOf("@") == -1) return false;
if (str.indexOf("@") == 0 || str.indexOf("@") == str,length - 1) return false;
return true;
}
Now, if the
errors array is not empty, return
true. This basically means that the form will submit.
if (txtComments.value.replace(/\s/g, "").length == 0)
{
errors.push("comments_required");
}
if (errors.length == 0)
{
return true;
}
If not, iterate through the
errors array using a
For loop and display the appropriate placeholders using the
getElementById() method. And, of course, return
false.
if (errors.length == 0)
{
return true;
}
else
{
for (var i = 0; i < errors.length; i++)
{
document.getElementById(errors[i]).className = "error";
}
return false;
}
This should appear exactly like the PHP validation that we wrote in the first part of this tutorial, except that the form will
not submit if there are errors! And if you turn JavaScript off, it should submit, then validate via the PHP code.
Yep!
Next
We will be beautifying this form somewhat and making it visually less confusing.