Chapter 5: Advanced Forms and Perl Control Structures
In the last chapter you learned how to decode form data, and mail it to
yourself. However, one problem with the guestbook program is that it didn't do
any error-checking or specialized processing.
A D V E R T I S E M E N T
You might not want to get blank
forms, or you may want to require certain fields to be filled out. You might
also want to write a quiz or questionnaire, and have your program take different
actions depending on the answers. All of these things require some more advanced
processing of the form data, and that will usually involve using control
structures in your Perl code.
Control structures include conditional statements, such as if/elsif/else
blocks, as well as loops like foreach , for and
while.
If Conditions
You've already seen if/elsif in action. The structure is always started by
the word if , followed by a condition to be evaluated, then a pair
of braces indicating the beginning and end of the code to be executed if the
condition is true. The condition is enclosed in parentheses:
The condition statement can be anything that evaluates to true or false. In
Perl, any string is true except the empty string and 0. Any number is true
except 0. An undefined value (or undef ) is false.You can also test
whether a certain value equals something, or doesn't equal something, or is
greater than or less than something. There are different conditional test
operators, depending on whether the variable you want to test is a string or a
number:
Relational and Equality Operators
Test |
Numbers |
Strings |
$x is equal to $y |
$x == $y |
$x eq $y |
$x is not equal to $y |
$x != $y |
$x ne $y |
$x is greater than $y |
$x > $y |
$x gt $y |
$x is greater than or equal to $y |
$x >= $y |
$x ge $y |
$x is less than $y |
$x < $y |
$x lt $y |
$x is less than or equal to $y |
$x <= $y |
$x le $y |
If it's a string test, you use the letter operators (eq, ne, lt, etc.), and
if it's a numeric test, you use the symbols (==, !=, etc.). Also, if you are
doing numeric tests, keep in mind that $x >= $y is not the same as $x => $y. Be
sure to use the correct operator!
Here is an example of a numeric test. If $varname is greater than 23, the
code inside the curly braces is executed:
If you need to have more than one condition, you can add elsif and else
blocks:
The line breaks are not required; this example is just as valid:
You can join conditions together by using logical operators:
Logical Operators
Operator |
Example |
Explanation |
&& |
condition1 && condition2 |
True if condition1 and condition2 are both true |
|| |
condition1 || condition2 |
True if either condition1 or condition2 is true |
and |
condition1 and condition2 |
Same as && but lower precedence |
or |
condition1 or condition2 |
Same as || but lower precedence |
Logical operators are evaluated from left to right. Precedence
indicates which operator is evaluated first, in the event that more than one
operator appears on one line. In a case like this:
condition2 && condition3 is evaluated first, then the result of
that evaluation is used in the || evaluation.
and and or work the same way as && and
|| , although they have lower precedence than their symbolic
counterparts.
Unless
unless is similar to if . Let's say you wanted to
execute code only if a certain condition were false. You could do something like
this:
The same test can be done using unless :
There is no "elseunless", but you can use an else clause:
Validating Form Data
You should always validate data submitted on a form; that is, check to
see that the form fields aren't blank, and that the data submitted is in the
format you expected. This is typically done with if/elsif blocks.
Here are some examples. This condition checks to see if the "name" field
isn't blank:
You can also test multiple fields at the same time:
The above code will return an error if either the name or email fields are
left blank.
param('fieldname') always returns one of the following:
undef � or undefined |
fieldname is not defined in the form itself, or it's a
checkbox/radio button field that wasn't checked. |
the empty string |
fieldname exists in the form but the user didn't type anything
into that field (for text fields)
|
one or more values |
whatever the user typed into the field(s) |
If your form has more than one field containing the same fieldname, then the
values are stored sequentially in an array, accessed by param('fieldname') .
You should always validate all form data � even fields that are submitted as
hidden fields in your form. Don't assume that your form is always the one
calling your program. Any external site can send data to your CGI. Never trust
form input data.
Looping
Loops allow you to repeat code for as long as a condition is met. Perl has
several loop control structures: foreach , for ,
while and until.
Foreach Loops
foreach iterates through a list of values:
This loops through each element of @arrayname, setting $i to the current
array element for each pass through the loop. You may omit the loop variable $i:
This sets the special Perl variable $_ to each array element. $_ does not
need to be declared (it's part of the Perl language) and its scope localized to
the loop itself.
For Loops
Perl also supports C-style for loops:
The for statement uses a 3-part conditional: the loop
initializer; the loop condition (how long to run the loop); and the loop re-initializer
(what to do at the end of each iteration of the loop). In the above example, the
loop initializes with $i being set to 1. The loop will run for as long as $i is
less than 23, and at the end of each iteration $i is incremented by 1 using the
auto-increment operator (++).
The conditional expressions are optional. You can do infinite loops by
omitting all three conditions:
You can also write infinite loops with while.
While Loops
A while loop executes as long as particular condition is true:
Until Loops
until is the reverse of while. It executes as long
as a particular condition is NOT true:
Infinite Loops
An infinite loop is usually written like so:
Obviously unless you want your program to run forever, you'll need some way
to break out of these infinite loops. We'll look at breaking next.
Breaking from Loops
There are several ways to break from a loop. To stop the current loop
iteration (and move on to the next one), use the next command:
This example prints the numbers from 1 to 20, except for the number 13. When
it reaches 13, it skips to the next iteration of the loop.
To break out of a loop entirely, use the last command:
This example prints the numbers from 1 to 12, then terminates the loop when
it reaches 13.
next and last only effect the innermost loop
structure, so if you have something like this:
The last command only terminates the innermost loop. If you want
to break out of the outer loop, you need to use loop labels:
The loop label is a string that appears before the loop command (foreach,
for, or while). In this example we used OUTER as the label for the outer foreach
loop and INNER for the inner loop label.
Now that you've seen the various types of Perl control structures, let's look
at how to apply them to handling advanced form data.
Handling Checkboxes
Checkboxes allow the viewer to select one or more options on a form. If you
assign each checkbox field a different name, you can print them the same way
you'd print any form field using param('fieldname') .
Here is the HTML code for a set of checkboxes:
This example lets the visitor pick as many options as they want � or none, if
they prefer. Since this example uses a different field name for each checkbox,
you can test it using param:
my @colors = ("red","green","blue","gold");
foreach my $color (@colors) {
if (param($color)) {
print "You picked $color.\n";
}
}
Since we set the value of each checkbox to 1 (a true value), we didn't need
to actually see if param($color) was equal to anything � if the box
is checked, its true. If it's not checked, then param($color) is
undefined and therefore not true.
The other way you could code this form is to set each checkbox name to the
same name, and use a different value for each checkbox:
param('color') returns a list of the selected checkboxes, which
you can then store in an array. Here is how you'd use it in your CGI program:
Handling Radio Buttons
Radio buttons are similar to checkboxes in that you can have several buttons,
but the difference is that the viewer can only pick one choice. As with our last
checkbox example, the group of related radio buttons must all have the same
name, and different values:
Since the viewer can only choose one item from a set of radio buttons,
param('color') will be the color that was picked:
It's usually best to set the values of radio buttons to something meaningful;
this allows you to print out the button name and its value, without having to
store another list inside your CGI program. But if your buttons have lengthy
values, or values unsuitable for storing in the value field, you can set each
value to an abbreviation, then define a hash in your CGI program where the hash
keys correspond to the abbreviations. The hash values can then contain longer
data.
Let's try it. Create a new HTML form called colors4.html:
Program 5-1: colors4.html - Favorite Colors HTML Form
Next create colors4.cgi. This example not only prints out the color you
picked, but also sets the page background to that color. The %colors hash stores
the various RGB hex values for each color. The hex value for the selected color
is then passed to CGI.pm's start_html function as the bgcolor
(background color) parameter.
Program 5-2: colors4.cgi - Favorite Colors Program
#!/usr/bin/perl -wT
use strict;
use CGI qw(:standard);
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
my %colors = ( red => "#ff0000",
green => "#00ff00",
blue => "#0000ff",
gold => "#cccc00");
print header;
my $color = param('color');
# do some validation - be sure they picked a valid color
if (exists $colors{$color}) {
print start_html(-title=>"Results", -bgcolor=>$color);
print "You picked $color.<br>\n";
} else {
print start_html(-title=>"Results");
print "You didn't pick a color! (You picked '$color')";
}
print end_html;
Handling SELECT Fields
SELECT fields are handled almost the same way as radio buttons. A SELECT
field is a pull-down menu with one or more choices. Unless you specify a
multiple select (see below), the viewer can only choose one option. Here is the
HTML for creating a SELECT field:
As with radio buttons, you access the selection in your CGI program using
param('color') :
Multiple-choice SELECTs
Multiple SELECTs allow the viewer to choose more than one option from the
list, usually by option-clicking or control-clicking on the options they want.
Here is the HTML for a multiple SELECT:
In your CGI program, param('color') returns a list of the
selected values, just as it did when we had multiple checkboxes of the same
name:
So now you've seen every type of form element (except for file-uploads, which
we'll look at in Chapter 14), and in every case you've seen that CGI.pm's param
function returns the value (or values) from each form field. The value returned
by param is always a list, but for text, textarea, password, radio,
and single select fields you can use it in a scalar context. For checkboxes and
multiple select fields, you use it in an array context.
In the next chapter we'll learn how to read and write data files, so you'll
be able to save and analyze the data collected by your forms.
|