Porting cgi-lib.pl Scripts to CGI.pm

Steve Brenner, author of cgi-lib.pl, recently asked me to prepare a document that compares cgi-lib.pl to CGI.pm and to give some advice for people wishing to port scripts from one to the other.

I heartily endorse cgi-lib.pl for people who have good reasons for sticking with Perl version 4. However, sites that use Perl 5.001 and higher should seriously consider switching to CGI.pm or to the CGI::* modules. Here are some reasons why.

Why use CGI.pm instead of cgi-lib.pl?

At their core, both cgi-lib.pl and CGI.pm provide convenient ways to get at CGI query strings. There are a number of reasons to use CGI.pm in preference to cgi-lib.pl.
CGI.pm provides better support for multi-valued parameters.
Named parameters that correspond to checkboxes and selection lists are frequently multi-valued. With cgi-lib.pl, you must manually split the components with split() or (in version 2.0) with SplitParam():
      @players=split("\0",$in{'players'});
      
With CGI.pm, you retrieve single or multi-valued parameters with the same syntax:
      @players=param('players');
      

CGI.pm provides a more elegant interface to file uploads.
In cgi-lib.pl you have to anticipate in advance how large Netscape file uploads may be and select whether the file is to be read into main memory or spooled to disk. CGI.pm provides you with a variable that you can treat as a scalar to recover the original file name, or as a file handle that you can read from just as if you were reading the original file. You don't have to worry about spooling issues:
      $in_file = param('file_to_upload');
      while (<$in_file>) {
         $lineCount++;
      }
      

CGI.pm gives you lots of HTML and HTTP shortcuts.
CGI.pm includes methods that generate HTTP headers, redirection requests, and HTML tags (including the Netscape extensions). These features are not included in cgi-lib.pl

CGI.pm provides a simple way of creating "sticky" forms and maintaining state.
Among the HTML tag-generating shortcuts are methods for generating the elements of fill-out forms. By default, these methods use the current query string to initialize the form element contents. This gives you a simple mechanism for saving the state of a session, and has the nice side effect that the form doesn't revert back to its initial state every time you regenerate it. Other methods in CGI.pm allow you to save state in URLs, write the state out to a file, or even store the session state in an external database.

CGI.pm gives you access to advanced HTTP and HTML features.
Support for persistent cookies, Netscape frames and JavaScript is built into the module, along with some of the more esoteric HTTP features such as content negotiation.

Reasons not to migrate to CGI.pm

The main difference is performance. On a Pentium 90 system running Linux, cgi-lib.pl takes 0.11 seconds to load. CGI.pm takes 0.21 seconds. If that tenth of a second matters to you, then you should continue to use cgi-lib.pl.

How do I migrate from cgi-lib.pl to CGI.pm?

A compatability mode allows you to port most scripts that use cgi-lib.pl to CGI.pm without making extensive source code changes. Most of the functions defined in cgi-lib.pl version 2.10 are available for your use. Missing functions are easy to work around. Follow this model:

Old Script

require "cgi-lib.pl";
&ReadParse;
print "The price of your purchase is $in{price}.\n";

New Script

use CGI qw(:cgi-lib);
&ReadParse;
print "The price of your purchase is $in{price}.\n";
In most cases the only change you'll need to make is the require line. The line
use CGI qw(:cgi-lib);
instructs Perl to read in CGI.pm and to import into your script's name space the cgi-lib.pl compatability routines. (In case you've never run into this syntax before, the colon in front of cgi-lib indicates that we're importing a family of routines identified by the tag cgi-lib rather than a single routine.) The main routine that is imported is ReadParse, which behaves in exactly the same way as cgi-lib.pl's. You can call it without any parameters, in which case it will place the query string in the associative array %in, or pass it the name of the associative array that you want to use:
ReadParse(*Query);
@partners = split("\0",$Query{'golf_partners'});
CGI.pm is object-oriented, meaning that the parsed query string is stored inside a "CGI" object. When you use ReadParse(), a default CGI object is created: behind the scenes access to the %in associative array is actually reading and writing its values to the CGI object. You can get direct access to the underlying object by using the special key 'CGI':
&ReadParse;
print "The price of your purchase is $in{price}.\n";
$q = $in{CGI};
print $q->textfield(-name=>'price',
                -default=>'$1.99');
This allows you to start taking advantage of the CGI.pm features without scouring your code for all the places where you used the cgi-lib.pl %in variable. An even simpler way to mix cgi-lib calls with CGI.pm calls is to import both the :cgi-lib and :standard method:
use CGI qw(:cgi-lib :standard);
&ReadParse;
print "The price of your purchase is $in{price}.\n";
print textfield(-name=>'price',
                -default=>'$1.99');

Cgi-lib functions that are available in CGI.pm

In compatability mode, the following cgi-lib.pl functions are available for your use:
  1. ReadParse()
  2. PrintHeader()
  3. HtmlTop()
  4. HtmlBot()
  5. SplitParam()
  6. MethGet()
  7. MethPost()

Cgi-lib functions that are not available in CGI.pm

Extended form of ReadParse()
The extended form of ReadParse() that provides for file upload spooling, is not available. However you can read the contents of the file directly from %in as follows:
      print "The name of the file is $in{uploaded_file};
      while (<$in{uploaded_file}>) {
         print "Next line = $_";
      }
      

MyBaseURL()
This function is not available. Use CGI.pm's url() method instead.

MyFullURL()
This function is not available. Use CGI.pm's self_url() method instead.

CgiError(), CgiDie()
These functions are not supported. Look at CGI::Carp for the way I prefer to handle error messages.

PrintVariables()
This function is not available. To achieve the same effect, just print out the CGI object:
      use CGI qw(:standard);
      $q = new CGI;
      print h1("The Variables Are"),$q;
      

PrintEnv()
This function is not available. You'll have to roll your own if you really need it.

@in not supported
The original ReadParse() stores the individual elements of the query string in an array named @in. This rarely- used feature is not supported. To retrieve the keywords from an oldstyle <ISINDEX> search, fetch the special array key keywords:
      @keywords = SplitParam($in{'keywords'});
      

Caveats

The compatability routines are a recent feature (added in CGI.pm version 2.20, released on May 22, 1996) and may contain bugs. Caveat emptor!
CGI.pm Documentation
Lincoln D. Stein, lstein@genome.wi.mit.edu
Whitehead Institute/MIT Center for Genome Research
Last modified: Wed May 22 23:33:25 EDT 1996