Processing Forms


You learned how to create forms back in Lesson 10, "Designing Forms," and although I explained how to design a form, I didn't give you a whole lot of information about what to do with form data once it's submitted. Now I'm going to explain how PHP makes data that has been submitted available to your PHP scripts.

When a user submits a form, PHP automatically decodes the variables and copies the values into some built-in variables. Built-in variables are like built-in functionsyou can always count on their being defined when you run a script. The three associated with form data are $_GET, $_POST, and $_REQUEST. These variables are all associative arrays, and the names assigned to the form fields on your form are the keys to the arrays.

$_GET contains all the parameters submitted using the GET method (in other words, in the query string). The $_POST method contains all the parameters submitted via POST in the response body. $_REQUEST contains all the form parameters regardless of how they were submitted. Unless you have a specific reason to differentiate between GET and POST, you can use $_REQUEST. Let's look at a simple example of a form:

<form action="post.php" method="post">   Enter your name: <input type="text" name="yourname" /><br />   <input type="submit" /> </form>


When the user submits the form, the value of the yourname field will be available in $_POST and $_REQUEST. You could return it to the user like this:

<p>Hello <?= $_REQUEST['yourname'] ?>. Thanks for visiting.</p>


Preventing Cross-Site Scripting

You have to be careful when you display data entered by a user on a web page because malicious users can include HTML tags and JavaScript in their input in an attempt to trick other users who might view that information into doing something they might not want to do, such as entering their password to your site and submitting it to another site. This is known as a cross-site scripting attack.

In order to prevent malicious users from doing that sort of thing, PHP includes the htmlspecialchars() function, which automatically encodes any special characters in a string so that they are displayed on a page rather than letting the browser treat them as markup. Or, if you prefer, you can use htmlentities(), which encodes all of the characters that are encoded by htmlspecialchars() plus any other characters that can be represented as entities. In the preceding example, you'd really want to write the script that displays the user's name like this:

<p>Hello <?= htmlspecialchars($_POST['yourname']) ?>. Thanks for visiting.</p>


That prevents the person who submitted the data from launching a successful crosssite scripting attack.

If you prefer, you can also use the strip_tags() function, which just removes all the HTML tags from a string.

Finally, if your form is submitted using the POST method, you should refer to the parameters using $_POST rather than $_REQUEST, which also helps to avoid certain types of attacks by ignoring information appended to the URL via the query string.


Once you have access to the data the user submitted, you can do whatever you like with it. You can validate it (even if you have JavaScript validation, you should still validate user input on the server as well), store it in a database for later use, or send it to someone via email.

Handling Parameters with Multiple Values

Most form fields are easy to deal with; they're simple name and value pairs. If you have a text field or radio button group, for example, you can access the value submitted using $_REQUEST, like this:

$radio_value = $_REQUEST['radiofield']; $text_value = $_REQUEST['textfield'];


There are some types of fields, however, that submit multiple name and value pairs, specifically check boxes and multiple select lists. If you have a group of five check boxes on a form, that field can actually submit up to five separate parameters, all of which have the same name and different values. PHP handles this by converting the user input into an array rather than a regular variable. Unfortunately, you have to give PHP a hint to let it know that a field should be handled this way. (PHP has no idea what your form looks like; all it knows about is the data that has been submitted.)

If you include [] at the end of the name of a form field, PHP knows that it should expect multiple values for that field and converts the parameters into an array. This occurs even if only one value is submitted for that field. Here's an example:

<form action="postmultiplevalues.php" method="post">   <input type="checkbox" name="colors[]" value="red" /> Red<br />   <input type="checkbox" name="colors[]" value="green" /> Green<br />   <input type="checkbox" name="colors[]" value="blue" /> Blue </form>


When the form is submitted, you can access the values as you would for any other parameter, except that the value in the $_REQUEST array for this parameter will be an array rather than a single value. You can access it like this:

$colors = $_REQUEST['colors']; foreach ($colors as $color) {   echo "$color<br />\n"; }


If the user selects only one check box, the value will be placed in an array that has only one element.

Task: Exercise 20.1. Validating a Form

One of the most common tasks when it comes to server-side processing is form validation. When users submit data via a form, it should be validated on the server, even if your page includes JavaScript validation, because you can't guarantee that JavaScript validation was actually applied to the form data.

I'm going to use a simplified version of the user registration form from Lesson 10 in this exercise. Figure 20.1 is a screenshot of the form I'll be using. Here's the HTML source:

Input

<html> <head> <title>Registration Form</title> </head> <body> <h1>Registration Form</h1> <p>Please fill out the form below to register for our site. Fields with bold labels are required.</p> <form method="post"> <p><label for="name"><b>Name:</b><br /> <input name="name" /></p> <p><label for="age"><b>Age:</b><br /> <input name="age" /></p> <p><label for="toys[]"><b>Toys:</b></label><br /> <input type="checkbox" name="toys[]" value="digicam" /> Digital Camera<br /> <input type="checkbox" name="toys[]" value="mp3" /> MP3 Player<br /> <input type="checkbox" name="toys[]" value="wlan" /> Wireless LAN</p> <p><input type="submit" value="register" /></p> </form> </body> </html>


Output

Figure 20.1. A simple user registration form.


As you can see, the form has three fieldsone for the user's name, one for the user's age, and one that enables the user to select some toys he or she owns. All three of the fields are required. The form submits to itself, using the POST method. I've specified the action for the form using a built-in PHP variable that returns the URL for the page currently being displayed. That way I can make sure the form is submitted to itself without including the URL for the page in my HTML. Here's the basic structure of the page:

<?php // Form processing code ?> <html>     <head>         <title>Page Structure</title>         <style type="text/css">             /* Page styles go here. */         </style>     </head>     <body>         <h1>Sample Page</h1>         <!-- Print form errors here -->         <form method="post" action="<?= $_SERVER['PHP_SELF'] ?>">             <!-- Present form fields here -->         </form>     </body> </html>


This structure is pretty common for pages that present a form and process that form as well. The PHP processor runs the scripts on the page from top to bottom, so all the form processing will take place before any of the page is presented. If this page were going to do more than just validate the form, it would probably redirect the user to a page thanking him or her for registering if the validation code found no errors. It would also probably save the values submitted through the form somewhere. In this case, though, I'm just explaining form validation.

As you can see, the form-processing code lives on the same page as the form itself, so the form will be submitted to this page. The validation code will live within the script section at the top of the page. My objective for this page is to make sure that the user enters all the required data and that the age the user enters is actually a number. To make things a bit easier on myself, I've written a function to do the actual validation for me.

Here's the function:

function validate() {     $errors = array();     if (empty($_POST['name'])) {        $errors['name'] = 'You must enter your name.';     }     if (!is_numeric($_POST['age'])) {         $errors['age'] = "You must enter a valid age.";     }     if (empty($_POST['toys'])) {         $errors['toys'] = 'You must choose at least one toy.';     }     return $errors;     }


This function validates each of the fields on the form and then places all the errors in an associative array called $errors. When an error is detected, a new entry is added to the array with the name of the field as the key and the error message as the array value. Later on, I'll display the error messages and use the field names to mark the fields that have errors.

On the first line of the function, I declare $errors to store the errors found during validation. Next, I validate the name parameter. PHP has a built-in function called empty() that checks to see whether a variable is empty. In this case, I use it to check $_POST['name'], which was set automatically when the form was submitted. If that variable is empty, meaning that the user did not submit his or her name, I add an entry to $errors.

Next, I validate the age field using PHP's is_numeric() function. I negate the condition with the not operator because it's only an error if the value in the field isn't numeric.

