User-Defined Functions User-Defined Functions PHP enables you to create user-defined functions that, like JavaScript functions, enable you to package up code you want to reuse. Here's how a function is declared: function myFunction($arg = 0) { // Do stuff } The function keyword indicates that you're creating a user-defined function. The name of the function follows. In this case, it's myFunction. The rules for function names and variable names are the samenumbers, letters, and underscores are valid. The list of arguments that the function accepts follows the function name, in parentheses. The preceding function has one argument, $arg. In this example, I've set a default value for the argument. The variable $arg would be set to 0 if the function were called like this: myFunction(); On the other hand, $arg would be set to 55 if the function were called like this: myFunction(55); Functions can just as easily accept multiple arguments: function myOtherFunction($arg1, $arg2, $arg3) { // Do stuff } As you can see, myOtherFunction accepts three arguments, one of which is an array. Valid calls to this function include the following: myOtherFunction('one', 'two', array('three')); myOtherFunction('one', 'two'); myOtherFunction(0, 0, @stuff); myOtherFunction(1, 'blue'); One thing you can't do is leave out arguments in the middle of a list. So if you have a function that accepts three arguments, there's no way to set just the first and third arguments and leave out the second, or set the second and third and leave out the first. If you pass one argument in, it will be assigned to the function's first argument. If you pass in two arguments, they will be assigned to the first and second arguments to the function. file:///G|/1/0672328860/ch20lev1sec6.html (1 von 3) [19.12.2006 13:50:15] User-Defined Functions Returning Values Optionally, your function can return a value, or more specifically, a variable. Here's a simple example of a function: function add($a = 0, $b = 0) { return $a + $b; } The return keyword is used to indicate that the value of a variable should be returned to the caller of a function. You could call the previous function like this: $sum = add(2, 3); // $sum set to 5 A function can just as easily return an array. Here's an example: function makeArray($a, $b) { return array($a, $b); } $new_array = makeArray('one', 'two'); If you don't explicitly return a value from your function, PHP will return the result of the last expression inside the function anyway. For example, let's say I wrote the add function like this: function add($a = 0, $b = 0) { $a + $b; } Because $a + $b is the last expression in the function, PHP will go ahead and return its result. That's the case for logical expressions as well. Here's an example: function negate($a) { !$a; } negate(1); // returns false negate(0); // returns true Your function can also return the result of another function, whether it's built in or one you wrote yourself. Here are a couple of examples: function add($a = 0, $b = 0) { return $a + $b; } function alsoAdd($a = 0, $b = 0) { return add($a, $b); file:///G|/1/0672328860/ch20lev1sec6.html (2 von 3) [19.12.2006 13:50:15] User-Defined Functions } file:///G|/1/0672328860/ch20lev1sec6.html (3 von 3) [19.12.2006 13:50:15] Processing Forms 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: file:///G|/1/0672328860/ch20lev1sec7.html (1 von 11) [19.12.2006 13:50:17] Processing Forms <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: file:///G|/1/0672328860/ch20lev1sec7.html (2 von 11) [19.12.2006 13:50:17] Processing Forms $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. [View full size image] file:///G|/1/0672328860/ch20lev1sec7.html (3 von 11) [19.12.2006 13:50:17] Processing Forms 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 file:///G|/1/0672328860/ch20lev1sec7.html (4 von 11) [19.12.2006 13:50:17] Processing Forms 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(); file:///G|/1/0672328860/ch20lev1sec7.html (5 von 11) [19.12.2006 13:50:17] Processing Forms 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 file:///G|/1/0672328860/ch20lev1sec7.html (6 von 11) [19.12.2006 13:50:17] Processing Forms $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 rule label.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" class="error"><b>Name:</b></label> <?php } else { ?> <label for="name"><b>Name:</b></label> <?php } ?> <br /> <input name="name" value="<?= strip_tags($_POST['name']) ?>" /></p> file:///G|/1/0672328860/ch20lev1sec7.html (7 von 11) [19.12.2006 13:50:17] . array('digicam' => 'Digital Camera', 'mp3' => 'MP3 Player', 'wlan' => 'Wireless LAN'); It's an array that contains a. 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. 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