PHP, MySQL, JavaScript & HTML5 All-in-One For Dummies (2013)

Book VI: Web Applications

Chapter 3: Validating Web Forms with JavaScript and PHP

In This Chapter

arrow.png Considering important web form validation issues

arrow.png Using JavaScript validation

arrow.png Using PHP validation

When you put a web form out on the Internet, you’re inviting people to send you information. Unfortunately, not everyone fills out web forms correctly; some people don’t know how the phone number should be formatted or whether to use a five-digit or nine-digit ZIP code. In addition to basic mistakes, there are also malicious users who fill out forms incorrectly to see if they can get your program to break or if they can access data that they shouldn’t.

Regardless of the reason why forms might be filled out with incorrect information, it’s up to you, the developer, to make sure that the data is formatted correctly prior to acting on it. For example, if someone fills out a form with letters instead of numbers for a ZIP code, chances are that you want to return some type of error message to have that user fix the issue.

This chapter tells you what important items to consider when you’re deciding |how to validate your web forms, how to set up JavaScript validation and provide feedback to form users, and how to validate user input on the server side.

Understanding How to Validate Web Forms

Form validation is the process by which you examine the data from a web form to make sure it’s the correct and expected data in the right format. There are two general types of validation, client-side and server-side.

check Client-side validation typically occurs with JavaScript right within the visitor’s web browser.

check Server-side validation occurs in the code running on the server, in this case, the PHP code.

The first section of this chapter looks at some high-level items that you should consider when validating web forms. Some of them are obvious, while others are overlooked by experienced programmers and newbies alike.

Always assume bad data

Rule #1 in programming is to always assume that the data you’re receiving is incorrect and only after it’s been proven correct should it be used. Working with this assumption greatly simplifies your task as a programmer. With this assumption, you no longer need to try to think of every way that a user could break your program. Rather, you merely need to think about the correct way to use it, and then make sure that your version of correctness is being followed.

Never assume JavaScript

A mistake made by new and experienced programmers alike is to assume that JavaScript will be enabled in the visitor’s browser. With that assumption, the programmers perform their validation in JavaScript and only do minimal validation in PHP, where it really counts. Unfortunately, JavaScript may not always be available, and even when it is, malicious users can still send bad data to the server by skipping the JavaScript checks. No amount of triple-extra checking to make sure JavaScript is enabled will help with that.

remember.eps The only solution is to never assume that JavaScript validation has occurred at all and always perform rigorous validation in PHP. Once the data gets into PHP, the user no longer controls it and the number of things that can go wrong decreases.

Sometimes mirror client- and server-side validation

When you implement a check in JavaScript, for example, to make sure that a ZIP code is five digits, that same type of check should also be added to the PHP code. Obviously, keeping these in sync can become a bit cumbersome, and there are certain times when a validation check might not be appropriate on the client side. For example, a website visitor’s selection from a drop-down for state (a menu that includes Arizona, California, Wisconsin, and so on) probably doesn’t need to be checked in the JavaScript, but it definitely does need to be checked in the PHP code.

As a general rule, though not always, you sometimes will mirror the validation logic between JavaScript and PHP.

Performing Basic JavaScript Validation

This section looks at basic validation using JavaScript for a variety of input types. This first exercise sets up the HTML for the web form. Once you complete this exercise and this section, you’ll have JavaScript validation done for the form.

1. Open your text editor and create a new empty file.

2. Within the file, place the following HTML:

<!doctype html>

<html>

<head>

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>

<script type="text/javascript" src="form.js"></script>

<link rel="stylesheet" type="text/css" href="form.css">

<title>A form</title>

</head>

<body>

<form id="userForm" method="POST" action="form-process.php">

<div>

        <fieldset>

        <legend>User Information</legend>

        <div id="errorDiv"></div>

        <label for="name">Name:* </label>

        <input type="text" id="name" name="name">

        <span class="errorFeedback errorSpan" id="nameError">Name is required</span>

        <br />

        <label for="city">City: </label>

        <input type="text" id="city" name="city">

        <br />

        <label for="state">State: </label>

        <select name="state" id="state">

        <option></option>

        <option>Alabama</option>

        <option>California</option>

        <option>Colorado</option>

        <option>Florida</option>

        <option>Illinois</option>

        <option>New Jersey</option>

        <option>New York</option>

        <option>Wisconsin</option>

        </select>

        <br />

        <label for="zip">ZIP: </label>

        <input type="text" id="zip" name="zip">

        <br />

        <label for="email">E-mail Address:* </label>

        <input type="text" id="email" name="email">

        <span class="errorFeedback errorSpan" id="emailError">E-mail is required</span>

        <br />

        <label for="phone">Telephone Number: </label>

        <input type="text" id="phone" name="phone">

        <span class="errorFeedback errorSpan" id="phoneError">Format: xxx-xxx-xxxx</span>

        <br />

        <label for="work">Number Type:</label>

        <input class="radioButton" type="radio" name="phonetype" id="work" value="work">

        <label class="radioButton" for="work">Work</label>

        <input class="radioButton" type="radio" name="phonetype" id="home" value="home">

        <label class="radioButton" for="home">Home</label>

        <span class="errorFeedback errorSpan phoneTypeError" id="phonetypeError">Please choose an option</span>

        <br />

        <label for="password1">Password:* </label>

        <input type="password" id="password1" name="password1">

        <span class="errorFeedback errorSpan" id="password1Error">Password required</span>

        <br />

        <label for="password2">Verify Password:* </label>

        <input type="password" id="password2" name="password2">

        <span class="errorFeedback errorSpan" id="password2Error">Passwords don't match</span>

        <br />

        <input type="submit" id="submit" name="submit">

        </fieldset>

