Using What We've Learned

This month Mr. Lerner shows us how to set up a web site using many of the techniques he's taught us over the past months.
Tracking Users

Now users can enter information about their birthdays into the system. But wait—at the beginning of the column, I said that we were going to make it possible to keep track of users' comings and goings. One easy way to do that is to use HTTP cookies, small pieces of data stored on the user's computer that are sent to the site that set them. CGI programs can set cookies whenever they return an HTTP response to a user's browser. Whatever cookies were set are then returned upon each subsequent visit to that web server. In this way, they can be used as variables, albeit variables that might be modified or removed by users worried about their privacy, or who might come to our site from a friend's computer.

Our cookie will have to be a unique identifier that can be used to bring up the user's entry from the “birthdays” table. We thus have two options—the user's e-mail address, which is guaranteed to be unique because it is a primary key, and person_id, which is automatically incremented each time a new entry is added to the table. We will use person_id, but there is no reason why you could not use the “email” column. Indeed, given that “email” is a primary key, you could even do away with the person_id column—except that if you were to create other tables that refer to individual users, keeping track of an integer (such as person_id) is much more efficient than using their full e-mail address.

How can we retrieve the ID that was added to a row that we might add? The most obvious way is to retrieve the row that we just entered, creating and sending an SQL query to the MySQL server. But MySQL has an easier way to do this—after sending our query, we can ask for $sth->insertid. $sth is the “statement handle,” an object that allows us to send and retrieve information about an individual SQL query and statement, and insertid is one of the methods that $sth provides.

Once we know the value of person_id, we can create a cookie with the following two lines of Perl:

my $cookie = $query->cookie(-name => "person_id",
        -value => $sth->insertid);

The cookie (now stored in $cookie) is sent as part of the HTTP header returned by the CGI program to the user's browser. Thus, the original version (see Listing 2) of our program sent a basic MIME header at the beginning of the program's execution with the command:

print $query->header("text/html");
But the new version of our program will have to move that to a later portion of the program, after we have already sent our query to the database. In addition, we will have to modify our statement such that it sends the cookie along with the MIME information in the header. This can be accomplished with the following code:
print $query->header(-type => "text/html",
                         -cookie => $cookie);
When the above code runs, it sends both the MIME header that describes the type of output that we are sending to the client, and the information describing the “person_id” cookie that we want to set. Every time this user visits our site in the future, we will be able to retrieve the value of “person_id”, and thus look the user up in a table in our database.

You can see the results of changing our program in Listing 3. The modifications are fairly small, but they ensure that a cookie is returned to the user's browser whenever we successfully add a row to the database. (When no row is added to the database, the header remains as before, sending the MIME header but nothing else.)

Retrieving the Data

The final part of our system will bring cookies and databases together with another technique we discussed several months ago, Perl/HTML templates. Templates are pages of HTML with small snippets of Perl interspersed between the HTML tags, accessed via a short program that uses the Text::Template module to turn the Perl into text. This allows HTML pages to perform database lookups, calculations and other elements that require computation too difficult to accomplish with server-side includes. Unlike straight CGI programs, templates can be edited by your site's designers and HTML coders, removing the programmer as a bottleneck to changing the HTML that a program produces.

In Listing 4, you can see wrapper.pl, a CGI program that turns templates into HTML. (A version of wrapper.pl published in a previous issue of LJ was not tested thoroughly, and contains several bugs that were fixed in this version.) Assuming that wrapper.pl is installed on your system and that the file /home/httpd/html/birthdayhp.tmpl is a valid template, you should be able to request

/cgi-bin/wrapper.pl?/home/httpd/html/birthdayhp.tmpl

In other words, wrapper.pl should be invoked with a single argument, the name of the template that should be evaluated and then returned.

This version of wrapper.pl allows us to pass variables to the template using the LJ package (as described in the manual pages for Text::Template). This allows us to pass variable values from wrapper.pl to templates. In general, we would not want to pass variable values from wrapper.pl to a template, since the template should be somewhat isolated from its surroundings and should be allowed to assign its own variables. But in this case, we do want to pass one value, that of $query (the CGI instance), which allows us to access information passed to wrapper.pl per the CGI specification. This includes the “cookie” method, which allows us to retrieve cookies that our server has set in the past.

I have included a simple version of birthdayhp.tmpl in Listing 5, so that you can see how easy it is to include Perl inside HTML. There is a performance penalty for serving documents in this way, since you are forcing an invocation of Perl each time a template is viewed on your system. But that drawback is often counterbalanced by a template's versatility and ease of inclusion in a site. A large Web site's editorial and production staffs can thus modify the site's content and design without disturbing programs necessary for the site to run. The programs are surrounded by curly braces, making them easy to spot in an HTML file.

One thing to remember when dealing with templates is that the output from each Perl fragment is inserted into the resulting HTML. If you wish to send the text “Hello, there” to the user's browser from within a Perl fragment, in order for things to work correctly you must use:

{
    "Hello, there"
  }

or a slightly more formal version:

{
    my $outputstring = "";
    $outputstring .= "Hello, there";
    $outputstring;
  }
Do not make the mistake of trying to use print from within a template, as in this case:
{
    my $outputstring = "";
    $outputstring .= "Hello, there";
    print $outputstring;
  }
The contents of $outputstring will indeed be sent to the user's browser thanks to print, but the text will be sent ahead of the rest of the template. In place of the Perl block containing this code, the Text::Template module will insert the result from print—the result will have a value of 1 when printing is successful. Setting strings, rather than printing them directly, is the norm in the case of templates, but takes some getting used to if you are an experienced Perl programmer.

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState