ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


User-Friendly Form Validation with PHP and CSS

by Jeff Cogswell
04/22/2004

One of the more common problems PHP programmers must tackle is that of form data validation. For example, suppose you have a form where users must enter an email address. Using a regular expression, you could test whether the format of the email address is valid before using the email address; if the email address doesn't have a valid format, you can ask the user to re-enter it.

Unfortunately, many people code their form tags so that the tag's action parameter takes the user to another page that processes the data. Where does data validation fit in? Many PHP programmers resort to client-side validation using JavaScript. In the case of validating an email address, you could have JavaScript test the email address against a regular expression, and if the format is wrong, display a pop-up message box and not allow the form's submit to go through.

However, that's a bit clumsy (not to mention problematic, if the user turns off JavaScript). Rather, today's web sites usually let the user click the Submit button, and then the form's page reappears along with a message right on the page itself, stating the problem, such as "Please enter a valid email address."

This message would be on the page along with the original form. Also, the item in the form that had the problem (in this case, the text box where the user enters an email address) might have a visual indication of a problem, such a text label whose color is red, as shown in Figure 1.

a form with a validation error
Figure 1. A form with a validation error

What if you want your validation to involve a database lookup? This clearly must take place on the server side. It would be nice if the user could click Submit and have the form return a message that the username is not valid, similar to the example in Figure 1, for valid email formats.

A lot of web sites already provide such functionality. Unfortunately, these websites often have the filename extensions .asp or .aspx, and not .php. What a shame. Who says the ASP folks down the street should have all the fun? In this article, I'll show you how you can add such functionality to your PHP forms. (Besides, have you ever tried programming in ASP? Frankly, PHP is a lot easier!)

The Basic Key

The basic key to implementing a form validation pattern is to understand the flow of page processing that takes place. Without server-side validation (or with client-side JavaScript validation), you would have your form's action parameter set to the page that will receive and process the request that's separate from the page hosting the form. For example, you might have this line inside of a login.php page:

<form method="post" action="processlogin.php">

In this scenario, the login page contains the form where the user enters the username and password, and the username and password get sent back to the processlogin.php page, which tests the username and password against a database. What if the login turns out to be incorrect? You might have separate PHP code in the processlogin.php page that prints out another form for the user to try again. Or, if you're sneaky, you might manipulate the headers and send the user back to the original processlogin.php page.

PHP Cookbook

Related Reading

PHP Cookbook
By David Sklar, Adam Trachtenberg

There's another way to do it that's a little cleaner, and, more importantly, allows you to keep your validation code right inside of the login.php page. Since data validation is associated with the form, it only makes sense to keep the data code together with the form. Consider the following line inside of your login.php file:

<form method="post" action="login.php">

Here, the action parameter points the user right back to the same page the user was already on. Hmmmm, now that sounds kind of interesting, doesn't it? Then, at the beginning of the login.php page, you would include PHP code that does the validation on the server side. (Of course, you have to get a little sneaky to prevent the validation from taking place the first time the page loads.) Then, when the validation succeeds, you issue a redirect like so, using the header function:

header("location: loginsucceeded.php");

This might sound a bit confusing, but once you get the gist of the basic flow, it actually makes good sense and is quite simple. The following sections show how to implement this approach.

A Sample Page

Here's a sample page called email.php that demonstrates the basic technique.

This page allows the user to enter an email address and name, such as in a signup page for a mailing list:

<?
    $message    = "";
    $emailclass = "basictext";
    $username   = "";

    if ($_POST['process'] == 1) {

        $pattern = '/.*@.*\..*/';
        $email   = $_POST['email'];
        $urlname = urlencode($$_POST['username']);

        if (preg_match($pattern, $_POST['email']) > 0) {
            // Here's where you would store 
            // the data in a database...
            header(
              "location: thankyou.php?&username=$urlname");
        }
        $message    = "Please enter a valid email address.";
        $username   = $_POST['name'];
        $emailclass = "errortext";
    }
?>

<html>
<style>
    .basictext {
        font-family: Arial, Helvetica, sans-serif; 
        font-size: 14px; color:#000066;
    }
    .errortext {
        font-family: Arial, Helvetica, sans-serif; 
        font-size: 14px; color:#C00000; font-weight: bold;
    }
</style>
<body>
<form action="email.php" method="post">
    <? if ($message != "") {
        print '<span class="errortext">'.
            $message."<span><br>\n";
    } 
    ?>
    <span class="<?print $emailclass; ?>">
        Email address:</span>
    <input name="email" type="text" 
        class="<? print $emailclass; ?>"><br>
    
    <span class="basictext">Your name:</span>
    <input name="name" type="text" class="basictext" 
        value="<?print $username; ?>"><br>
    <input type="hidden" name="process" value="1">
    <input type="submit" name="Button1" value="Sign up!">