</div>

</form>

</body>

</html>

3. Save the file as form.php in your document root.

4. View the file in your web browser by going to http://localhost/form.php.

You should see a page like that in Figure 3-1.

The HTML looks pretty bad, with misaligned form fields and errors displaying. You can fix that with CSS.

9781118213704-fg060301.eps

Figure 3-1: A web form for validation.

5. Create a new text file in your editor and enter the following CSS:

form fieldset {

        display: inline-block;

}

.radioButton {

        float: none;

        display: inline;

        margin-right: 0.1em;

        width: 2em;

}

form label {

        width: 8em;

        margin-right: 1em;

        float: left;

        text-align: right;

        display: block;

}

form input {

        width: 15em;

}

#submit {

        margin-top: 2em;

        float: right;

}

.errorClass {

        background-color: #CC6666;

}

#errorDiv {

        color: red;

}

.errorFeedback {

        visibility: hidden;

}

6. Save the file as form.css in your document root.

This file was already referenced in the HTML that you created in Step 2, so no other changes are necessary to that file.

7. Reload the form.php file in your browser.

The form should now look like that in Figure 3-2.

9781118213704-fg060302.eps

Figure 3-2: The form with CSS added.

With the HTML and CSS in place, it’s time to add some JavaScript. Note: You build the validation code later in this chapter. For now, you just add a basic JavaScript file.

8. Create a new text file in your editor.

9. Place the following JavaScript in the file.

$(document).ready(function() {

    alert("hello");

});

10. Save the file as form.js in your document root.

11. Reload form.php in your web browser.

You should receive an alert dialog like the one shown in Figure 3-3.

9781118213704-fg060303.eps

Figure 3-3: An alert from JavaScript.

12. Click OK to dismiss the dialog.

While the alert dialog itself is nothing new, it proves that you’ve connected the HTML and JavaScript correctly for this exercise. From here, you work on adding JavaScript validation to the form. Prior to doing so, you may find it helpful to break down some of the HTML and CSS that you’ve created.

Looking at the form HTML and CSS

The HTML used for the form is standard (and valid) HTML5. It begins by referencing some external files, including a Cascading Style Sheet (CSS) file and two JavaScript files.

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>

<script type="text/javascript" src="form.js"></script>

<link rel="stylesheet" type="text/css" href="form.css">

The JavaScript being loaded is jQuery from a Content Delivery Network (CDN) and your own JavaScript file.

The next area of interest is setting up the form itself, with this code:

<form id="userForm" method="POST" action="form-process.php">

That code creates a form that will use the HTTP POST method and call a PHP file named form-process.php. Directly below the form is an empty <div> element. This is used to provide feedback for the user that an error has occurred:

        <div id="errorDiv"></div>

Form elements are added next. The various form elements on this page all follow the same general pattern with a <label> followed by an <input> and then a <span> for error feedback. The <span> element is hidden through the CSS. More on that later.

<label for="name">Name:* </label>

<input type="text" id="name" name="name">

<span class="errorFeedback errorSpan" id="nameError">Name is required</span>

<br />

The CSS for the form looks like this:

form fieldset {

        display: inline-block;

}

.radioButton {

        float: none;

        display: inline;

        margin-right: 0.1em;

        width: 2em;

}

form label {

        width: 8em;

        margin-right: 1em;

        float: left;

        text-align: right;

        display: block;

}

form input {

        width: 15em;

}

#submit {

        margin-top: 2em;

        float: right;

}

That CSS sets up the look and feel of the form elements, including the width and alignment of the various elements. The next part of the CSS handles the error displays that provide visual and textual feedback to the user when something goes wrong.

.errorClass {

    background-color: #CC6666;

}

#errorDiv {

    color: red;

}   

.errorFeedback {

    visibility: hidden;

}

Adding JavaScript validation

Now it's time to add JavaScript validation to the web form. Since you have a form, you need to connect to the form's submit event. Since you're using jQuery, doing so is really, really easy. The basic process is to check for errors, and if errors are found, to stop the "default" action from occurring.

The “default” action for a form is to submit to a server (or whatever’s in the action attribute on the form). But if an error occurs, we might as well save that round-trip to the server and back and just keep the user right on the form to correct the mistakes.

For this validation, set up a validation function and then call that function from within the submit event handler. Doing so means that you can keep all the validation logic within a single function, which makes maintenance and troubleshooting easier.

Here's an exercise to add a submit handler and a validation function.

1. Open form.js within your editor.

The file should look like this:

$(document).ready(function() {

    alert("hello");

});

2. Remove alert("hello"); from the code. In its place, add the following code:

   $("#userForm").submit(function(e) {

        var errors = validateForm();

        if (errors == "") {

            return true;

        } else {

            e.preventDefault();

            return false;

        }

    });

    function validateForm() {

        var errorFields = new Array();

        return errorFields;

    }

The file now looks like this:

$(document).ready(function() {

   $("#userForm").submit(function(e) {

        var errors = validateForm();

        if (errors == "") {

            return true;

        } else {

            e.preventDefault();

            return false;

        }

    });

    function validateForm() {

        var errorFields = new Array();

        return errorFields;

    }  //end function validateForm

});

3. Save the file (with the same name, form.js) in your document root.

4. Reload the form.php page within your web browser.

There shouldn’t be any changes to the form, even on submit; you haven’t added any validation yet, just the foundation for it.

Add rudimentary validation, to check that required fields have something in them.

5. Within the validateForm() function, after the errorFields declaration, add the following code:

        //Check required fields have something in them

        if ($('#name').val() == "") {

            errorFields.push('name');

        }

        if ($('#email').val() == "") {

            errorFields.push('email');

        }

        if ($('#password1').val() == "") {

            errorFields.push('password1');

        }

The code for that function should look like this:

    function validateForm() {

        var errorFields = new Array();

        //Check required fields have something in them

        if ($('#name').val() == "") {

            errorFields.push('name');

        }

        if ($('#email').val() == "") {

            errorFields.push('email');

        }

        if ($('#password1').val() == "") {

            errorFields.push('password1');

        }

        return errorFields;

    } //end function validateForm

6. Save the file (as form.js) in your document root.

7. Reload the form.php page through your browser.

8. Without filling in any form fields, click Submit Query.

Notice that the form doesn’t appear to do anything at all. This is expected.

9. Fill in the Name, E-mail Address, and Password fields with something.

Anything will do.

10. With those fields filled in, click Submit Query.

The form should submit and give a Page Not Found (or similar) error because the form’s action hasn’t been set up yet.

11. Click Back to go back to the form.

Now you have basic validation for required fields in place but no feedback for the user. Adding feedback is a matter of activating the CSS classes that you already set up in a prior exercise.

Providing feedback to form users

The general pattern for the feedback on this form will be to highlight the field that needs attention and activate messaging for the individual field and the overall form.

To facilitate providing feedback, create two new functions in form.js.

1. Open form.js in your editor, if it isn't already open.

2. Within form.js, add the following functions, after the validateForm function:

    function provideFeedback(incomingErrors) {

        for (var i = 0; i < incomingErrors.length; i++) {

           $("#" + incomingErrors[i]).addClass("errorClass");

           $("#" + incomingErrors[i] + "Error").removeClass("errorFeedback");        

        }

        $("#errorDiv").html("Errors encountered");

    }

    function removeFeedback() {

        $("#errorDiv").html("");

        $('input').each(function() {

            $(this).removeClass("errorClass");

        });

        $('.errorSpan').each(function() {

            $(this).addClass("errorFeedback");

        });

    }

3. With those functions in the file, you next need to call them.

The call to the removeFeedback function is added right away within the submit handler so that error feedback is cleared when the form is submitted. That call looks like this:

removeFeedback();

The provideFeedback function needs to be added within the else condition in the form's submit handler and looks like this:

provideFeedback(errors);

The submit handler should now look like this:

   $("#userForm").submit(function(e) {

        removeFeedback();

        var errors = validateForm();

        if (errors == "") {

            return true;

        } else {

            provideFeedback(errors);

            e.preventDefault();

            return false;

        }

    });

4. Save the file (as form.js) within your document root.

At this point, the entire file should consist of this:

$(document).ready(function() {

    $("#userForm").submit(function(e) {

        removeFeedback();

        var errors = validateForm();

        if (errors == "") {

            return true;

        } else {        

            provideFeedback(errors);

            e.preventDefault();

            return false;

        }

    });

    function validateForm() {

        var errorFields = new Array();

        //Check required fields have something in them

        if ($('#name').val() == "") {

            errorFields.push('name');

        }

        if ($('#email').val() == "") {

            errorFields.push('email');

        }

        if ($('#password1').val() == "") {

            errorFields.push('password1');

        }

        return errorFields;

    } //end function validateForm

    function provideFeedback(incomingErrors) {

        for (var i = 0; i < incomingErrors.length; i++) {

           $("#" + incomingErrors[i]).addClass("errorClass");

           $("#" + incomingErrors[i] + "Error").removeClass("errorFeedback");

        }  

         $("#errorDiv").html("Errors encountered");

    }

    function removeFeedback() {

        $("#errorDiv").html("");

        $('input').each(function() {

            $(this).removeClass("errorClass");

        });

        $('.errorSpan').each(function() {

            $(this).addClass("errorFeedback");

        });

    }

});

5. Reload form.php in your browser.

6. Clear any information from the fields, if any was saved by your browser.

7. Within empty fields in the form, click Submit Query.

You should receive errors like those shown in Figure 3-4.

9781118213704-fg060304.eps

Figure 3-4: Errors provided through JavaScript.

8. Fill in the Name field and click Submit Query.

The feedback indicating there was an error in the Name field should clear, but the others will remain, as in Figure 3-5.

9781118213704-fg060305.eps

Figure 3-5: Correcting one error in the form.

9. Fill in details within the E-mail Address and Password fields and click Submit Query.

The form should submit, again giving a Page Not Found or similar error.

Refining the validation

Now you’ve checked your required fields and provided feedback to the user. Next up, you need to refine the validation. Prior to doing so, you should pause and look at the code you’ve added for validation.

The submit event handler is set up through jQuery's submit() function:

    $("#userForm").submit(function(e) {

        removeFeedback();

        var errors = validateForm();

        if (errors == "") {

            return true;

        } else {        

            provideFeedback(errors);

            e.preventDefault();

            return false;

        }

    });

Within the submit() function, the first thing that happens is any feedback is removed. Next, the validateForm() function is called and anything that comes back from that function is set into the errors variable. If the errors variable is empty, then the submit() function returns Boolean true, which essentially tells the browser, "Everything's okay; go ahead and submit the form." However, if errors are encountered, the provideFeedback() function is called and the default actions (to submit the form) are stopped, thanks to the preventDefault and return false statements.

The validateForm() function is the heart of the validation logic for the form.

    function validateForm() {

        var errorFields = new Array();

        //Check required fields have something in them

        if ($('#name').val() == "") {

            errorFields.push('name');

        }

        if ($('#email').val() == "") {

            errorFields.push('email');

        }

        if ($('#password1').val() == "") {

            errorFields.push('password1');

        }

        return errorFields;

    } //end function validateForm

In this function, an array is instantiated to hold the error fields. This enables you to store more than one error instead of a single error at a time (which would be frustrating to the user).

Each required field is retrieved using its ID. If the value of that field is "", then the ID of the field with the error is pushed onto the errorFields array. Finally, the errorFields array is returned and becomes the error array that you see in the submit() handler.

tip.eps Another way to accomplish this task would be to add a class to each element that's required and then loop through each of the required classes with jQuery, like $('.required').each(.

With that validation, you can look at the provideFeedback() function:

function provideFeedback(incomingErrors) {

    for (var i = 0; i < incomingErrors.length; i++) {

        $("#" + incomingErrors[i]).addClass("errorClass");

        $("#" + incomingErrors[i] "Error").removeClass("errorFeedback");

    }

    $("#errorDiv").html("Errors encountered");

}

The provideFeedback() function loops through the incoming errors and adds the errorClass class to the fields. Recall from the CSS that this class simply sets the background color to a shade of red. Next, the errorFeedback class is removed. This class hides the textual feedback, so by removing the class, the feedback becomes visible to the user. Finally, outside of the loop, the errorDiv's HTML is set to the phrase "Errors encountered".

The final piece of the form.js file (so far) is the removeFeedback() function:

    function removeFeedback() {

        $("#errorDiv").html("");

        $('input').each(function() {

            $(this).removeClass("errorClass");

        });

        $('.errorSpan').each(function() {

            $(this).addClass("errorFeedback");

        });

    }

This function first sets the errorDiv's HTML to blank. Next, each input has its errorClass removed and each errorSpan on the page has its errorFeedback class added, which essentially hides them from visibility. All of this is done with the help of jQuery selectors and functions.

Adding more validation

Looking at the validation you’ve done so far, a couple things are evident: First, the E-mail Address field can be filled in with an invalid e-mail address in it. Second, there’s nothing verifying that the passwords match. You next tackle both of those and one more for the phone number too. Luckily, you already have the underlying structure in place for validation, so refinements become much easier.

Continue with more validation by adding a check to make sure the passwords match and that the e-mail address contains a period and an @ symbol.

1. Within the form.js file, add the following code in the validateForm() function, prior to the return errorFields statement:

// Check passwords match

if ($('#password2').val() != $('#password1').val()) {

    errorFields.push('password2');

}

//very basic e-mail check, just an @ symbol

if (!($('#email').val().indexOf(".") > 2) && ($('#email').val().indexOf("@"))) {

    errorFields.push('email');

}

2. Save the file (as form.js) in your document root.

3. Load http://localhost/form.php in your browser or reload the page if it's already open.

4. Enter something other than an e-mail address into the E-mail Address field.

Specifically, don’t enter an @ symbol in your input.

5. Click Submit Query.

You should see a page like the one in Figure 3-6.

9781118213704-fg060306.eps

Figure 3-6: Testing e-mail validation.

tip.eps Note that the error feedback indicates that e-mail is required. A further refinement would be to indicate that the address is invalid.

