Tuesday, July 14, 2009

Advanced PHP for Web Professionals By Christopher Cosentino Chapter 4

Introduction to Better Forms
This chapter covers forms. Forms are the most fundamental method of interaction for your users. Users must use a form to enter information into a site. Think about it, every bulletin board, shopping cart, feedback form, and poll is a type of form. Without forms, the Web is nothing more than a publishing medium for those who can FTP Web pages up to a server.

Since forms are the basis for much of the user interaction on your site, it behooves you to make it easier for users to enter information. How many times have you filled in a long form only to have it tell you that you filled something out wrong and to click "Back" to fix it? Upon clicking the "Back" button you find the entire form empty, forcing you to fill in all of the information again! Or how about forms that tell you that you filled in something incorrectly, but upon closer inspection you cannot tell what is wrong, and the script does not provide any more clues. Wouldn't it be better if you didn't have to enter all of the information again, and the fields that you filled out incorrectly were highlighted some way so that you could easily make corrections?

You can check data before the form is submitted by using JavaScript, but not all browsers treat JavaScript the same way, so you might run into compatibility problems with some browsers. With PHP, you can be sure to send only pure HTML to the browser, reducing cross-browser compatibility errors. Checking for errors using PHP instead of JavaScript does increase the load on the server slightly, depending on the complexity of your code.

Post and Get
There are two methods that you can use when creating a form in HTML. They are post and get, as in:

<form action=submit.php method=post>

or:

<form action=submit.php method=get>

If you don't specify a method, then the Web server assumes that you are using the get method. So what's the deal? They do the same thing right? Well, almost. You may have noticed that the URL looks a lot longer after you submit a form that uses the get method. For example, you may see something like:

http://web.com/form.php?name=fred&age=20&comments=This+site+rocks

That's because the get method puts the contents of the form right in the URL. There are a few disadvantages to this. First, depending on your Web server's operating system and software, there is a limit to how much data you can send through a form using the get method. On many systems, this limit is 256 characters. Also, the individual get queries may be stored in your Web server logs. If you are using space on a shared server, then other people may be able to decipher data sent from your forms that use the get method.

The post method was created to correct the inadequacies of the get method. The information sent using the post method is not visible in the URL, and form data cannot be deciphered by looking in the Web server logs. There also isn't such a small limit on the amount of data that can be sent from a form. Again, it depends on your server, but you probably won't ever hit the limit of sending data using the post method for a text-based form.

I use the post method for my scripts unless I need to debug something. When you need to debug something on a form, it is easy enough to switch to the get method (by changing the action line in your script) and then check the URL after you submit your buggy form. You can usually pick up typos and such with a quick look.

Making Forms Friendly
The first script in this chapter, form1.php, shows you how you can make a simple script to determine if the user filled in all of the fields. If the user fails to fill in all the fields, then the script prints out the form again, printing out the field names in red for the fields that the user missed. It also saves any text that the user previously entered and automatically populates the form fields with those values.

The script contains three functions:

print_form()

check_form()

error_flag()

The complete script and explanation follows:

Script 4-1 form1.php
[View full width]
1. <?
2. $page = "form1.php";
3. ?>
4. <html>
5. <head>
6. <style type="text/css">
7. .error {color:red;}
8. </style>
9. </head>
10. <body bgcolor="#FFFFFF" text="#000000">
11. <?
12. function error_flag($error, $field) {
13. if($error[$field]) {
14. print("<td>");
15. } else {
16. print("<td>");
17. }
18. } //end function error_flag
19.
20. function print_form() {
21. global $error, $print_again, $first, $last, $page;
22. ?>
23. <form action="<? echo $page ?>" method="post">
24. <?
25. if($print_again) {
26. ?><h3>You missed some fields. Please correct the <span>red</span>
fields.<?
27. } else {
28. ?><h3>Please fill-in the following fields.</h3><?
29. }
30. ?>
31. <table border="0">
32. <tr><td <? error_flag($error, "first"); ?>First Name:</td>
33. <td><input type="text" name="first" value="<?=$first ?>"></td></tr>
34. <tr><td <? error_flag($error, "last"); ?>Last Name:</td>
35. <td><input type="text" name="last" value="<?=$last ?>"></td></tr>
36. <tr><td colspan="2" align="center">
37. <input type="submit" name="submit" value="Submit Form"></td></tr>
38. </table>
39. </form>
40. <?
41. } // end function print_form
42.
43. function check_form() {
44. global $error, $print_again, $first, $last;
45. $error['first'] = false;
46. $error['last'] = false;
47. $print_again = false;
48. if($first == "") {
49. $error['first'] = true;
50. $print_again = true;
51. }
52. if($last == "") {
53. $error['last'] = true;
54. $print_again = true;
55. }
56. if($print_again) {
57. print_form();
58. } else {
59. print("<h3>Thank you for completing the form!</h3>");
60. }
61. } // end function check_form
62.
63. /***** MAIN *****/
64. if(isset($submit)) {
65. check_form();
66. } else {
67. print_form();
68. }
69. ?>
70. </body>
71. </html>

