http://qs321.pair.com?node_id=46976

The question of how to use CGI scripts to generate HTML comes up frequently. Consistently, people try to embed the HTML directly in the CGI script. While for simple scripts, this may be useful, it quickly becomes a maintenance nightmare for large sites.

Imagine even a relatively small site that has 5 CGI scripts that each generate 5 Web pages. Since we want a common look and feel, those Web pages often have duplicate elements that get repeated throughout the code. Sometimes, the programmer realizes that common elements should be stuffed in a single location and then added to each page as appropriate. This is useful because it makes updating the pages easier, but it still faces the "mixing HTML and code" problem.

Typically, an organization has relatively few programmers, so updating these Web pages require having relatively high-priced programmers doing HTML maintenance. The programmers often hate this and the company suffers. Another annoying problem with this is a problem that Perl has with "here" documents. Imagine a 100 line here document beginning on line 219 of a script. If one of the values embedded in a document is undefined, you often get an error message like the following:

Use of uninitialized value in concatenation (.) at D:\oursite\local\cg +i-bin\script.cgi line 219.
Perl reports the first line that begins the here document as the one containing the unitialized value. Often, this can be a difficult bug to track down!

While there are many reasons to consider alternatives to embedding HTML in your CGI scripts, I'll just leave it at that and suggest that you consider a template scheme. Many recommend using Template Toolkit for this. However, for a light-weight, easy to implement system, consider using HTML::Template.

Here's a simple example. We'll create a small Web page that will print out all of your environment variables. We'll show four basic functions of HTML::Template: embedded values, looping, branching, and including external HTML files in the main HTML document.

<html> <head><title>Test Template</title></head> <body bgcolor='#999999'> <h1><TMPL_VAR NAME=HEADING></h1> <table border="1"><TMPL_LOOP NAME=ENV_VARS> <tr bgcolor='<TMPL_VAR NAME=BGCOLOR>'> <td><TMPL_VAR NAME=NAME></td> <td><TMPL_VAR NAME=VALUE></td> </tr></TMPL_LOOP> </table> <p> <TMPL_INCLUDE NAME="footer.tmpl"> <p> <TMPL_IF NAME="BOOL"> Some text that only gets displayed if BOOL is true! <TMPL_ELSE> Looks like BOOL was false. </TMPL_IF> </body> </html>
Create the above document and save it as "template.tmpl". You'll notice some new tags in there. Here's what they do:
<TMPL_VAR NAME=HEADING>
The TMPL_VAR tag allows you to assign a value to a name (in this case, HEADING will be assigned a scalar value in the CGI script.
<TMPL_LOOP NAME=ENV_VARS>
This tells HTML::Template that we want to start a loop named ENV_VARS. While it's iterating through the loop, it will look for TMPL_VAR tags and assign them scalar values that the CGI script creates. The following tags are used in the HTML template:
<TMPL_VAR NAME=BGCOLOR> <TMPL_VAR NAME=NAME> <TMPL_VAR NAME=VALUE>
To populate them, an array of anonymous hash references will be created. Each hash reference will look like the following:
{ BGCOLOR => '#FFFFFF', NAME => 'Some Name', VALUE => 'Some Value' }
As HTML::Template iterates over the array, it will take the name/value pairs in the hash reference and use them to duplicate the items in the loop with the name/value pairs filled in appropriately.
<TMPL_INCLUDE NAME="footer.tmpl">
The TMPL_INCLUDE tag tells HTML::Template to take the contents of the filename specified and insert them into the HTML document where the tag is found. Note that, as of this writing, you cannot include HTML::Template tags in the included document.

For now, just create a file named footer.tmpl and add the following line to it:

<small>This document brought to you by the letter 'P'</small>
Simple branching is also possible:
<TMPL_IF NAME="BOOL"> Some text that only gets displayed if BOOL is true! <TMPL_ELSE> Looks like BOOL was false. </TMPL_IF>
This is a simple branch. It tests the value of the BOOL scalar that gets passed to the template object. If it evaluates as true... well, you get the idea.

Finally, here's the script that gets this to work:

C:\perl\bin\perl.exe -T # Strictly speaking, taint checking is not required when you are # simply printing a Web page, but don't leave it out! use warnings; use strict; use HTML::Template; # Always be sure to encode anything that you print to a Web page, part +icularly # if it's not from a trusted source. One Porn image in your guestbook + or properly # chosen meta tags can make your life a headache. use HTML::Entities; use CGI qw/:standard/; # Here we create a new template object and tell it where the template +is my $template = HTML::Template->new( filename => 'template.tmpl' ); # We'll use toggle to set background colors my $toggle = 1; # This array contains a anonymous hash references that correspond to v +alues in the template my @ENV; for ( sort keys %ENV ) { # This toggles $toggle between 0 and 1 $toggle ^= 1; push @ENV, { BGCOLOR => ( $toggle ? '#FFFFFF' : '#CCCCCC' ) , NAME => $_, VALUE => encode_entities( $ENV{$_} ) }; } $template->param( { HEADING => 'Some Environment Variables', ENV_VARS => \@ENV, BOOL => 1 } ); print header, $template->output;
It's the $template->param() call that actually sets up the template. Note that HEADING and BOOL are simply scalar values, while ENV_VARS is the array of hash references that is used for the loop. Try playing around with some of these values to get a feel for how this works.

HTML::Template actually has quite a bit of functionality beyond what I have described here, so be sure to read the documentation. Further, there are a variety of ways to get the output that I did above.

Incidentally, I did not describe the use of "loop context vars." They can be handy to control the behavior of loops, but they've been noted to be buggy in the past and I have found them to still have some issues. Handle with care.

Replies are listed 'Best First'.
Re: Using HTML::Template
by dmitri (Priest) on Dec 16, 2000 at 23:53 UTC
    Thanks for the info! I used to do the same thing manually a couple of years ago. Now I will use the module if, of course, I ever work with CGI in Perl (I am mostly using PHP nowadays).
      Monks, I am new to HTML::Template, and my script is not working :( I have created file /apache/www/test.tmpl, a normal html file AND a test.pl file, which is placed in /cgi-bin/test.pl (relative to apache server). no output and no apache errors are logged Using Ubuntu Linux and location of Template.pm /usr/lib/perl5/HTML/Template.pm Any idea ? Am I missing something simple?