Tuesday, July 14, 2009

Beginning PHP and Oracle From Novice to Professional by W. Jason Gilmore and Bob Bryla Chapter 13

You can throw about technical terms such as relational database, Web Services, session handling,
and LDAP, but when it comes down to it, you started learning PHP because you wanted to build cool,
interactive Web sites. After all, one of the Web’s most alluring aspects is that it is two-way media; the Web not only enables you to publish information but also offers an effective means for interaction with peers, clients, and friends. This chapter introduces one of the most common ways in which you can use PHP to interact with the user: Web forms.
The majority of the material in this chapter should be relatively simple to understand, yet it is crucial for anybody who is interested in building even basic Web sites. In total, we talk about the following topics:

• Understanding basic PHP and Web form concepts

• Passing form data to PHP functions

• Working with multivalued form components

• Taking advantage of PEAR's HTML_QuickForm package

• Creating a forms auto-completion mechanism

PHP and Web Forms

What makes the Web so interesting and useful is its ability to disseminate information as well as collect it, primarily through HTML-based forms. These forms are used to encourage site feedback, facilitate forum conversations, collect mailing addresses for online orders, and much, much more. But coding the HTML form is only part of what’s required to effectively accept user input; a server- side component must be ready to process the input. Using PHP for this purpose is the subject of this section.
Because you’ve used forms hundreds if not thousands of times, this chapter won’t introduce
form syntax. If you require a primer or a refresher course on how to create basic forms, consider reviewing any of the many tutorials available on the Web. Two particularly useful sites that offer forms-specific tutorials follow:

• W3 Schools: http://www.w3schools.com/

• TopXML: http://www.topxml.com/

We will review how you can use Web forms in conjunction with PHP to gather and process valu- able user data.
There are two common methods for passing data from one script to another: GET and POST. Although GET is the default, you’ll typically want to use POST because it’s capable of handling

249

considerably more data, an important behavior when you’re using forms to insert and modify large blocks of text. If you use POST, any posted data sent to a PHP script must be referenced using the
$_POST syntax, introduced in Chapter 3. For example, suppose the form contains a text-field value named email that looks like this:

<input type="text" id="email" name="email" size="20" maxlength="40" />

Once this form is submitted, you can reference that text-field value like so:

$_POST['email']

Of course, for the sake of convenience, nothing prevents you from first assigning this value to another variable, like so:

$email = $_POST['email'];

But following the best practice of never presuming user input will be safe, you should filter it through one of the several functions capable of sanitizing data such as htmlentities(), like so:

$email = htmlentities($_POST['email']);

The htmlentities() function converts strings consisting of characters capable of maliciously modifying an HTML page if the user-submitted data is later published to a Web site, such as a Web forum. You can learn more about filtering user input for safe publication and storage in Chapter 21.
Keep in mind that, other than the odd format, $_POST variables are just like any other variable. They’re simply referenced in this fashion in an effort to definitively compartmentalize an external variable’s origination. As you learned in Chapter 3, such a convention is available for variables orig- inating from the GET method, cookies, sessions, the server, and uploaded files. For those of you with an object-oriented background, think of it as namespaces for variables.
This section introduces numerous scenarios in which PHP can play a highly effective role not only in managing form data but also in actually creating the form itself. For starters, though, let’s take a look at a simple example.

A Simple Example

The following script renders a form that prompts the user for his name and e-mail address. Once completed and submitted, the script (named subscribe.php) displays this information back to the browser window:

<?php
// If the submit button has been pressed if (isset($_POST['submit']))
{
$name = htmlentities($_POST['name']);
$email = htmlentities($_POST['email']);
printf("Hi %s! <br />", $name);
printf("The address %s will soon be a spam-magnet! <br />", $email);
}
?>

<form action="subscribe.php" method="post">
<p>

</p>

Name:<br />
<input type="text" id="name" name="name" size="20" maxlength="40" />

<p>

E-mail address:<br />
<input type="text" id="email" name="email" size="20" maxlength="40" />

</p>
<input type="submit" id="submit" name = "submit" value="Go!" />
</form>
Assuming that the user completes both fields and clicks the Go! button, output similar to the following will be displayed:

Hi Bill!
The address bill@example.com will soon be a spam-magnet!