Script 4-1. form1.php Line-by-Line Explanation LINE
DESCRIPTION

2
Set the $page variable to the script name.

12
Define the error_flag() function. It takes two arguments, $error and $field. The $error variable is actually an array, which contains all of the errors that were found in the form. The $field variable is the current field for which the script is generating an error.

13–17
Check to see if there is an error for this particular field. If there is an error, then the script prints <td>, which causes that field name to be printed in red. Otherwise, the script prints out a normal <td> tag.

18
End the error_flag function.

20
Declare the print_form() function.

21
Define some global variables so that they can be easily used in the function. The variables are:

$error— An array of errors (if any) returned from the check_form() function.

$print_again— A boolean variable returned from the check_from() function.

$first and $last— Variables returned from the forms in this field.

$page— We use this variable in the form action=… line. Useful so that we can easily change the name of the page and not worry about breaking our scripts.

All of these variables are empty if this is the first time that this script is being run.

22–24
Print out the form tag. Note that we include the $page variable. Also note the post method.

25–30
The if/then/else statement checks to see if the $print_again variable is set to true. If it is, then we print a message saying that some of the fields were missed previously. If the $print_again variable is not set, then we just print a message asking the user to fill in the form.

31–40
Print out the form fields. This is a pretty standard form except for two things:

Each field label is preceded by the function error_flag instead of a normal <td> tag. This is because we want to check to see if the users previously screwed up entering information into this field. If they did (and they do at one time or another), then the error_flag() function prints out <td> instead of <td>. The error Cascading Style Sheets (CSS) class simply makes the text in that cell red.

Each text input has a value associated with it—for example, value="<?=$first ?>". If this is the first time that the user is filling in the form, then these values are blank. However, if the user previously made an error and this is the second or subsequent time that he or she is filling out the form, then the previously entered value is shown in the form. That way, the user doesn't have to re-enter information into the form.

41
End the print_form() function.

43
Define the check_form() function.

44
Allow the function to use the global variables listed. Note that these are the same variables used in the print_form() function.

47
Initialize the $print_again variable to false. Assuming all goes well with the data that the user entered, then this should remain false.