</form>
</body></html>

How it Works

The first section is the processing section. Notice the trick: if the $_POST array contains a key called Process with a value of 1, then begin processing; otherwise, just continue on. In other words, the first time the user opens the page, processing will not take place. You can see farther down that the form includes a hidden field called process with the value 1. Thus, when the user clicks the submit button, the page will load again, but this time processing will take place. Voila! The problem of the chicken and egg has been solved!

(Note, however, that the purists might balk at the fact that I'm playing on a feature in PHP: ff the $_POST['process'] variable doesn't exist, as is the case the first time the page loads, PHP still lets me test it against the value of 1; I'll simply get back a false. If this bothers you, you can add an isset call to first check whether $_POST['process'] even exists.)

The processing here is simple. I threw together a simple regular expression to check for an email address. (However, please don't use this regular expression in your own production code, as it's not foolproof; for a better one, check out the PHP Cookbook.) If the email address matches the regular expression, then all is fine and I issue a redirect to the next page, called thankyou.php. If the address doesn't survive the regular expression test, then I stay on the page and store a value in the $message variable.

Next, notice how I play with the styles: I have two styles defined inside of the <head> tag. One is for the basic text, a basic dark blue. The other is for bold red text used with errors to attract attention to the problem. In the PHP code, I store the basic text-style name inside of the $emailclass variable. If the code encounters an invalid email address, it changes the value of $emailclass to errortext. Then, when I create the label for the email address and the text box for the email address, I use the class stored in $emailclass, like so:

class="<?print $emailclass; ?>">

Thus, when all is fine, the HTML displays regular text. When there's a problem, it displays bold red text.

Notice also that I store some defaults in the fields. Initially, $username holds an empty string. On the return trip through the file, if the validation fails, I put the $_POST['name'] value back into the $username variable. (Remember, and this is important: on the return trip through the file, you are running the program as if it's brand new. Any variables you set the previous time are no longer present, unless you use a session variable; see Session handling functions for more information.) Later down in the code, I stuff the $username value into the form, like so:

<input name="name" type="text" class="basictext" value="<?print $username; ?>"><br>

Thus, if the user enters an improper email address, when the form returns, it will have the username already filled in. This technique is extremely important for user-friendly forms, because if the user forgets an @ sign in the email address (or the AOL users just type a screen name without @aol.com), he or she doesn't want to have to retype everything else into the form. This way, the form will return already populated with everything the user already typed in. In this case, I also decided to fill in the email address box with what the user previously typed, just so he or she can see the incorrect value and fix it.

The final thing to show you about this code is how I pass the value of the username on to the next page. Notice first that the form uses a POST method. Whenever you're sending out passwords, you want to use a POST method, since a GET method will show the password in the URL (and in the history file). People wouldn't be too happy about that if somebody is glancing over their shoulders. Of course, I'm not using a password in this example, but you might be in your own project; thus, I used the POST method as a demonstration.

However, the next page (in this case, thankyou.php) might well need access to the data that the user entered. The easiest way to pass it is through a GET, like so:

header("location: thankyou.php?&username=$urlname");

(I called urlencode so that the spaces and any other unusual characters will make it across.) Yes, I'm using a GET method, even though I just raved about the importance of using a POST method. However, I didn't pass the email address. The reason is that you would probably go ahead and store or process the username and password right inside of the processing code before calling header, rather than waiting until you get to the next page, in this case thankyou.php. Why now? Because you're already doing all the verifying; you might as well do the real processing, as well. For example, in a signup form, you might need to test whether the chosen username already exists; if it does, then you'll issue a message to the user that the username exists and to try a different one. If all is fine, you could go ahead and store the information in the database, before proceeding to the thankyou.php page. (It's possible, then, that you won't have to pass anything at all to the thankyou.php page, and therefore won't even need the ?&username= portion of the URL in the header call.)

More Possibilities

Here are some things that you could do with this technique:

You can even get fancier. When a user logs out, you could send the user back to a login.php page, with an added message saying, "You have been logged out. Use this form if you wish to log back in." How would you do that? After logging the user out, redirect the user back to the login.php page, passing a unique value (such as 2) in with the URL, as in login.php?process=2. Then add an if block to the login.php page checking the $_GET['process'] for the value 2. If the comparison succeeds, set the message appropriately. That way, you don't need a separate login page just for the extra message. (You are free to mix $_GET and $_POST all within the same page. PHP handles it just fine.)

Conclusion

Using this technique, you can easily create some sophisticated web sites that don't require a lot of duplicate code and unnecessary processing, all while sticking to our beloved PHP without having to touch either JavaScript or ASP.

Jeff Cogswell lives near Cincinnati and has been working as a software engineer for over 15 years.


Return to the PHP DevCenter.

Copyright © 2009 O'Reilly Media, Inc.