In this example the form refers to the script in which it is found, rather than another script. Although both practices are regularly employed, it’s quite commonplace to refer to the originating document and use conditional logic to determine which actions should be performed. In this case, the conditional logic dictates that the echo statements will only occur if the user has submitted (posted) the form.
In cases where you’re posting data back to the same script from which it originated, as in the preceding example, you can use the PHP superglobal variable $_SERVER['PHP_SELF']. The name of the executing script is automatically assigned to this variable; therefore, using it in place of the actual file name will save some additional code modification should the file name later change. For example, the <form> tag in the preceding example could be modified as follows and still produce the same outcome:

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">

Passing Form Data to a Function

The process for passing form data to a function is identical to the process for passing any other variable; you simply pass the posted form data as function parameters. Suppose you want to incorporate some server-side validation into the previous example, using a custom function to verify the e-mail address’s syntactical validity. Listing 13-1 presents the revised script.

Listing 13-1. Validating Form Data in a Function (subscribe.php)

<?php

// Function used to check e-mail syntax function validateEmail($email)
{
// Create the e-mail validation regular expression
$regexp = "^([_a-z0-9-]+)(\.[_a-z0-9-]+)*@([a-z0-9-]+)➥
(\.[a-z0-9-]+)*(\.[a-z]{2,6})$";

// Validate the syntax
if (eregi($regexp, $email)) return 1;
else return 0;
}

// Has the form been submitted?
if (isset($_POST['submit']))
{

$name = htmlentities($_POST['name']);
$email = htmlentities($_POST['email']);

printf("Hi %s<br />", $name);

if (validateEmail($email))
printf("The address %s is valid!", $email);
else
printf("The address <strong>%s</strong> is invalid!", $email);
}
?>

<form action="subscribe.php" method="post">
<p>
Name:<br />
<input type="text" id="name" name="name" size="20" maxlength="40" />
</p>

<p>

E-mail address:<br />
<input type="text" id="email" name="email" size="20" maxlength="40" />

</p>

<input type="submit" id="submit" name = "submit" value="Go!" />
</form>

Working with Multivalued Form Components

Multivalued form components such as checkboxes and multiple-select boxes greatly enhance your Web-based data-collection capabilities because they enable the user to simultaneously select multiple values for a given form item. For example, consider a form used to gauge a user’s computer-related interests. Specifically, you would like to ask the user to indicate those programming languages that interest her. Using a multiple-select box, this form item might look similar to that shown in Figure 13-1.

Figure 13-1. Selecting multiple values for a given form item

The HTML code for rendering the checkboxes looks like this:

<input type="checkbox" name="languages[]" value="csharp" />C#<br />
<input type="checkbox" name="languages[]" value="jscript" />JavaScript<br />
<input type="checkbox" name="languages[]" value="perl" />Perl<br />
<input type="checkbox" name="languages[]" value="php" />PHP<br />

The HTML for the multiple-select box might look like this:

<select name="languages[]" multiple="multiple">
<option value="csharp">C#</option>
<option value="jscript">JavaScript</option>
<option value="perl">Perl</option>
<option value="php">PHP</option>
</select>

Because these components are multivalued, the form processor must be able to recognize that there may be several values assigned to a single form variable. In the preceding examples, note that both use the name languages to reference several language entries. How does PHP handle the matter? Perhaps not surprisingly by considering it an array. To make PHP recognize that several values may be assigned to a single form variable, you need to make a minor change to the form item name, appending a pair of square brackets to it. Therefore, instead of languages, the name would read languages[]. Once renamed, PHP will treat the posted variable just like any other array. Consider a complete example in the script multiplevaluesexample.php:

<?php
if (isset($_POST['submit']))
{
echo "You like the following languages:<br />";
foreach($_POST['languages'] AS $language) {
$language = htmlentities($language);
echo "$language<br />";
}
}
?>

<form action="multiplevaluesexample.php" method="post">
What's your favorite programming language?<br /> (check all that apply):<br />
<input type="checkbox" name="languages[]" value="csharp" />C#<br />
<input type="checkbox" name="languages[]" value="jscript" />JavaScript<br />
<input type="checkbox" name="languages[]" value="perl" />Perl<br />
<input type="checkbox" name="languages[]" value="php" />PHP<br />
<input type="submit" name="submit" value="Go!" />
</form>