48–51
Check the $first variable (representing the user's first name) to see if the user entered a value. If he or she did not enter anything into this field, then we generate an error by setting the first element in the $error array to true and also setting the $print_again variable to true.

52–55
These three lines do the same thing as lines 48–51, except that this time we are checking the $last variable (representing the user's last name).

56–60
If an error was encountered and the $print_again variable is set to 1 (true), then print the form again, else print a message to the user.

61
End the check_form() function.

64–65
If the Submit button was pressed, then execute the check_form() function.

66–68
If the Submit button was not pressed, then execute the print_form() function.

70–71
Close out the HTML page.

Souping Up the Script
You can soup up this script to be smarter:

Check for Spaces
This script as it currently is can be easily fooled if a user just enters spaces in each of the fields. In the check_form script on lines 46 and 50, we check to see if the user entered anything. This includes spaces. If we want to make sure the users enter characters (not just spaces), we could check like this:

if(ereg("^ +", $last)) {
$error[last] = true;
$print_again = true;
}

Basically, the ereg() function is checking the $last variable to see if it starts with one or more spaces. We'll take a closer look at the ereg() function a little later in this chapter.

Check for Minimum Number of Characters
Sometimes you may want to make sure the user enters a minimum number of characters. It's easy enough to enforce a limit on the maximum number of characters that a user can enter, by using the maxlength attribute on an HTML input tag—for example, <input type="text" maxlength="24">.

if(strlen($first) < 3) {
$error[first] = true;
$print_again = true;
}

Here we use the strlen() function (string length) to see if the $first variable contains at least three characters. If it has zero, one, or two characters then we throw an error.

Add More Error Information
It's also useful to add a little extra error information. We can modify our examples above slightly:

if(strlen($first) < 3) {
$error['first'] = "Please enter more than two characters in this field. ";
$print_again = true;
}
if(ereg("^ +", $last)) {
$error['last'] = "Please enter characters in this field, not spaces. ";
$print_again = true;
}

Here, instead of assigning the $error variable a true value, we can add a short description of the error. Then if we modify our error_flag function as follows, we can print this message out to the users when we display the form to them again:

function error_flag($error, $field) {
if($error[$field]) {
print("<td>$error[$field]");
} else {
print("<td>");
}
} //end function error_flag

Form-Field Checking
As you can see, it isn't too hard to make a form a little more user-friendly. You can make the task of entering information easier on the user, but to make it worthwhile for yourself, you should check to see that the user has entered data that is actually useful. Otherwise, why collect it? Just checking to make sure that they filled in a field is not always adequate.

That's where ereg() comes in. ereg() is a built-in PHP function. It means Evaluate a Regular Expression. There is also a PERL-compatible function that works the same as ereg() (and may be a little faster) called preg_match(). The syntax for both functions is very similar, if not exactly the same in most instances. The syntax is:

ereg(search, string)

where search is the regular expression search criteria, and string is the string that you wish to search. The ereg function returns a boolean value of either true or false. If the search criteria matches the string, then ereg() returns true; otherwise, it returns false.

Now, regular expressions are one of those things that all programmers who spend a lot of time wading around in text should really get to know. The different search criteria used in regular expressions can be cryptic, but they really get the job done.

Take a look at the example we used earlier:

if(ereg("^ +", $last)) {
$error['last'] = true;
$print_again = true;
}

The search criteria, "^ +", literally means, "A string starting with a space and followed by zero or more spaces." We search for this because we do not want users to fool the form into being accepted by entering spaces for the last name instead of characters.

However, we can get a lot more detailed about analyzing the data entered into a form using other regular expression operators. A list of common regular expression operators can be found in Table 4-1.

Table 4-1. Common Regular Expression Operators SYMBOL
DESCRIPTION
EXAMPLES

*
Match zero or more occurrences.
Just like the UNIX wildcard symbol, * matches everything. Use this for groups of symbols.

+
Match one or more occurrences.
"e+" matches a string that has one or more occurrences of the character "e" in it.

^
Placed at the beginning of a string, matches that string at the start.
"^Hello" matches any string that starts with the word "Hello".

$
Placed at the end of a string, matches that string at the end.
"$goodbye!" matches any string that ends with "goodbye!"

|
Used to separate searches.
"a|b" matches a string that contains the character "a" or the character "b".

[]
Matches range of characters.
"[a-z]" matches a string that contains a lowercase character from a through z.

"[A-D]" matches a string that contains an uppercase character from A through D.

"[0-9]" matches a string that contains a numeral from 0 through 9.

"[1-5]" matches a string that contains a numeral from 1 to 5.

()
Group operators in a sequence.
Parentheses work similarly to how they do in most programming operations. You can group items together. For example, (5[0-9]+) matches a string that contains a 5 followed by one or more numerals. "5" does not match, while "55" does.

.
Matches any single character.
"[0-9].[0-9]" matches a string that contains a numeral followed by any other character, followed by a numeral.

\
Escape an operator so that it is taken literally.
You must escape the following operators if you want to search for that particular operator literally: [*\+?{}.]

Checking for Valid Email Addresses
One of the most common things for a user to enter into a form is an email address. Having users' email addresses lets you do things like send users their password if they happen to forget it. This saves users from having to create a new account on your site, and it also saves you some space on your database.

But sometimes a user will fill out a form and not put in a valid email. Have you ever received a form submission with something along the lines of "qwert" in the email field? Using regular expression matching, you can at least check the user's email address entry to see if it is technically valid.

A technically valid email address consists of a username, the "@" sign, and a server name. Valid usernames can contain letters, numbers, the underscore ("_"), the minus sign ("-"), and periods ("."). Valid server names are almost the same, except that server names cannot contain an underscore. Finally, the end of the domain name must have a "." in it followed by two or more letters, such as ".com", ".it", or ".info". Using our regular expression operators from Table 4-1, we can build a regular expression that matches against valid email addresses.

Let's state that a valid username must start with at least one letter or one number:

^[a-z0-9]+

This is followed by zero or more letters or numbers, underscores "_", or minus signs "-":

[a-z0-9_-]*

Then it can be followed by zero or one "."s, followed by any number of letters or numbers, underscores "_", or minus signs "-":

(\.[a-z0-9_-]+)*

then followed by the @ sign:

@

which is then followed by at least one letter or number:

[a-z0-9]+

Then it can be followed by zero or one "."s, followed by any number of letters or numbers, underscores "_", or minus signs "-":

(\.[a-z0-9_-]+)*

Finally, it can be followed by another ".", then two or more letters (the end of the domain name), and then the email string must end:

\.([a-z]+){2,}$

Put it all together and you get:

^[a-z0-9]+(\.[a-z0-9_-]+)*@[a-z0-9_-]+(\.[a-z0-9-]+)*\.([a-z]+){2,}$

But we haven't taken into account the case of the letters! The expressions above only allow for lowercase letters in the email address, but uppercase letters are valid as well. To save some time, we didn't include the "A–Z" ranges in our expressions, because we can use a slightly different form of ereg(), which is called eregi(). eregi() works exactly the same as ereg() except that it is not case-sensitive. That way you don't have to search for both uppercase and lowercase characters in your expressions, using the [a-zA-Z] syntax. You only need to use [a-z].

Our eregi() function now looks like this:

eregi("^[a-z0-9]+[a-z0-9_-]*(\.[a-z0-9_-]+)*@[a-z0-
9_-]+(\.[a-z0-9_-]+)*\.([a-z]+){2,}$", $email);

It's very ugly, but it does a very good job of checking for valid email addresses.

Here is a short script that demonstrates the prowess of this function:

Script 4-2 checkemail.php
1. <?
2. $email = array ("chris_2@company.com", "-
fred@broken.org", "joe.smith@works.it", "Busted@bad.a",
"strange@44.44", "works.fine.all_day@x.y.z.com","is-
dashed-line@d-a-s-h-e-d.com", "CC@c.com",
"this.works.fine@also.ok");
3.
4. for($i = 0; $i < sizeof($email); $i++) {
5. if(eregi("^[a-z0-9]+[a-z0-9_-]*(\.[a-z0-9_-]+)*@[a-z0-9_-]+(\.[a-z0-
9_-]+)*\.([a-z]+){2,}$", $email[$i])) {
6. echo "<p>$email[$i] is valid.";
7. } else {
8. echo "<p>$email[$i] is <b>not</b> valid.";
9. }
10. }
11. ?>

Script 4-2. checkemail.php Line-by-Line Explanation LINE
DESCRIPTION

1
Tell the server to start parsing as PHP code.

2
Create an array of valid and invalid email addresses.

4
Create a for loop to loop through each email address in the array.

5
Test the email address received from the array against the eregi() function to check for valid email addresses. If the email is valid, line 6 executes. Otherwise, the script executes line 8.

6
Executed if the email address matches the eregi() expression.

7–9
Executed if the email address does not match the eregi() expression.

10
End the for loop.

11
Tell the server to stop parsing the page as PHP code.

There is also an additional syntax that you can use with ereg() and eregi() to search strings. See Table 4-2 below.

Table 4-2. Portable Regular Expression Operators SYMBOL
DESCRIPTION

[[:alpha:]]
equal to [a-zA-Z]

[[:alnum:]]
equal to [a-zA-Z0-9]

[[:digit:]]
equal to [0-9]

[[:lower:]]
equal to [a-z]

[[:upper:]]
equal to [A-Z]

[[:space:]]
equal to a blank space (" ")

[[:print:]]
matches any printable character, except that /n (newline) and /t (tab) do not match

[[:graph:]]
matches a string with any graphical character except a space

[[:xdigit:]]
hexidecimal, equal to [a-fA-F0-9]

[[:punct:]]
matches a string with any punctuation

The same expression that we used for the email syntax checking:

eregi("^[a-z0-9]+[a-z0-9_-]*(\.[a-z0-9_-]+)*@[a-z0-
9_-]+(\.[a-z0-9_-]+)*\.([a-z]+){2,}$", $email);

is written like this using the portable syntax:

eregi("^[[:alpha:]]+[[:alnum:]_-]*(\.[[:alnum:]_-
]+)*@[[:alnum:]_-]+(\.[[:alnum:]_-]+)*\.([[:alpha:]_-
]+){2,}$", $email);