Finally, I check to make sure that the user has selected a toy. As you saw, this field is actually a check box group, meaning that the contents of the field are submitted as an array (assuming I've named the field properly). Again, I use empty() here. It works with regular variables and arrays, and it returns true if an array contains no elements. If there are no elements in the array, no toys were submitted, and the error is added to the array.

Once validation is complete, I return the value of the $errors variable to the caller. Here's the code I use to call the validate() function. It lives right at the top of the page:

$errors = array(); if ($_SERVER['REQUEST_METHOD'] == 'POST') {     $errors = validate(); }


I'm going to check on $errors later in the page regardless of whether I validate the input, so I go ahead and declare it. To determine whether I should validate a form submission or display an empty form, I check the built-in variable $_SERVER['REQUEST_METHOD'] to see whether the request was submitted using the POST method. If it was, then I want to do the input validation. If not, then I just want to display the form.

If any parameters were submitted via POST, I run the validate() function I just described. There's one more line of code at the top of the page where most of my PHP code lives:

$toys = array('digicam' => 'Digital Camera',     'mp3' => 'MP3 Player', 'wlan' => 'Wireless LAN');


It's an array that contains a list of all the check boxes to display for the toys field. It's easier to iterate over all the check boxes in a loop than it is to code them all by hand. If I need to add new toys to the list, I can just add them to the array definition and they'll automatically be included on the page. I'll show you how the code that displays the field works shortly.

Presenting the Form

Aside from validating form submissions, one of the other important functions of serverside processing is to prepopulate forms with data when they are presented. Many web applications are referred to as CRUD applications, where CRUD stands for create/update/delete. It describes the fact that the applications are used to mostly manage records in some kind of database. If a user submits a form with invalid data, when you present the form for the user to correct, you want to include all the data that the user entered so that he or she doesn't have to type it all in again. By the same token, if you're writing an application that enables users to update their user profile for a website, you will want to include the information in their current profile in the update form. This section explains how to accomplish these sorts of tasks.

However, before I present the form to the user, I'm going to provide a list of errors that the user needs to correct before the form submission is considered valid. Here's the code to accomplish that task:

<?php if (!empty($errors)) { ?>     <ul>         <?php foreach (array_values($errors) as $error) { ?>             <li><?= $error ?></li>         <?php } ?>     </ul> <?php } ?>


I use the empty() function yet again to determine whether there are any errors. If there aren't any, I can go ahead and present the form. If there are any errors, I present them in a list. First, I create an unordered list; then I use a foreach loop to iterate over the errors. The $errors variable is an associative array, and the error messages to present are the values in the array. I use the built-in array_values()function to extract an array containing only the values in the $errors array, and iterate over that array using the foreach loop. There's something interesting going on here. The body of the foreach loop is HTML, not PHP code. Look closely and you'll see the opening and closing braces for the foreach loop. Rather than sticking with PHP for the body of the loop, though, I go back to HTML mode by closing the PHP script, and I use regular HTML to define the list items. Inside the list item I use a short tag to present the current error message.

Separating Presentation and Logic

The point here is that it's common to mix PHP and HTML in this way. You create your loop using PHP but you define the HTML in the page rather than in echo() calls inside your PHP code. This is generally considered the best practice for PHP. You should write as much HTML as possible outside your PHP scripts, using PHP only where it's necessary to add bits of logic to the page. Then you can keep the bulk of your PHP code at the top or bottom of your page or in included files in order to separate the presentation of your data and the business logic implemented in code. That makes your code easier to work on in the future. As an example, rather than sprinkling the validation code throughout my page, I put it in one function so that a programmer can work on it without worrying about the page layout. By the same token, I could have built the unordered list inside the validation function and just returned that, but then my HTML would be mixed in with my PHP. Cleanly separating them is generally the best approach.


Once I've listed the errors, I can go ahead and present the form fields. Before I do that, let me show you one more thing I've added to the page. I included a style sheet that defines one rulelabel.error. The labels for any fields with errors will be assigned to this class so that they can be highlighted when the form is presented. Here's the style sheet:

<style type="text/css"> label.error {     color: red; } </style>


OK, now that everything is set up, let's look at how the name field is presented. Here's the code:

<p> <?php if (array_key_exists('name', $errors)) { ?>     <label for="name" ><b>Name:</b></label> <?php } else { ?>     <label for="name"><b>Name:</b></label> <?php } ?> <br /> <input name="name" value="<?= strip_tags($_POST['name']) ?>" /></p>


This code is a lot different from the old code I used to present the name field in the original listing in this example. First, I include an if statement that checks to see whether there's an error associated with this field. To do so, I use the array_key_exists() function, which is yet another built-in PHP function. Remember that the keys in $errors are the names of the fields with errors. So if the $errors array contains an element with the key name, it means that this field was not valid.

If there is an error with the field, I include the attribute in the <label> tag for the field. When the form is presented, the label will be red, indicating to the user that he or she needs to fix that field, even if the user didn't bother to read the list of errors. If there isn't an error, the normal <label> tag is printed.

Once that's done, I just have to print out the name field, but in this case I need to include the value that was submitted for the field if it's available. I include the value attribute in my <input> tag, and I use a short tag to include the value of $_POST['name'] as the value. Inside the expression evaluator, I've wrapped the variable containing the value for the field in the strip_tags() function. This PHP function automatically removes any HTML tags from a string, and it's used here to thwart any possible cross-site scripting attacks a malicious person might employ.

The age field is identical to the name field in every way except its name, so I'll skip that and turn instead to the toys field. Here's the code:

<p> <?php if (array_key_exists('toys', $errors)) { ?>     <label for="toys[]" ><b>Toys:</b></label> <?php } else { ?>     <label for="toys[]"><b>Toys:</b></label> <?php } ?> <br /> <?php foreach ($toys as $key => $value) { ?>     <input type="checkbox" name="toys[]"     <?php if (in_array($key, $_POST['toys'])) { echo ?checked="checked" ?; } ?>     value="<?= $key ?>" /> <?= $value?><br /> <?php } ?> </p>


As you can see, the code for marking the label for the field as an error is the same for this field as it was for name. The more interesting section of the code here is the loop that creates the check boxes. When I was describing the form-processing section of the page, I explained that I put all the toys inside the array $toys so that I could print out the check boxes using a loop. There's the loop.

The values in the <input> tags are the keys in the array, and the labels for the check boxes are the values. I use the associative array version of the foreach loop to copy each of the key/value pairs in the array into the variables $key and $value. Inside the loop, I print out the <input> tags, using toys[] as the parameter name to let PHP know that this field can have multiple values and should be treated as an array. To include the value for a field, I just use a short tag to insert the key into the value attribute of the tag. I use it again to print out the label for the check box, $value, after the tag. The last bit here is the if statement found within the <input> tag. Remember that if you want a check box to be prechecked when a form is presented, you have to include the checked attribute. I use the in_array() function to check if the key currently being processed is in $_POST['toys']. If it is, I then print out the checked attribute using echo(). This ensures that all the items the user checked before submitting the form are still checked if validation fails.

A browser displaying a form that contains some errors appears in Figure 20.2. Here's the full source listing for the page:

Input

<?php     $toys = array('digicam' => 'Digital Camera',         'mp3' => 'MP3 Player', 'wlan' => 'Wireless LAN');     $errors = array();     if ($_SERVER['REQUEST_METHOD'] == 'POST') {         $errors = validate();     }     function validate() {         $errors = array();         if (empty($_POST['name'])) {             $errors['name'] = 'You must enter your name.';         }         if (!is_numeric($_POST['age'])) {             $errors['age'] = "You must enter a valid age.";         }         if (empty($_POST['toys'])) {             $errors['toys'] = 'You must choose at least one toy.';         }         return $errors;     } ?> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Registration Form</title> <style type="text/css"> label.error {     color: red; } </style> </head> <body> <h1>Registration Form</h1> <p>Please fill out the form below to register for our site. Fields with bold labels are required.</p> <?php if (!empty($errors)) { ?>     <ul>         <?php foreach (array_values($errors) as $error) { ?>             <li><?= $error ?></li>         <?php } ?>     </ul> <?php } ?> <form method="post" action="<?= $_SERVER['PHP_SELF'] ?>"> <p> <?php if (array_key_exists('name', $errors)) { ?>     <label for="name" ><b>Name:</b></label> <?php } else { ?>     <label for="name"><b>Name:</b></label> <?php } ?> <br /> <input name="name" value="<?= strip_tags($_POST['name']) ?>" /></p> <p> <?php if (array_key_exists('age', $errors)) { ?>     <label for="age" ><b>Age:</b></label> <?php } else { ?>     <label for="age"><b>Age:</b></label> <?php } ?> <br /> <input name="age" value="<?= strip_tags($_POST['age']) ?>"/></p> <p> <?php if (array_key_exists('toys', $errors)) { ?>     <label for="toys[]" ><b>Toys:</b></label> <?php } else { ?>     <label for="toys[]"><b>Toys:</b></label> <?php } ?> <br /> <?php foreach ($toys as $key => $value) { ?>     <input type="checkbox" name="toys[]"     <?php if (in_array($key, $_POST['toys'])) { echo 'checked="checked" '; } ?>     value="<?= $key ?>" /> <?= $value?><br /> <?php } ?> </p> <p><input type="submit" value="register" /></p> </form> </body> </html>


Output

Figure 20.2. A form with some errors that were caught during validation.





Sams Teach Yourself Web Publishing with HTML and CSS in One Hour a Day
Sams Teach Yourself Web Publishing with HTML and CSS in One Hour a Day (5th Edition)
ISBN: 0672328860
EAN: 2147483647
Year: 2007
Pages: 305

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net