Validation in practice

bool is_string ( mixed var)

bool is_numeric ( mixed var)

bool is_float ( mixed var)

bool is_array ( mixed var)

bool is_object ( mixed var)

bool is_resource ( mixed var)

Any sensible site should include server-side validation of variables, simply because they are much harder to hack through, and because they will work no matter what browsers your visitors are using. The question is: how much client-side validation should be used?

In practice I server-validate everything, and client-validate as much as is possible. That way I am reducing server load because most people will take the hints from the client-side scripts and get the form right, and I am also keeping user wait times down. If you don't have time for this - after all, coding in JavaScript is not much fun - then you should at least client-validate variables that are of particular importance to your script. For example, check email addresses, ID numbers, and the like - it all helps improve your user experience.

An increasing number of sites are combining the bullet-proof nature of server-side validation with the quick user feedback of client-side validation use Ajax calls - use whatever works for you.

When validating data in PHP, I find it easiest to create an array of field names that are required, then make each field in my form an element in a master array - this allows me to check for required fields merely be differencing the two, and also to validate each field by simply looping through the array of submitted value.

Consider this script:

<?php
    
if (isset($_POST['Form'])) {
        
import_request_variables("p", "z");
        
$missingfields = array();
        
$required = array("FName"=>"First Name", "LName"=>"Last Name");

        while (list(
$var, $val) = each($required)) {
            if (isset(
$zForm[$var]) && $zForm[$var] != '') {
                
// check this value further here
            
} else {
                
$missingfields[$var] = $val;
            }
        }

        if (
count($missingfields)) {
            print
"You missed out one or more fields:<br />";

            while(list(
$var, $val) = each($missingfields)) {
                print
$val . "<br />";
            }
        } else {
            print
"Form passed!<br />";
            
var_dump($zForm['Languages']);
            exit;
        }
    }
?>

<form method="post" action="array.php">
First Name: <input type="text" name="Form[FName]" /> (required)<br />
Last Name: <input type="text" name="Form[LName]" /> (required) <br />
Age: <input type="text" name="Form[Age]" /><br /><br />
Languages known:<br />
<input type="checkbox" name="Form[Languages][]" value="PHP" checked="checked"> PHP</input>
para><input type="checkbox" name="Form[Languages][]" value="CPP"> C++</input>
<input type="checkbox" name="Form[Languages][]" value="Delphi"> Delphi</input>
<input type="checkbox" name="Form[Languages][]" value="Java"> Java</input>
<input type="submit" />
</form>

First take a look over the HTML part - notice that the NAME attribute for each element is Form[xxxx], which means PHP will end up with one variable holding an array containing each HTML element. Particularly of interest is Form[Languages], which has an empty set of brackets after it - this is so that $Form['Languages'] will be an array of the selected languages.

The PHP code to handle the form is only a little more complicated than the HTML, and is only executed if $_POST['Form'] has been set - that is, if the form has been submitted. The first thing I do is to use import_request_variables() to make all POST variables ("p") into regular variables, preceded with the letter "z", which will create the variable $zForm containing our user input.

The next step is to create an array of field names that are required fields - the reason it is a two-dimensional array is because the first dimension stores the field name (e.g. Fname), and the second dimension stores the textual name of the field (e.g. First Name). Testing for required fields being set is as simple as looping through the array of required fields, making sure each field is set in user input and also, crucially, making sure that the field has a non-blank value.

Each required value that is missing gets an entry in the $missingfields array, which is printed out at the end if there are any fields missing. The reason $missingfields is declared as an empty array just after import_request_variables() is because PHP will flag an error if we call count() on $missingfields when it hasn't been set, which would be the case if no missing fields were found.

One the form passes, there is a call to var_dump() in there so that you can see how the array-within-array mechanism works.

That script performs fairly basic validation - the comment "// check this value further here" is where you can really get into the nitty-gritty, because just verifying that a variable exists and is set is not really enough to get you by. At this point you need to call in the cavalry of type checking, which, in PHP terms, is the set of functions is_string(), is_numeric(), is_float(), is_array(), and is_object().

Each of these functions take just one parameter, a variable of their namesake, and return true if that variable is of the appropriate type. For example, is_numeric() will return true if the variable passed to it is a number, and is_object() will return true if its variable is an object. There is one other function of this type that works the same way but is useless for validation, and that is is_resource() - it's mentioned here for the sake of completeness.

Once you have verified that you have each of your required variables, that they are not empty, and that they are of the type you were expecting, you can go on to do more complicated checks. Are the integer values in the range you would expect? Do the string values have enough characters? Do the arrays have enough elements? Check, check, and check again, as it's better to validate your data early and issue an error as opposed to letting subtly bad input into your system.

Author's Note: There is a function confusingly similar to is_numeric(), called is_int(). This returns true if the variable passed in is an integer, which may sound similar to is_numeric(). However, data passed in through a form, even if numeric in content, is of type string, which means that is_int() will fail. Is_numeric(), on the other hand, returns true if the variable is a number or if it is a string containing a number also. This same problem applies to is_float(), as floating-point values set from user input are typed as strings.

 

Next chapter: Advanced variable validation using CTYPE >>

Previous chapter: Server-side validation

Jump to:

 

Home: Table of Contents

Follow us on Identi.ca or Twitter

Username:   Password:
Create Account | About TuxRadar