If the user was to choose the languages C# and PHP, he would be greeted with the following output:

You like the following languages:
csharp php

Taking Advantage of PEAR: HTML_QuickForm

While the previous examples show it’s fairly easy to manually code and process forms using plain old HTML and PHP, matters can quickly become complicated and error-prone when validation and more sophisticated processing enter the picture, as is likely for any ambitious application. Thank- fully this is a challenge faced by all Web developers, so therefore quite a bit of work has been put into automating the forms creation, validation, and handling process. A solution comes by way of the impressive HTML_QuickForm package, available through the PEAR repository.

HTML_QuickForm is much more than a simple forms-generation class; it offers more than 20 XHTML- compliant form elements, client-side and server-side validation, the ability to integrate with templating engines such as Smarty (see Chapter 19 for more about Smarty), an extensible model allowing you to create your own custom elements, and much more. This section introduces this great package, demonstrating some of its most useful features.

Installing HTML_QuickForm

To take advantage of HTML_QuickForm’s features, you’ll need to install it from PEAR. Because it depends on HTML_Common, another PEAR package capable of displaying and manipulating HTML code, you’ll need to install it also, which is done automatically by passing the --onlyreqdeps flag to the install command:

%>pear install --onlyreqdeps HTML_QuickForm downloading HTML_QuickForm-3.2.7.tgz ...
Starting to download HTML_QuickForm-3.2.7.tgz (102,475 bytes)
........................done: 102,475 bytes downloading HTML_Common-1.2.3.tgz ...
Starting to download HTML_Common-1.2.3.tgz (4,746 bytes)
...done: 4,746 bytes
install ok: channel://pear.php.net/HTML_Common-1.2.3 install ok: channel://pear.php.net/HTML_QuickForm-3.2.7

Creating a Simple Form

Creating a form is a breeze using HTML_QuickForm; just instantiate the HTML_QuickForm class and call the addElement() method as necessary, passing in the element types and attributes to create each form component. Finally, call the display() method to render the form. Listing 13-2 creates the form displayed in Figure 13-1.

Listing 13-2. Creating a Form with HTML_QuickForm

<?php

require_once "HTML/QuickForm.php";

// Create array of languages to be used in multiple select box
$languages = array(
'C#' => 'C#',
'JavaScript' => 'JavaScript',
'Perl' => 'Perl',
'PHP' => 'PHP'
);

// Instantiate the HTML_QuickForm class
$form = new HTML_QuickForm("languages");

// Add text input element for entering user name
$form->addElement('text', 'username', 'Your name: ', array('size' => 20, 'maxlength' => 40));

// Add text input element for entering e-mail address
$form->addElement('text', 'email', 'E-mail address: ', array('size' => 20, 'maxlength' => 50));

// Add select box element for choosing favorite programming languages
$select =& $form->addElement('select', 'languages',
'Your favorite<br />programming languages: ', $languages);

// Assign the multiple attribute to select box
$select->setMultiple(1);

// Add submit button
$form->addElement('submit', null, 'Submit!');

// Display the form
$form->display();

?>

But creating and displaying the form is only half of the battle; you must always validate and then
process the submitted data. These tasks are discussed next.

Validating Form Input

As mentioned earlier in this chapter and elaborated further upon in Chapter 21, you should never blindly accept user input. The cost of ignoring this advice could be the integrity of your data, the destruction of your Web site, the loss of confidential user information, or any number of other undesired outcomes.
But data validation is a tiresome and error-prone process, one in which incorrect validation code can result in a dire situation, and one in which the developer must be abundantly aware of the charac- teristics of the data he’s trying to validate. For instance, suppose you want to validate the syntax of an e-mail address according to the specification as set forth in RFC 2822 (http://www.faqs.org/rfcs/ rfc2822). But in creating the rather complex regular expression required to properly validate an e-mail address, you limit the domain extension to four characters, considering yourself particularly Internet savvy for remembering the more recently available .mobi and .name top-level domains. However, you neglect to factor in the even more recently available .museum and .travel domains, thereby preventing anybody using those addresses from registering on your Web site.
Or take the simple example of ensuring the user enters what you perceive to be a valid first name.
Surely names should only consist of alphabetical characters and won’t consist of less than three and no more than ten letters, right? But what about people who go by initials, such as R.J., or come from countries where particularly long names are common, such as the Indian name Swaminathan?
Thankfully, as this section shows, HTML_QuickForm can remove much of the difficulty involved in data validation. However, even this great package is unable to foresee what sort of special constraints your user-supplied data will have; so take extra special care to think about such matters before putting HTML_QuickForm’s validation facilities to work.

Using Filters

HTML_QuickForm provides a means for passing data through a filter, which can perform any sort of analysis you please. These filters are actually functions, and you can use any of PHP’s built-in func- tions, or you can create your own. For instance, suppose you are creating a corporate intranet that requires employees to log in using their employee identification number, which consists of integers and capital letters. For security purposes you log each employee login, and for reasons of consistency

you want the employee identification numbers to be logged using the proper uppercase format. To do so, you could install the filter like so:

$form->applyFilter('employeeid', 'strtoupper');

■Note When using filters, the user will not be notified of any modifications made to the submitted data. The filter
will simply execute once the form is submitted and perform any actions should the filter meet the criteria as defined by the function. Therefore, you shouldn’t use filters to modify data that the reader will later depend upon without explicitly telling him as much, such as changing the casing of a username or a password.

Using Rules

While filters offer an implicit method for tidying up user data before processing continues, sometimes you want to expressly restrict the user from inserting certain forms of data, preventing the form from being processed until certain constraints are met. For instance, when asking the user for his name, you’ll want to prevent numerical characters from being passed in. Therefore, while Jason Gilmore and Bob Bryla are valid names, JasonGilmore1 and B0b Bryla are not. But you can’t just filter out the digits, because you just can’t be sure of what the user intended to type. Therefore, the mistake must be flagged and the user notified of the problem. This is where rules come in.
Rules can be instituted to impose strict restrictions on the contents of a string, and HTML_QuickForm comes packaged with several of the more commonplace rules ready for use. Table 13-1 summarizes what’s at your disposal. If none meet your needs, you can instead use a callback (also listed in Table 13-1) to create your own function.

Table 13-1. Common Predefined Validation Rules

Rule Description Specification

alphanumeric Value can only contain letters and numbers N/A

callback Value must pass through user-defined function Name of function
compare Value is compared with another field value eq, neq, gt, gte, lt, lte email Value must be a valid e-mail address Boolean (whether to
perform domain verifica- tion with checkdnsrr())

lettersonly Value must contain only letters N/A maxlength Value cannot exceed N characters Integer value minlength Value must equal or exceed N characters Integer value nopunctuation Value cannot contain punctuation N/A
nonzero Value cannot begin with zero N/A

numeric Value must be a number N/A

rangelength Value must be between the minimum and maximum characters

array(min,max)

regex Value must correctly pass regular expression Regular expression

required Value required N/A

To define a rule, for instance, requiring the user to enter his ZIP code, you would use this:

$form->addRule('zipcode', 'Please enter a zipcode', 'required', null, 'client');

All of the input parameters should be self-explanatory, save for the concluding null and client designations. Because the required rule doesn’t require any further details, the null value comes next. However, if this was a minlength rule, the minimum length would be specified here. The client value specifies that validation will occur on the client side. If the browser lacks sufficient JavaScript
capabilities, not to worry, server-side validation is also always performed.

■Note HTML_QuickForm also supports file uploading and rules for validating these files. However, due to the
extensive coverage devoted to file uploads in Chapter 15, with special attention given to the HTTP_Upload PEAR
package, this particular feature of HTML_QuickForm is not covered in this chapter.

Enforcing Filters and Rules

Because filters are nonintrusive constraints, meaning they’ll execute without requiring user notifica- tion, they simply happen when the form is processed. Rules on the other hand won’t be enforced without executing the validate() method. If validate() executes okay, all of the rules were satisfied, otherwise the appropriate error messages are displayed.
The following example demonstrates the use of the required rule, enforcing client-side valida- tion by displaying an error message using a JavaScript alert window (HTML_QuickForm’s default behavior), or displaying a welcome message, should the rule pass muster:
<?php

require_once "HTML/QuickForm.php";

// Instantiate the HTML_QuickForm class
$form = new HTML_QuickForm("login");

// Add text input element for entering username
$form->addElement('text', 'username', 'Your name: ', array('size' => 20, 'maxlength' => 40));

// Add text input element for entering e-mail address
$form->addElement('text', 'email', 'E-mail address: ', array('size' => 20, 'maxlength' => 50));

// Add a rule requiring the username
$form->addRule('username', 'Please provide your username.',
'required', null, 'client');

// Add submit button
$form->addElement('submit', null, 'Submit!');

if ($form->validate()) {

echo "Welcome to the restricted site, ". htmlspecialchars($form->exportValue('username')). ".";

}

// Display the form
$form->display();

?>

■Caution HTML_QuickForm harbors an odd side effect. For example, validate() will process correctly in
instances where the minlength or maxlength rules are added but the user neglects to enter any data into the field. In order to ensure these rules process correctly, you must also add a required rule.

Processing Form Values

Once the form is submitted, you’ll want an easy means to retrieve the form values. Three methods are available: getSubmitValues(), process(), and exportvalue().
The getSubmitValues() method returns the submitted values by way of an array, as in this example:

if ($form->validate()) {
print_r($form->getSubmitValues());
}

This produces output similar to the following:

Array ( [username] => jason [email] => wj@example.com )

The process() method passes values to a function. For instance, suppose you create a function for communicating with Amazon’s Web services named retrieveBook(). The user data could be passed to it like so:
if ($form->validate()) {
$form->process('retrieveBook');
}

Finally, the exportvalue() function will selectively retrieve each value by specifying its field
name. For instance, suppose you want to retrieve the username value defined by a username form field:

if ($form->validate()) {
$username = $form->exportvalue('username');
}

Using Auto-Completion

HTML_QuickForm comes with an amazing array of features, and the surface has hardly been scratched in this chapter. Beyond the forms creation, validation, and processing features, HTML_QuickForm offers a number of advanced capabilities intended to further enhance your Web site’s forms features. One such feature is auto-completion.
Sometimes it’s useful to provide the user with free-form text input rather than a drop-down box
containing predefined values in case his answer is not one of the available choices. However, because there’s a significant likelihood the user is going to specify one of a set number of values, you want to facilitate his input using auto-completion. Auto-completion works by monitoring what the user begins to type into the input box and suggesting a value based on what’s been entered so far.

For instance, suppose you’re building a fantasy football Web site and want to collect informa- tion about each user’s favorite football team. While one could presume most will choose an NFL
or collegiate team, some of the younger players might opt to enter their favorite high school team. While it’s fairly trivial to compile a list of NFL and at least the well-known collegiate teams, creating a similar list of the thousands of high school teams around the country would be difficult at best. Therefore, you use a text input box with auto-completion enabled. Should the user begin entering Steel, the auto-complete mechanism will offer up the first matching array element, which is Steelers, as shown in Figure 13-2.

Figure 13-2. Using auto-completion

However, if the user continues typing, changing the string to Steel (with a concluding space), auto-completion will present Steel Curtains, as shown in Figure 13-3.

Figure 13-3. Auto-completion adapting to alternative choices

The code used to implement this feature follows:

<?php
require 'HTML/QuickForm.php';

// Create the array used for auto-completion
$teams = array('Steelers', 'Seahawks', 'Steel Curtains');

// Instantiate the HTML_QuickForm class
$form = new HTML_QuickForm();

// Create the autocomplete element
$element =& $form->addElement('autocomplete', 'teams',
'Favorite Football Team:');

// Map the array to the autocomplete field
$element->setOptions($teams);

// Display the form
$form->display();
?>

Summary

One of the Web’s great strengths is the ease with which it enables us to not only disseminate but also compile and aggregate user information. However, as developers this mean that we must spend an enormous amount of time building and maintaining a multitude of user interfaces, many of which are complex HTML forms. The concepts described in this chapter should enable you to decrease that time a tad.

In addition, this chapter offered a few commonplace strategies for improving your application’s general user experience. Although not an exhaustive list, perhaps the material presented in this chapter will act as a springboard for you to conduct further experimentation as well as help you decrease the time that you invest in what is surely one of the more time-consuming aspects of Web development: improving the user experience.
The next chapter shows you how to protect the sensitive areas of your Web site by forcing users to supply a username and password prior to entry.

0 comments: