home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Perl CookbookPerl CookbookSearch this book

20.9. Using Templates to Generate HTML

20.9.3. Discussion

Parameterized output for your CGI scripts is a good idea for many reasons. Separating your program from its data lets other people (art directors, for instance) change the HTML but not the program. Even better, two programs can share the same template, so style changes in the template are immediately reflected in both programs' output.

For example, suppose you have the first template from the Solution stored in a file. Then your CGI program contains the definition of the template subroutine shown earlier and makes appropriate settings for variables $username, $count, and $total. You can fill in the template by simply using:

%fields = (
            username => $whats_his_name,
            count    => $login_count,
            total    => $minute_used,
);

print template("/home/httpd/templates/simple.template", \%fields);

The template file contains keywords surrounded by double percent symbols (%%KEYWORD%%). These keywords are looked up in the %$fillings hash whose reference was passed as the second argument to template. Example 20-7 is a more elaborate example using an SQL database.

Example 20-7. userrep1

  #!/usr/bin/perl -w
  # userrep1 - report duration of user logins using SQL database
  
  use DBI;
  use CGI qw(:standard);
  
  # template( ) defined as in the Solution section above
  
  $user = param("username")                   or die "No username";
  
  $dbh = DBI->connect("dbi:mysql:connections:mysql.domain.com",
      "connections", "seekritpassword")       or die "Couldn't connect\n";
  $sth = $dbh->prepare(<<"END_OF_SELECT")     or die "Couldn't prepare SQL";
      SELECT COUNT(duration),SUM(duration) 
      FROM logins WHERE username='$user'
  END_OF_SELECT
  
  # this time the duration is assumed to be in seconds
  if (@row = $sth->fetchrow_array( )) {
      ($count, $seconds) = @row;
  } else {
      ($count, $seconds) = (0,0);
  } 
  
  $sth->finish( );
  $dbh->disconnect;
  
  print header( );
  print template("report.tpl", {   
      'username' => $user,
      'count'    => $count,
      'total'    => $total 
  });

For a fancier, more flexible solution, look at the second template in the Solution section, which relies upon the CPAN module Text::Template. Contents of braces found within the template file are evaluated as Perl code. Ordinarily, these substitutions are just simple variables:

You owe: {$total}

but they can also include full expressions:

The average was {$count ?  ($total/$count) : 0}.

Example 20-8 is an example of using that template.

Example 20-8. userrep2

  #!/usr/bin/perl -w
  # userrep2 - report duration of user logins using SQL database
  
  use Text::Template;
  use DBI;
  use CGI qw(:standard);
  
  $tmpl = "/home/httpd/templates/fancy.template";
  $template = Text::Template->new(-type => "file", -source => $tmpl);
  $user = param("username")                   or die "No username";
  
  $dbh = DBI->connect("dbi:mysql:connections:mysql.domain.com",
      "connections", "secret passwd")         or die "Couldn't db connect\n";
  $sth = $dbh->prepare(<<"END_OF_SELECT")     or die "Couldn't prepare SQL";
      SELECT COUNT(duration),SUM(duration) 
      FROM logins WHERE username='$user'
  END_OF_SELECT
  
  $sth->execute( )                             or die "Couldn't execute SQL";
  
  if (@row = $sth->fetchrow_array( )) {
      ($count, $total) = @row;
  } else {
      $count = $total = 0;
  }
  
  $sth->finish( );
  $dbh->disconnect;
  
  print header( );
  print $template->fill_in( );

But this approach raises security concerns. Anyone who can write to the template file can insert code that your program will run. See Recipe 8.17 for ways to lessen this danger.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.