Using $HTTP_GET_VARS and $HTTP_POST_VARS
PHP automatically registers[1] any variables passed from a form into the $HTTP_GET_VARS or $HTTP_POST_VARS array. If the form is submitted with the post method, then any values in the form are assigned to the $HTTP_POST_VARS array. If the form is submitted using the get method (the default method if no method is specified), then the values of the form are assigned to the $HTTP_GET_VARS array.

[1] Your php.ini file is automatically enabled for this feature if you are using PHP 4.0.3 or greater. If it is not turned on, then you have to edit the line in php.ini for track_vars and set it to true.

For example, when the following form is submitted to a PHP script:

<form action=script.php method=post>
<input type=hidden name=firstname value="Edward">
<input type=hidden name=occupation value="Computer Specialist">
</form>

PHP automatically creates an array that can be accessed associatively:

$PHP_POST_VARS['name'] = "Edward";
$PHP_POST_VARS['occupation'] = "Computer Specialist";

This functionality makes it easier to create and manage form functions, as you don't have to either globalize each variable or list each variable when you call the form handler function.

If you had a form that called for the input of 15 separate fields and did not use $HTTP_POST_VARS or $HTTP_GET_VARS, then your function definition might look something like:

function my_form_handler($var1, $var2, $var3, $var4,
..., $var19, $var 20){

or:

function my_form_handler(){
global $var1, $var2, $var3, $var4, ..., $var19, $var 20;

Using $HTTP_POST_VARS or $HTTP_GET_VARS, your code is more manageable:

function my_form_handler($HTTP_POST_VARS) {

or:

function my_form_hander() {
global $HTTP_POST_VARS;

This also makes it easier to add (or subtract) additional fields to your forms without having to search through your code for the function calls that handle the form and the additional variables to the function call line.

An example:

Script 4-3 form2.php
1. <?
2. function print_form() {
3. ?>
4. <form action="form2.php" method=post>
5. <p>First Name: <input type="text" name="first">
6. <br>Last Name: <input type="text" name="last">
7. <br>Number: <input type="text" name="number">
8. <br><input type="submit" name="submit" value="Submit">
9. </form>
10. <?
11. }
12.
13. function form_handler($first, $last, $number) {
14. $sql = "insert into phonelist values ('$first', '$last', '$number')";
15. echo "<P>$sql";
16. }
17.
18. function form_handler2($HTTP_POST_VARS) {
19. $sql = "insert into phonelist values('" . $HTTP_POST_VARS['first'] .
20. "','" . $HTTP_POST_VARS['last'] .
21. "','" . $HTTP_POST_VARS['number'] . "')";
22. echo "<P>$sql";
23. }
24.
25. if(isset($submit)) {
26. form_handler($first, $last, $number);
27. form_handler2($HTTP_POST_VARS);
28. } else {
29. print_form();
30. }
31. ?>

Script 4-3. form2.php Line-by-Line Explanation LINE
DESCRIPTION

2–11
Declare a function, print_form(). This function simply prints a form so that the user can enter a first name, a last name, and a phone number.

13
Declare a function, form_hander(), that requires three arguments, the items returned when the user submits the form from the print_form() function.

14
Generate the SQL using the values provided by the user.

15
Print out the generated SQL.

16
End the function.

18
Declare a function, form_handler2(), that requires only one argument, HTTP_POST_VARS.

19–21
Generate the SQL using the values provided by the user.

22
Print out the generated SQL.

23
End the function.

25
If the user has pressed the "Submit" button, then execute the script until line 27.

26
Call the form_handler() function with the three required variables.

27
Call the form_handler2() function with just $HTTP_POST_VARS.

28–30
If the "Submit" button has not been pushed, then execute the print_form() function.

Putting It All Together
Now that we have the building blocks to build smart, friendly, and easy form processing, let's take a look at a real-world example. This example, shown in Figure 4-1, provides a way for a user to register for a typical community site.

Figure 4-1. register.php

Some of the fields are required and some are not. The script deems fields to be required if the field name ends with "_required".

The script also checks to make sure that a valid email address is entered and that the user enters two identical passwords.

Script 4-4 register.php
[View full width]
1. <html>
2. <head>
3. <title>Register</title>
4. <style type="text/css">
5. .error {color:red; font-weight: bold;}
6. </style>
7. </head>
8. <body>
9. <?
10. $page = "register.php";
11.
12. function print_form() {
13. global $page, $error, $print_again, $HTTP_POST_VARS;
14. $fields = array("first" => "text",
15. "last" => "text",
16. "age" => "text",
17. "email_required" => "text",
18. "login_required" => "text",
19. "password1_required" => "password",
20. "password2_required" => "password");
21. $labels = array("first" => "First Name",
22. "last" => "Last Name",
23. "age" => "Age",
24. "email_required" => "Email",
25. "login_required" => "Desired Username",
26. "password1_required" => "Password",
27. "password2_required" => "Confirm Password");
28. ?>
29. <form action="<? echo $page ?>" method="post">
30. <?
31. if($print_again) {
32. ?><h3>You missed or incorrectly entered some fields. Please correct the <span
class=error>red</span> fields. Passwords must match.<?
33. } else {
34. ?><h3>Please fill-in the following fields.</h3><?
35. }
36. ?>
37. <table border="0">
38. <?
39. foreach($fields as $key => $value) {
40. ?>
41. <tr><td <? error_flag($error, $key); ?><?=$labels[$key]?>: </td>
42. <td><input type="<?=$value?>" name="<?=$key?>" value="<? @print(
$HTTP_POST_VARS[$key])?>"></td></tr>
43. <?
44. }
45. ?>
46. <tr><td colspan="2"><input type="submit" name="submit" value="Submit"></td></tr>
47. </table>
48. </form>
49. <?
50. } // end function print_form
51.
52. function error_flag($error, $field) {
53. if($error[$field]) {
54. print("<td>");
55. } else {
56. print("<td>");
57. }
58. } //end function error_flag
59.
60. function check_form() {
61. global $error, $print_again, $HTTP_POST_VARS;
62. $print_again = false;
63. //Check Required Fields Have Been Entered
64. foreach($HTTP_POST_VARS as $key => $value) {
65. if(($value == "") && eregi("_required$", $key)){
66. $error[$key] = true;
67. $print_again = true;
68. } else {
69. $error[$key] = false;
70. }
71. }
72. //Verify Email
73. if(!eregi("^[a-z0-9]+[a-z0-9_-]*(\.[a-z0-9_-]+)*@[a-z0-9_-]+(\.[a-z0-9_-]+)*\.(
[a-z]+){2,}$", $HTTP_POST_VARS['email_required'])) {
74. $error['email_required'] = true;
75. $print_again = true;
76. $HTTP_POST_VARS['email_required'] = "ENTER A VALID EMAIL";
77. }
78. //Verify Desired User Name Is Available
79. $available = true;
80. if(!$available) {
81. $error['login_required'] = true;
82. $print_again = true;
83. $HTTP_POST_VARS['login_required'] = "Name Not Available: " .
$HTTP_POST_VARS['login_required'];
84. }
85. //Verify Passwords Match
86. if($HTTP_POST_VARS['password1_required'] != $HTTP_POST_VARS['password2_required']) {
87. $error['password1_required'] = true;
88. $error['password2_required'] = true;
89. $HTTP_POST_VARS['password1_required'] = NULL;
90. $HTTP_POST_VARS['password2_required'] = NULL;
91. $print_again = true;
92. }
93. //Print Again If Errors Are Found
94. if($print_again) {
95. print_form();
96. } else {
97. print("<h3>Thank you for completing the form!</h3>");
98. // Do database insert, email, etc. Since Data Is OK
99. }
100. } // end function check_form
101.
102. /***** MAIN *****/
103. if(isset($submit)) {
104. check_form();
105. } else {
106. print_form();
107. }
108. ?>
109. </body>
110. </html>

Script 4-4. register.php Line-by-Line Explanation LINE
DESCRIPTION

1–8
Create the beginning of the HTML page.

10
Create a $page variable for use in the function to print the forms.

12
Create a print_form() function used to print the form to the screen.

14–20
Create an array to be used as input for the form-field names and types.

21–27
Create an array to be used as input for the form-field labels.

29
Start printing the form to the browser.

31
Check to see if the $print_again variable is set. If it is, then the user made a mistake entering information on his or her previous try filling in the form.

32
Print a message to the user notifying him or her that he or she made an error when filling out the form.

33
If there were no previous errors, then print out a simple message asking the user to fill out the form.

37
Begin printing the table for the form fields.

39
Loop though the $fields array and create the form. Notice how the keys for the $fields array are the same as the keys for the $labels array.

41
If there was an error, then highlight the label using the error_flag() function. Print the label for the field.

42
Print the field name and type (from the $fields array). If there was an existing value that the user entered, then print it out. The "@" sign suppresses any warnings that occur if the user is filling out the form for the first time or failed to enter a value previously. If you omit the "@" sign, then a warning is printed because the $HTTP_POST_VARS[$key] variable has not been declared.

46
Print the submit button for the form.

50
End the function.

52
Declare the error_flag function. The error_flag() function takes two arguments:

$error— A boolean value of 0 or 1. 1 means an error occurred with this field. Either it was not filled in or the user's entry was invalid.

$field— The key of the field in which the error occurred.

53
If the $error is 1 (true), then apply some formatting to the field label using CSS.

56
If there was no error, then just print a normal <td> tag.

58
End the function.

60
Declare the function check_form().

61
Allow the check_form() function to use and modify the variables listed.

64–71
These lines check to see if any fields ending with "_required" are not filled in. If they are not, then an error is generated for each field that is not filled in. Additionally, the $print_again variable is set to true.

73–77
These lines verify that the email address is the proper format. If it is not, then an error is generated for the email field and an error message is inserted into the form the next time it is printed.

79–84
These lines pseudo-check if a login name is available. You can change the setting on line 79 to false to see what happens when the name is not available. Normally, you would query your database or text-file list of users to verify whether a name was available or not. If the name is not available, then an error is generated for the login_required field.

86–92
These lines check if the password fields match. If they do not, then an error is generated and the existing passwords are set to NULL so that they are not reinserted into the form when it is printed again.

94
If the $print_again variable is set, then print the form again.

97
If the $print_again variable is not set, then all went well and you can insert the user registration into a database, or email the contents to an email address.

102
Begin the main program. If the submit button is pressed, then go to line 104. Otherwise, go to line 106.

104
Run check_form(), since we know the user has pressed the submit button.

106
The user has not yet filled out the form because the Submit button hasn't been pushed. Print the form using print_form().

Variable Variables
One thing that I've found incredibly useful in some of my form-based applications is variable variables. Variable variables allow you to have variable names for variables. This way you can generate variable names on the fly for whatever purposes your script requires.

Example:

$name = "integer";
$$name = 10;
//results in $integer = 10;

Note that when you echo these values to the screen, you get different results from those which you may expect. In the above example, if you want to print the value out to the screen, you cannot enclose the variable variable name in quotes as you might normally:

echo "<p>$$name";
// results in "$integer" being printed to the screen
echo "<p>" . $$name;
//results in "10" being printed to the screen

Variable variable names can also be created using expressions such as the following:

$name = "integer";
${$name . "_1"} = 10;
// results in $integer_1 = 10;

When coupled with a for loop, this helps create dynamic forms that can be easily changed to include more or fewer fields.

The example below, shown in Figure 4-2, allows you to upload files to a server. You just need to specify how many files you would like to upload, and the script automatically creates the necessary variables.

Script 4-5 upload.php
[View full width]
1. <?
2. $page = "upload.php";
3. ?>
4. <html>
5. <head>
6. <title>Upload Files</title>
7. </head>
8. <body>
9. <?
10.
11. function specify_number() {
12. global $page;
13. ?>
14. <form action=<?=$page?> method=post>
15. Enter number of fields to display for upload and press Enter: <input type="text"
name="fields" size="2" maxlength="2">
16. </form>
17. <?
18. }
19.
20. function print_form($fields) {
21. global $page
22. ?>
23. <form action=<?=$page; ?> method=post enctype="multipart/form-data">
24. <table border=1>
25. <?
26. for($i = 0; $i < $fields; $i++) {
27. ?>
28. <tr><td>File:</td>
29. <td>
30. <input type="hidden" name="MAX_FILE_SIZE" value="1024000">
31. <input type="file" name="file_<?=$i?>">
32. </td></tr>
33. <?
34. }
35. ?>
36. </table>
37. <input type="hidden" name="fields" value="<?=$fields?>">
38. <input type="submit" name="submit" value="Upload!">
39. </form>
40. <?
41. } // end print_form
42.
43. $uploaddir = "/tmp/"; //make sure you include the trailing slash.
44. if(isset($submit)) {
45. for($i = 0; $i < $fields; $i++) {
46. if(is_uploaded_file(${"file_".$i})) {
47. $newfile = $uploaddir . $_FILES['file_' . $i]['name'];
48. if(!@copy(${"file_".$i}, $newfile)) {
49. echo "<p>Error Uploading <i>" . $_FILES['file_' . $i]['name'] . "</i>.";
50. } else {
51. echo "<p>Upload OK for " . $_FILES['file_' . $i]['name'];
52. }
53. }
54. flush();
55. }
56. } elseif(isset($fields)) {
57. print_form($fields);
58. } else {
59. specify_number();
60. }
61. ?>
62. </body>
63. </html>

Figure 4-2. upload.php

Script 4-5. upload.php Line-by-Line Explanation LINE
DESCRIPTION

2
Define the name of the page so that it can be used in functions that submit forms.

4–8
Print out the beginning HTML for the page.

11
Declare the specify_number() function. This function simply allows the users to enter the number of files that they wish to upload.

12
Allow this function to access the global $page variable.

14–16
Print the form that asks the users how many files they want to upload.

20
Declare the print_form() function. This function takes one argument, $fields, which is the number of fields the user entered into the specify_number function's form.

23
Create the form.

24
Create a table to display the form fields.

26–32
Create a form field for a file upload. Do this as many times as the user entered for the number of fields. Note how the field is named on line 31.

36–39
Close the table and the form. Add a hidden variable for the number of fields that were requested.

41
End the function.

43
Specify an upload directory for the files.

44
If the submit button has been pressed, then execute through line 56.

45
Create a for loop that goes through the same number of iterations as specified by the user for files to be uploaded.

46
Verify that a file has been uploaded by the user. Note the variable variable.

47
Create a variable that contains the path and filename for the uploaded file.

48
Attempt to copy the file from PHP's temporary file space to the location you have specified in the $newfile variable.

49
Notify the user if there is an error.

51
Notify the user if the upload was successful.

54
Flush the notification to the user.

56
If the submit button has not been pressed but the $fields variable has been set, then we know the user just entered how many files uploaded.

57
Execute the print_form() function, since the above statement is true.

58
If the submit button has not been pressed and $fields is not set…

59
Execute the specify_number() function.

60
End the if/else statement started on line 44.

62–63
Close out the HTML for the page.

0 comments: