Registration Log
May 10, 1999
The ever popular "Guestbook" is a typical sort
of informal registration log that many web sites offer
(and promote). Typically, though, a guestbook is simply
a compilation of comments, such as those found scrawled
on the open book at the exit of a national historic site.
We're going to aim for a more formal method of compiling
visitor registrations -- in fact, we want to create a basic
database of these registrations. For our purposes,
we can imagine that visitors who register with us provide
four pieces of information: name, telephone number, e-mail
address, and ZIP code (the latter could allow us to
chart registrants geographically).
How will we store this information once it is submitted?
An advanced configuration would probably insert the
data into an existing database, using some type of
database management technology such as SQL. We won't delve into
that here, although we may perhaps in a future installment.
For starters, let's simply plan on a plain text, tab-delimited
table format. This is a simple format, yet easily imported
into any "real" database management system.
The goal, then, of our Perl-based registration program is
to receive the user's submission from the web page,
validate it, process where necessary, and store the results
into a text file which will ultimately resemble:
Ella DiBella=>5551239876=>ella@suky.com=>13062
Borders McGee=>5551235432=>bmcgee@silly.net=>14850
Rather than the => symbol, though, our table will actually
contain tab characters (which cannot easily be
displayed in this medium).
In building our ®isterForm subroutine, which
will eventually become part of the register.cgi
Perl script, we'll encounter a new facility of Perl programming:
opening and writing to local files. Remember that
the Perl script, by virtue of CGI, is running on the web
server. This means that the Perl script can open, read
from, and write to files on the web server -- provided
that an existing file has the proper permissions to be read
by the Perl script, depending on the operating system.
Note: a Perl script running on the web server cannot
read or write files on the web page visitor's computer,
unless the host web server is the visitor's computer!
Let's begin ®isterForm by opening the text
file to which we are adding the registration data:
sub registerForm
{ unless (open (LOGFILE,">>/var/logs/registerlog"))
{print "Failed to open Registration Log!";exit}
flock(LOGFILE, LOCK_EX);
The action of this block of code may not be obvious. At its
heart, we try to open the file "/var/logs/registerlog"
-- of course, this exact path and filename would be your
choice depending on your operating system. The double-greater-than
preceding the filename (>>) indicates that the file should
be opened in append mode. In this mode,
any data written to the file will be appended to any data
already in the file; or, if the file doesn't yet exist,
it will be created. Note: a single greater-than (>)
opens the file in create or overwrite
mode, which will wipe out any existing file with that name
and start it anew. Be careful!
The open() function creates a filehandle named
LOGFILE. We can think of a filehandle as being
similar to a variable, but it references a particular file
and the mode to operate in (again, we're using
/var/logs/registerlog
in append mode). If the open() function fails
to open the file for the specified mode -- it may not
exist, or may not possess permission for reading or writing --
a false value is returned. This is why the open()
call is wrapped inside an unless statement: if the file
fails to open, the failure clause executes, printing
an error message and then quitting the program ("exit").
Assuming the file has successfully been opened for appending,
we now see a strange statement, the Perl flock()
function. If you are developing on a UNIX system, this
statement is very important; other operating systems
may not support flock() so you may need to omit this
line in Windows, for example. In brief, flock()
locks the file that has been opened so that another process
cannot touch it, until the file has been unlocked.
This is crucial on the web, when multiple users may be
submitting registrations at the same time -- this would
cause havoc if each tried to open and write to the same
file simultaneously. Using flock() causes these
multiple submissions to be processed in queue, preserving
the integrity of the open file. Note: some UNIX
systems may not support the LOCK_EX parameter, in
which case you should try substituting the number 2, as
in flock(FILEHANDLE,2).
Now, imagine that we've already collected the form data
into four variables: $username, $userphone, $usermail,
$userZIP. Outputting this data to the newly opened file
is quite simple:
print LOGFILE "$username\t$userphone\t$usermail\t$userZIP\n";
unless (close (LOGFILE))
{ print "Failed to close Registration Log!";exit}
print "<h2>Thank you for your registration. ".
"Click <a href=\"home.html\">here</a>".
"to return to the home page.";
}
This print statement directs the output to the
filehandle LOGFILE, which of course points to the file
we have opened for appending. The output itself simply
consists of each data variable separated by a tab character
(the \t escape sequence in Perl). The final piece of
data is followed by a newline character, also known as a carriage
return (the \n escape sequence).
Once output to the log file, we close the log file, which
also causes the lock to be released. Should the close()
call fail, which is rare if the file was already successfully
opened, an error message appears and the script exits.
Otherwise, a print statement outputs HTML markup thanking the
user for their registration, and the subroutine is
closed with a final curly brace.
We're ready to bring together the pieces into a final,
complete register.cgi Perl script. Note one other
addition -- the statement "use Fcntl;"
which has been added near the top of this script. This
statement is sometimes a prerequisite for using the
flock() call, however some Perl installations may not
support the Fcntl module -- if you get an error, omit this line.
register.cgi (complete)
#!/usr/bin/perl
use CGI;
use Fcntl;
#create an instance of the CGI object
$cgiobject = new CGI;
#grab the values submitted by the user
$userphone=$cgiobject->param("userphone");
#output HTML header to web browser
print $cgiobject->header;
#test form validation, output error if necessary
#otherwise proceed to registration log
if ( &validateForm )
{ ®isterForm }
else
{ &output_fail }
# subroutine which validates form fields and
#returns a true or false result
sub validateForm
{ $failedFields="";
$formValid=1;
#validate phone number
$fieldValid=$userphone=~/^\D*\d{3}?\D*\d{3}?\D*\d{4}?\D*$/;
if ($fieldValid)
{ $userphone=~s/\D//g }
else
{ $failedFields.="Telephone Number,";
$formValid=0 }
#validate user name
$fieldValid=$username=~/^[a-zA-Z]+/;
unless ($fieldValid)
{ $failedFields.="User Name,";
$formValid=0 }
#validate ZIP code
$fieldValid=$userZIP=~/^\d{5}(-\d{4})?$/;
unless ($fieldValid)
{ $failedFields.="ZIP Code,";
$formValid=0 }
return $formValid
}
#subroutine which outputs failure message
#if form does not validate
sub output_fail
{ chop($failedFields);
$resultPage="<html><head>".
"<title>Uh-Oh: Registration Problem</title>".
"</head><body bgcolor=\"white\">".
"<h2>Sadly, there seems to be a problem with your ".
"form submission. Specifically, the following ".
"mandatory fields were filled in improperly:</h2>".
"<Br><h3>$failedFields</h3>".
"<Br>Please go back and try again.".
"</body></html>";
print $resultPage;
}
#subroutine which saves the form data to a log file
sub registerForm
{ unless (open (LOGFILE,">>/var/logs/registerlog"))
{print "Failed to open Registration Log!";exit}
flock(LOGFILE, LOCK_EX);
print LOGFILE "$username\t$userphone\t$usermail\t$userZIP\n";
unless (close (LOGFILE))
{ print "Failed to close Registration Log!";exit}
print "<h2>Thank you for your registration. ".
"Click <a href=\"home.html\">here</a>".
"to return to the home page.";
}
Form Validation
The Perl You Need to Know
A Peek at Environment Variables
|