Creating a Web-based BBS, Part 1

Ready to create your own virtual community? Here's how to begin.
Common Program Elements

The programs in this project share a number of elements. Each starts with a series of use statements:

use strict;
use diagnostics;
use CGI;
use CGI::Carp qw(fatalsToBrowser);
use DBI;

The first, use strict, prods us into making our variable references explicit, either by creating them as lexicals (with the my statement) or with the use vars statement. I have chosen to create all variables as lexicals, but if you were interested in putting common variable definitions into an external file, you might want to consider making them globals.

Next, we invoke use diagnostics, which tells Perl to give us information from the perldiag manual page if and when there are problems with our program. I find use diagnostics to be an invaluable debugging tool when working with web applications, since it often points to a foolish mistake I have made. This, along with use strict and the -w flag, makes programming in Perl much less error-prone.

We then load the CGI::Carp module, which overrides the built-in Carp module with routines of its own that make for more accurate messages in our HTTP server's error log. We also import CGI::Carp::fatalsToBrowser, which sends an error message to the user's browser if and when an error occurs. This allows us to use the standard die statement without having to worry about whether we have already sent the HTTP “Content-type” header. Sending a message to the user's browser without such a header almost always causes an error message to be displayed.

Each program in the BBS also defines a number of variables: $database, $server, $port, $username and $password. These variables are used to log into the database with DBI; by setting them at the top of the program, you can modify them as necessary without having to change hard-coded strings.

Each program also turns off buffering, so that information is sent to the user's browser as soon as the program sends it to the appropriate file handle. Normally, saying

print "<P>Hello</P>";

does not send <P>Hello</P> to the user's browser. Rather, it places the string in a buffer. When the buffer is filled, its contents are shipped off to the user's browser. This is more efficient, since the computer can copy a lot of data at once, rather than spending its time entering and exiting from the routines that handle file operations. However, it also means the user must wait to see results. We can turn off buffering by setting the built-in Perl variable $|:

$| = 1;
Finally, each program connects to the database with the standard DBI routine:
my $dbh =
 $username, $password);
If the connection succeeds, we receive a database handle (dbh) in return and store it in $dbh. If $dbh is false, however, we should report an error, since it means the connection did not work:
die "DBI error from connect:", $DBI::errstr
    unless $dbh;
We can do the same thing when preparing a query. The result from $dbh->prepare is a statement handle (sth). When defined, $sth is an object that itself accepts methods. When $sth is undefined, the statement preparation failed:
my $sth = $dbh->prepare($sql);
die "DBI error with prepare: ", $sth->errstr
    unless $sth;
We can execute our statement with $sth->execute, which works in much the same way as $dbh->prepare. The difference is that the result code is a simple value, rather than an object:
my $result = $sth->execute;
In some programs, we test the value of $result and use die to report an error:
die "DBI error with execute: ", $sth->errstr
    unless $result;
In others, we use $result to decide whether to continue with the program or to print a more user-friendly error message:
if ($result)
{   # do something
{   # indicate an error
Finally, we always disconnect from the database at the end of our programs:
This is not truly necessary, since DBI and Perl close all such connections when the program exits. However, if you are running with -w, a message will be inserted into your error log each time a program exits without disconnecting from the database nicely. We do this in order to keep our error log free of spurious details.