6. Enter a valid e-mail address into the field and enter a password into the first Password field.

7. Click Submit Query.

You now see an error indicating that the passwords don’t match, as depicted in Figure 3-7.

9781118213704-fg060307.eps

Figure 3-7: Testing password match validation.

Breaking this code down, you see there were two validations added: one for password match and one for e-mail address validation. Here’s the password matching validation:

if ($('#password2').val() != $('#password1').val()) {

    errorFields.push('password2');

}

This code simply checks the value of both fields and if they don't match, sets up an error connected to the password2 field of the form.

The e-mail validation looks like this:

//very basic e-mail check, just an @ symbol

if (!($('#email').val().indexOf(".") > 2) && ($('#email').val().indexOf("@"))) {

    errorFields.push('email');

}

This validation looks for a single dot in the address and also looks for an @ symbol. Granted, this is very basic validation, but e-mail addresses are notoriously complex things to check, given the number of valid variations and characters allowed in an address.

One final area to validate: the phone number. Although it isn’t a required field, when it is filled in, it would be nice to make sure that it contains at least a certain number of digits. Also, if the phone number is filled in, then the Number Type field suddenly becomes required.

Adding these checks won’t be quite as simple as the others, especially since the Number Type field is a radio button. Nevertheless, it isn’t too difficult to do so. Follow these steps.

1. Within form.js, add the following code in the validateForm() function prior to the return errorFields statement:

if ($('#phone').val() != "") {

    var phoneNum = $('#phone').val();

    phoneNum.replace(/[^0-9]/g, "");

    if (phoneNum.length != 10) {  

        errorFields.push("phone");

    }

    if (!$('input[name=phonetype]:checked').val()) {

        errorFields.push("phonetype");

    }

}

2. Save the file (as form.js).

3. Load the form.php page or reload if your browser is already open.

4. Fill in the required fields correctly and a valid ten-digit phone number into the Phone Number field, but don’t select either of the Number Type options. Click Submit Query.

You should receive a page like the one in Figure 3-8.

9781118213704-fg060308.eps

Figure 3-8: Testing Number Type validation.

You can see from Figure 3-8 that the visual feedback isn’t very evident or easy to spot. To correct that, you need to add some CSS.

5. Open form.css in your editor.

6. Within form.css, add the following CSS at the bottom of the file.

.phoneTypeError {

    margin-left: 1.2em;

    padding: 0.1em;

    background-color: #CC6666;

}

7. Reload form.php in your browser.

8. Fill in the required fields correctly and then type a valid ten-digit phone number into the Phone Number field, but don’t select either of the Number Type options. Click Submit Query.

You should now receive a page like the one in Figure 3-9.

9781118213704-fg060309.eps

Figure 3-9: The Number Type validation feedback is now more visible.

You now have some JavaScript validation complete, but your job isn’t nearly done. What you’ve done so far is helped the user receive fast feedback for filling out the form. The main and most important area for true form validation is within the server-side code, the PHP.

Performing PHP Validation

This section examines server-side validation with PHP. You use the HTML, CSS, and JavaScript from earlier in the chapter for the exercises in this section. The overall goal is to make sure that any input received from the user, whether from a web form, a web service, or elsewhere, is checked and sanitized.

So far you've been using an HTML page called form.php that set up a web form. The action of that web form refers to a page called form-process.php. In this section, you build form-process.php and a success page, too.

In order to pass errors back to the form, you need to use sessions. Additionally, you need to carve out a space to provide the error feedback from PHP within that form page. This means making some slight changes to the form.php file that you've been using. That seems like a logical place to start with an exercise.

1. Open form.php in your editor.

2. Within form.php, add the following code to the top, above the <doctype> declaration:

<?php session_start(); ?>

3. Change the <div id="errorDiv"></div> line to look like this code:

<div id="errorDiv">

<?php

    if (isset($_SESSION['error']) && isset($_SESSION['formAttempt'])) {

        unset($_SESSION['formAttempt']);

        print "Errors encountered<br />\n";

        foreach ($_SESSION['error'] as $error) {

            print $error . "<br />\n";

        } //end foreach

    } //end if

?>

</div>

4. In order to test the PHP validation, you need to skip the JavaScript validation. Therefore, comment out the JavaScript validation file, form.js, so that it doesn't load.

The line should look like this when you’re done:

<!-- <script type="text/javascript" src="form.js"></script> -->

5. Save form.php.

6. Load the page in your browser at http://localhost/form.php.

There should be no change from previous times when you loaded the page. However, now you don’t have to fill anything in at all and the form will submit without error because the JavaScript validation has been temporarily removed.

The PHP you added to form.php starts the session and then looks to see if the session variables named error and formAttempt are set. If those are set, then you know that there are errors and that the errors are the result of a form attempt. The formAttempt session variable is then unset. This helps for situations where users use the Back button in their browser. The formAttempt session variable will again be set next time they submit the form (as you see later).

If errors are encountered, output is created to that effect and each error message is printed to the screen. (You test it shortly.)

One other prerequisite item is to set up a success page. Follow these steps:

1. Create a new empty text file in your editor.

2. Place the following HTML in that file:

<!doctype html>

<html>

<head>

<title>A form - Success</title>

</head>

<body>

<div>

    Thank you for registering

</div>

</body>

</html>

3. Save the file as success.php in your document root.

Validating required fields

With the prep work complete, you can now begin building the form-process page. You build this file in stages, starting with the basic framework and then adding more complex validation and features as you go.

1. Open your text editor and create a new file.

2. In that file, place the following code:

<?php

//prevent access if they haven't submitted the form.

if (!isset($_POST['submit'])) {

    die(header("Location: form.php"));

}

session_start();

$_SESSION['formAttempt'] = true;

if (isset($_SESSION['error'])) {

    unset($_SESSION['error']);

}

$required = array("name","email","password1","password2");

$_SESSION['error'] = array();

//Check required fields

foreach ($required as $requiredField) {

    if (!isset($_POST[$requiredField]) || $_POST[$requiredField] == "") {

        $_SESSION['error'][] = $requiredField . " is required.";

    }

}

//final disposition

if (isset($_SESSION['error']) && count($_SESSION['error']) > 0) {

    die(header("Location: form.php"));

} else {

    unset($_SESSION['formAttempt']);

    die(header("Location: success.php"));

}

?>

3. Save the file as form-process.php in your document root.

4. Load the main form.php file at http://localhost/form.php in your web browser.

5. Click Submit Query without filling anything out in the form.

You should receive a page like that in Figure 3-10.

9781118213704-fg060310.eps

Figure 3-10: Verifying PHP validation.

tip.eps If you receive a page like those in any of the previous figures, with the text fields colored red, then the JavaScript validation is still firing. Make sure you've commented out the JavaScript from form.php, and make sure the page has been reloaded recently in your browser.

Before continuing, look at this code since it serves as the basis for your PHP validation.

The first thing done in the file is to make sure it’s being hit from the form’s Submit button:

//prevent access if they haven't submitted the form.

if (!isset($_POST['submit'])) {

    die(header("Location: form.php"));

}

If that isn't the case, then the browser is redirected back to form.php.

Next up, the session is started and the formAttempt variable is set to true. Recall that this variable is used within the form.php page to indicate that the user has come from this process page versus reloading or using his or her Back button.

Next, all the existing errors are unset. There is no need for them in the process page, and you need to recheck everything again. The error array is initialized again.

if (isset($_SESSION['error'])) {

    unset($_SESSION['error']);

}

$_SESSION['error'] = array();

Next, an array is set up with the required fields. This makes adding required fields later an easy task. Just add them to this array:

$required = array("name","email","password1","password2");

The heart of the basic required field validation is next, inside a foreach loop:

//Check required fields

foreach ($required as $requiredField) {

    if (!isset($_POST[$requiredField]) || $_POST[$requiredField] == "") {

        $_SESSION['error'][] = $requiredField . " is required.";

    }

}

If the field isn't set or is empty, then an error element is added to the $_SESSION['error'] array.

Finally, if the $_SESSION['error'] array has any elements, you need to redirect back to the form page; otherwise, send them to the success page.

//final disposition

if (count($_SESSION['error']) > 0) {

    die(header("Location: form.php"));

} else {

    unset($_SESSION['formAttempt']);

    die(header("Location: success.php"));

}

Validating text

You’ve now checked to make sure that something is filled in for the required fields, but you haven’t checked to see what they contain. For all you know, they could contain a single space.

Validating text typically means using a regular expression. This condition can be added to form-process.php directly above the //final disposition section:

if (!preg_match('/^[\w .]+$/',$_POST['name'])) {

    $_SESSION['error'][] = "Name must be letters and numbers only.";

}

This code sets up a regular expression to look for anything that isn't a letter or number (the \w part), a space, or a period. Obviously, if you have a form that allows other characters, they can be added to the character class. If you add that code to form-process.php and attempt to fill in something with other characters into the Name field, you'll receive the error.

Validating drop-downs, radio buttons, and check boxes

Validating data from drop-downs (or select/option elements), radio buttons, or check boxes should be done in the PHP. Even though it may appear that the users have to pick from one of the options, they may (maliciously or otherwise) not have that filled out correctly. It’s your job to make sure it’s valid.

The following code sets up an array of the valid states (from the drop-down in form.php) and then looks to see if what's being received is found in that valid array. This code can be added just above the final disposition section.

validStates = array("Alabama","California","Colorado","Florida","Illinois","New Jersey","New

York","Wisconsin");

if (isset($_POST['state']) && $_POST['state'] != "") {

    if (!in_array($_POST['state'],$validStates)) {

        $_SESSION['error'][] = "Please choose a valid state";

    }

}

One item of note here is that you not only need to check to see if the state is set, but also need to see that it isn’t blank. You need to do this because the default value on the form is blank for this drop-down and the field isn’t required, so blank is a valid value. If it’s set and not blank, though, then it needs to be set to a valid value.

The set of phone number type radio buttons is the same concept. Set up an array of valid values and check to make sure the value passed in is one of those valid values. Since this field isn’t required unless the phone number is filled in, save its check for later.

Validating numbers

Validating numbers can involve a regular expression, if you’re expecting a certain format or number of digits, or can involve math if you’re looking for certain values (or could be both too).

ZIP code validation presents an easier case, so you tackle that first. You need to validate that only digits were entered into the ZIP field and that there are at least five and no more than nine digits in the field. You could do this with a single regular expression, but doing so would prevent you from returning a specific error message: You wouldn’t know if users filled in letters or if they only had four digits in the ZIP field. Therefore, the method you in the next exercise separates those two tests into their own conditional.

This code can be added above the final disposition section:

if (isset($_POST['zip']) && $_POST['zip'] != "") {

    if (!preg_match('/^[\d]+$/',$_POST['zip'])) {

        $_SESSION['error'][] = "ZIP should be digits only.";

    } else if (strlen($_POST['zip']) < 5 || strlen($_POST['zip']) > 9) {

        $_SESSION['error'][] = "ZIP should be between 5 and 9 digits";

    }

}

The code first checks to see if the ZIP is set. If it is set and isn’t empty, then the next check is to see if it contains only digits. If it contains something other than digits, then there’s no need to run the next test. If digits are all that’s found, then the next check can be run, to make sure the length is between 5 and 9 digits.

Validating the phone number uses the same logic. If the phone field is set and not blank, then check to make sure it contains only digits. Next, the length is checked to make sure it’s at least ten digits. You could also add a maximum length check here, but this one will account for international numbers, too.

The phonetype field is checked next. If it isn't set (and you know that it's required because you're inside of a conditional test checking whether the phone number was set), then you return an error. Assuming that it's indeed set, check the value to make sure it's one of the acceptable values for the field, similar to that done in the previous section for the state drop-down.

This code can be added above the final disposition section in form-process.php.

if (isset($_POST['phone']) && $_POST['phone'] != "") {

    if (!preg_match('/^[\d]+$/',$_POST['phone'])) {

        $_SESSION['error'][] = "Phone number should be digits only";

    } else if (strlen($_POST['phone']) < 10) {

        $_SESSION['error'][] = "Phone number must be at least 10 digits";

    }

    if (!isset($_POST['phonetype']) || $_POST['phonetype'] == "") {

        $_SESSION['error'][] = "Please choose a phone number type";

    } else {

        $validPhoneTypes = array("work","home");

        if (!in_array($_POST['phonetype'],$validPhoneTypes)) {

            $_SESSION['error'][] = "Please choose a valid phone number type.";

        }

    }

}

Validating URLs and e-mail addresses

Truly validating an e-mail address is a surprisingly difficult task. The standard for e-mail addresses allows for complex combinations of letters, numbers, and special characters, some of which can only appear in certain positions. PHP versions 5.2 and greater include a filter_var()function that takes this complexity away and makes it easier to filter things like e-mail addresses and URLs (among other things).

This section examines validation of e-mail addresses and URLs.

Validating an e-mail address

The filter_var() function includes a number of built-in tests to check to see if an e-mail address is valid. Table 3-1 shows some of the built-in filters for validation.

Table 3-1 Select Validation Filters in PHP

Filter

Description

FILTER_VALIDATE_BOOLEAN

Validates that a value is a Boolean.

FILTER_VALIDATE_INT

Validates that a number is an integer.

FILTER_VALIDATE_FLOAT

Validates that a number is a floating point number.

FILTER_VALIDATE_IP

Validates an IP address.

FILTER_VALIDATE_EMAIL

Validates an e-mail address.

FILTER_VALIDATE_URL

Validates a URL.

Using the filters is very easy. For example, here's the code to validate an e-mail address. This code could be plugged into the form-process.php file above the final disposition section:

if (!filter_var($_POST['email'],FILTER_VALIDATE_EMAIL)) {

    $_SESSION['error'][] = "Invalid e-mail address";

}

That code is all you need to validate an e-mail address in PHP.

Validating a URL

Though not included in the form used in this chapter, URLs can be validated in the same way. Say you have a variable called $url. The validation code looks the same; it just uses a different filter.

if (!filter_var($url,FILTER_VALIDATE_URL)) {

    $_SESSION['error'][] = "Invalid URL";

}

Making sure the passwords match

Users who fill out this form need to enter their password twice. It’s then up to you to make sure that the passwords that a user entered are the same. Though this check occurs in the JavaScript, it also needs to occur in the PHP.

Your form processing page has already checked to make sure there are values in both of the password fields on the form, so checking that they match is as simple as this:

if ($_POST['password1'] != $_POST['password2']) {

    $_SESSION['error'][] = "Passwords don't match";

}

With that check, the form processing has been completed. Users can fill out the form and if, for some reason, the JavaScript didn’t catch an error, the error would be caught in the PHP.

Listing 3-1 shows the final form process page built in this chapter.

Listing 3-1: The Final Form Processing Page

<?php

//prevent access if they haven't submitted the form.

if (!isset($_POST['submit'])) {

    die(header("Location: form.php"));

}

session_start();

$_SESSION['formAttempt'] = true;

if (isset($_SESSION['error'])) {

    unset($_SESSION['error']);

}

$_SESSION['error'] = array();

$required = array("name","email","password1","password2");

//Check required fields

foreach ($required as $requiredField) {

    if (!isset($_POST[$requiredField]) || $_POST[$requiredField] == "") {

        $_SESSION['error'][] = $requiredField . " is required.";

    }

}

if (!preg_match('/^[\w .]+$/',$_POST['name'])) {

    $_SESSION['error'][] = "Name must be letters and numbers only.";

}

$validStates = array("Alabama","California","Colorado","Florida","Illinois","New Jersey","New

York","Wisconsin");

if (isset($_POST['state']) && $_POST['state'] != "") {

    if (!in_array($_POST['state'],$validStates)) {

        $_SESSION['error'][] = "Please choose a valid state";

    }

}

if (isset($_POST['zip']) && $_POST['zip'] != "") {

    if (!preg_match('/^[\d]+$/',$_POST['zip'])) {

        $_SESSION['error'][] = "ZIP should be digits only.";

    } else if (strlen($_POST['zip']) < 5 || strlen($_POST['zip']) > 9) {

        $_SESSION['error'][] = "ZIP should be between 5 and 9 digits";

    }

}

if (isset($_POST['phone']) && $_POST['phone'] != "") {

    if (!preg_match('/^[\d]+$/',$_POST['phone'])) {

        $_SESSION['error'][] = "Phone number should be digits only";

    } else if (strlen($_POST['phone']) < 10) {

        $_SESSION['error'][] = "Phone number must be at least 10 digits";

    }

    if (!isset($_POST['phonetype']) || $_POST['phonetype'] == "") {

        $_SESSION['error'][] = "Please choose a phone number type";

    } else {

        $validPhoneTypes = array("work","home");

        if (!in_array($_POST['phonetype'],$validPhoneTypes)) {

            $_SESSION['error'][] = "Please choose a valid phone number type.";

        }

    }

}

if (!filter_var($_POST['email'],FILTER_VALIDATE_EMAIL)) {

    $_SESSION['error'][] = "Invalid e-mail address";

}

if ($_POST['password1'] != $_POST['password2']) {

    $_SESSION['error'][] = "Passwords don't match";

}

//final disposition

if (count($_SESSION['error']) > 0) {

    die(header("Location: form.php"));

} else {

    unset($_SESSION['formAttempt']);

    die(header("Location: success.php"));

}

?>

Creating a validation function

The filter_var function goes a long way towards providing automated validation for common form elements. If you start working with forms, you'll find that you need to validate the same things over and over again, like ZIP code or state, too. Unfortunately, there aren't any built-in PHP functions to validate a ZIP code or state. But there's nothing preventing you from creating one!

For example, Listing 3-2 shows a function to validate a state.

Listing 3-2: Creating a State Validation Function

function is_valid_state($state) {

    $validStates = array("Alabama","California","Colorado","Florida","Illinois","New Jersey","New York","Wisconsin");

    if (in_array($state,$validStates)) {

        return true;

    } else {

        return false;

    }

} //end function is_valid_state

This function accepts an argument of the state to check. The state is checked against the list of known states. If the state is found among that list, the function returns Boolean true, meaning that it's a valid state.

Listing 3-3 shows a function to validate the ZIP.

Listing 3-3: Creating a ZIP Validation Function

function is_valid_zip($zip) {

        if (preg_match('/^[\d]+$/',$zip)) {

                return true;

        } else if (strlen($zip) == 5 || strlen($zip) == 9) {

                return true;

        } else {

                return false;

        }

} //end function is_valid_zip

Like the state function, the function in Listing 3-3 also accepts an incoming argument, this time the ZIP code to validate. The same basic validation checks are performed in this function as they were in the non-functionalized version from the form-process.php file. If the ZIP is just digits and is either five or nine digits, then Boolean true is returned; otherwise, false is returned.

In most cases, you'd create these functions in an external file and then require that file wherever needed through require_once() or through your autoload process. For example, you included those validation functions in a file called validation.inc and then used the following line at the top of the form-process.php file.

require_once("validation.inc");

Changing the form-process.php file to use these functions looks like this:

if (isset($_POST['state']) && $_POST['state'] != "") {

    if (!is_valid_state($_POST['state'])) {

        $_SESSION['error'][] = "Please choose a valid state";

    }

}

if (isset($_POST['zip']) && $_POST['zip'] != "") {

    if (!is_valid_zip($_POST['zip'])) {

        $_SESSION['error'][] = "ZIP code error.";

    }

}

Variations of these functions and concepts are used in the next chapter — and indeed throughout your career as a PHP programmer!