CGI.pm Basics

The CGI.pm module offers Perl programmers powerful page construction and form parsing capabilities.

The Lazy Programmer

If you've been following along in our Perl 101 series of tutorials, you probably already know how Perl scripts can dynamically generate HTML pages in response to user input. You've learnt how to create a basic guestbook and form mailer, and you've understood the nitty-gritties of string and file manipulation.

Now that you've done all that hard work, I'm going to show you a few shortcuts that should help reduce the amount of time you spend on your Perl scripts. These shortcuts come in the form of a powerful Perl "module" called CGI.pm, which offers a number of interesting features and functions for the lazy Perl programmer.

Over the next few pages, I'm going to show you a few CGI.pm basics. So order a pizza, pop a can of Coke, unplug the telephone, and sit back - this is good stuff!

The Wonder That Is CGI.pm

First, we need to get the definitions down. In case you're wondering what a module is, don't - for the purpose of this article, just assume that it's a thingamajig that allows you to add new capabilities to your Perl program, or a series of pre-rolled functions which can be plugged in to your Perl program.

There are a number of such modules out there - CPAN, the Comprehensive Perl Archive Network, at http://www.cpan.org/ , has a complete list - and most are available free of charge, and are simple to import into your Perl program. If you're running a fairly recent version of Perl - say, 5.004 or higher - you probably already have CGI.pm installed as part of your distribution; if not, drop by CPAN and get yourself a copy.

You're probably wondering just what CGI.pm brings to the Perl party. Let me enlighten you.

In the Perl 101 series, we showed you to how to roll your own functions to parse the query strings generated when a form is submitted. With CGI.pm, all that is history - the module comes with powerful parsing capabilities that assist in the icky task of parsing query strings and separating them into individual name-value pairs.

Next, it also comes with some neat functions that simplify the task of writing HTML code. No more coding your way through <TABLE>s and <FORM>s - CGI.pm can generate them for you with simple, easy-to-understand-and-remember commands.

Finally, CGI.pm is written in the best traditions of object-oriented programming, fondly known as OOP. All the activities I've described above take place through a CGI object, which has its own methods and properties. In case you don't know what OOP is, you're probably not impressed. Don't worry about it - it's a good thing, and I'll be covering the basics of OOP in Perl in a separate article very soon.

Starting At The Top

There are two ways in which CGI.pm can be used - the manual refers to them as "function-oriented" mode and "object-oriented" mode. For the moment, I'll stick to function-oriented mode, and will demonstrate object-oriented mode as we proceed.

CGI.pm offers numerous functions that simplify the creation of HTML pages. For example, all HTML documents typically begin with

<html>
<head>
</head>
<body>

and end with

</body>
</html>

If you were using CGI.pm to generate your HTML page, you could generate all that code in one fell swoop like this:

#!/usr/bin/perl

use CGI qw(:standard);

print header();
print start_html();
print end_html();

And here's what the output looks like when you browse to the page:

</body>
</html>

Why stop there?

#!/usr/bin/perl

use CGI qw(:standard);

print header();
print start_html();

print "Really?! You mean I don't need to remember the difference between
SUP and SUB anymore?";

print br(), hr();

print end_html();

And here's what it looks like:

</body>
</html>

Thus, CGI.pm comes with numerous methods that allow you to generate the basic components of an HTML page. As the example above demonstrates, the start_html() and end_html() methods generate the opening and closing tags for an HTML document, while the p(), br() and hr() methods generate the <p>, <br> and <hr> tags respectively.

You can add a title to the document through the optional -title parameter to the start_html() function - the following code

print start_html(-title=>'The Light Dawns');

would generate a page with the title

</body>
</html>

You're probably wondering what the

:standard

means. The CGI.pm module is classified into different "families" of methods, thereby allowing the user to only import those methods into his or her Perl script which are absolutely necessary. The important families are:

:all - all available methods

:html2 - all methods which generate HTML code, as per the HTML2 specification

:html3 - all methods which generate HTML code, as per the HTML3 specification

:form - all methods related to form elements

:cgi - methods relating to CGI script processing (including parsing of query strings)

:standard - an umbrella family covering :html2, :form and :cgi

Pretty Pictures And Twirly Tables

Obviously, CGI.pm also allows you to add formatting to your document - the following example will demonstrate bold, italic and underlined text, together with the various block-level headings.

#!/usr/bin/perl

use CGI qw(:all);

print header();
print start_html(-title=>'Test Drive');

print center(h1("Test Drive"));
print h4("Hmm. Let's see what else we can do with this thing.");

print p();

print "I can " . b("emphasize") . ", " . i("italicize") . " and " .
sup("superscript") . " text - as a matter of fact, I just did!";

print end_html();

How about fonts, anchors and images?

#!/usr/bin/perl

use CGI qw(:all);

# header
print header();
print start_html();

# set up some fonts
# note how tag attributes are placed in a hash, with the text outside the hash
print font({-face=>'Arial', -size=>'+1'}, 'Pick a font...any font!');

print p();

# create an <IMG> tag
print img({-src=>'button.gif',-border=>'0'});

print p();

# or an anchor - it's very similar to the font() method above
print a({-href=>'http://www.somewhere.com/somefile.html'}, 'And then click
me...');

print p();

# or you could get really advanced - this is a clickable image
print a({-href=>'http://www.somewhere.com/somefile.html'},
img({-src=>'button.gif', -border=>'0'}));

print end_html();

And here's the output:

</body>
</html>

Note the manner in which tags with attributes are represented in the code above - does it look familiar? If it does, it's because the various attributes and their values are placed in an associative array or hash as key-value pairs.

The dashes preceding each key are optional - the above example could be re-written like this:

#!/usr/bin/perl

use CGI qw(:all);

# header
print header();
print start_html();

# set up some fonts
# note how tag attributes are placed in a hash, with the text outside the hash
print font({face=>'Arial', size=>'+1'}, 'Pick a font...any font!');

print p();

# create an <IMG> tag
print img({src=>'button.gif',border=>'0'});

print p();

# or an anchor - it's very similar to the font() method above
print a({href=>'http://www.somewhere.com/somefile.html'}, 'And then click
me...');

print p();

# or you could get really advanced - this is a clickable image
print a({href=>'http://www.somewhere.com/somefile.html'},
img({src=>'button.gif', border=>'0'}));

print end_html();

and still function correctly.

Finally, you can also easily create tables, complete with rows and cells, through the aptly named table(), Tr() and td() methods - take a look:

#!/usr/bin/perl

use CGI qw(:all);

# header
print header();
print start_html();

print

# table
table({-border=>'1', -cellpadding=>'5', -cellspacing=>'5'},

# table row
    Tr({-align=>'left', -valign=>'top'},
        [

# table cells
            td(['R1,C1','R1,C2','R1,C3']),
            td(['R2,C1','R2,C2','R2,C3'])
        ]
      )

    );

print end_html();

And here's what it looks like:

</body>
</html>

Up Close And Personal

Since the primary raison d'etre of the CGI.pm module is to simplify your interaction with form data, it's only natural that the module include form creation capabilities. Forms typically begin with a call to the start_form() method and end, predictably, with a call to end_form().

Note that the start_form() method needs to know the form action, method and encoding type; these elements are passed to it as parameters.

#!/usr/bin/perl

use CGI qw(:all);

# header
print header();
print start_html();

# form opening and closing tags
print start_form(-method=>'post', -action=>'script.cgi');

print end_form();

print end_html();

And you'll get the basic skeleton of a form:

</body>
</html>

CGI.pm also offers methods to construct the various elements of a form - text boxes, radio buttons, checkboxes and the like - and the following example demonstrates a simple form with three text fields, one password field, one hidden field, and submit and reset buttons.

#!/usr/bin/perl

use CGI qw(:all);

# header
print header();
print start_html(-title=>'Personal Information');

print center(h1('So, Who Are You Anyway?'));

print start_form(-method=>'post', -action=>'script.cgi');

# text field
print "First name" . textfield(-name=>'fname');

print br();

print "Last name" . textfield(-name=>'lname');

print br();

print "Desired user name" . textfield(-name=>'username');

print br();

# password field
print "Desired password" . password_field(-name=>'password');

print br();

# hidden form data
print hidden(-name=>'action', -value=>'register');

# submit and reset buttons
print submit("Register"), reset("Start Over");

print end_form();

print end_html();

You can also add a checkbox (or a group of checkboxes) and a few radio buttons

# radio buttons
print "Sex: " . radio_group(-name=>'sex', -values=>['Male', 'Female'],
-default=>'Male');

# checkbox
print checkbox(-name=>'married', -value=>'yes', -label=>'Married?');

Note that the values for the radio button group must be passed as an array.

Need a list box? No problem!

# drop-down listbox
print "Age" . popup_menu(-name=>'age', -values=>['Under 18', '18-30',
'30-45', 'Over 45']);

Note that the "-values" parameter must be passed as an array, as with radio buttons.

You can add labels to the items in the listbox by passing them to the function as a hash; the keys of the hash correspond to the elements of the "-values" array, while the corresponding values of the hash are the labels that become visible to the user.

# drop-down listbox
print "Age" . popup_menu(-name=>'age', -values=>['Under 18', '18-30',
'30-45', 'Over 45'], -labels=>{'Under 18'=>'Young', '18-30'=>'Older',
'30-45'=>'Middle-aged', 'Over 45'=>'Out to pasture'});

Finally, you can also add a file upload button to the form with the filefield() method:

# file upload button
print "Enter path to picture file: " . filefield(-name=>'picture_file');

And here's what the finished product looks like:

#!/usr/bin/perl

use CGI qw(:all);

# header
print header();
print start_html(-title=>'Personal Information');

print center(h1('So, Who Are You Anyway?'));

print start_form(-method=>'post', -action=>'script.cgi');

# text field
print "First name: " . textfield(-name=>'fname');

print br();

print "Last name: " . textfield(-name=>'lname');

print p();

print "Desired user name: " . textfield(-name=>'username');

print br();

# password field
print "Desired password: " . password_field(-name=>'password');

print p();

# drop-down listbox
print "Age: " . popup_menu(-name=>'age', -values=>['Under 18', '18-30',
'30-45', 'Over 45'], -labels=>{'Under 18'=>'Young', '18-30'=>'Older',
'30-45'=>'Middle-aged', 'Over 45'=>'Out to pasture'});

print p();

# radio buttons
print "Sex: " . radio_group(-name=>'sex', -values=>['Male', 'Female'],
-default=>'Male');

print p();

# checkbox
print checkbox(-name=>'married', -value=>'yes', -label=>'Married?');

print p();

# file upload button
print "Enter path to picture file: " . filefield(-name=>'picture_file');

# hidden form data
print hidden(-name=>'timestamp', -value=>'200011011256');

print p();

# submit and reset buttons
print submit("Register"), reset("Start Over");

print end_form();

print end_html();

And the output is:

</body>
</html>

All done? Let's see what happens when you submit the form.

Within The Parameters

Once the form is submitted, the task becomes to split the various elements of the query string into individual name value pairs and make them available to Perl as variables. And CGI.pm comes well-equipped to do this with the powerful param() function.

If used without any parameters, the param() function will simply display a list of all available name-value pairs. Alternatively, if you need to obtain the value of a particular form field, you can simply provide the field name to param() as a parameter.

#!/usr/bin/perl

# this is script.cgi - accepts form input and displays welcome message

use CGI qw(:all);

print header(), start_html(), "Hello, " . param('fname'), end_html();

What does this mean? It means that you can use param() to combine the query page and the result page into a single script, like this:

#!/usr/bin/perl

# this is script.cgi - accepts form input and displays welcome message

use CGI qw(:all);

# if param() returns a result, it implies that the form has been submitted
if (param())
        {
        print header(), start_html(), "Hello, " . param('fname'), end_html();
        }
# else display the initial page
else
        {

        # header
        print header();
        print start_html(-title=>'Personal Information');

        print center(h1('So, Who Are You Anyway?'));

        print start_form(-method=>'post', -action=>'script.cgi');

        # text field
        print "First name: " . textfield(-name=>'fname');

        print br();

        print "Last name: " . textfield(-name=>'lname');

        print p();

        print "Desired user name: " . textfield(-name=>'username');

        print br();

        # password field
        print "Desired password: " . password_field(-name=>'password');

        print p();

        # drop-down listbox
        print "Age: " . popup_menu(-name=>'age', -values=>['Under 18',
'18-30',
'30-45', 'Over 45'], -labels=>{'Under 18'=>'Young', '18-30'=>'Older',
'30-45'=>'Middle-aged', 'Over 45'=>'Out to pasture'});

        print p();

        # radio buttons
        print "Sex: " . radio_group(-name=>'sex', -values=>['Male',
'Female'], -default=>'Male');

        print p();

        # checkbox
        print checkbox(-name=>'married', -value=>'yes', -label=>'Married?');

        print p();

        # file upload button
        print "Enter path to picture file: " .
filefield(-name=>'picture_file');

        # hidden form data
        print hidden(-name=>'timestamp', -value=>'200011011256');

        print p();

        # submit and reset buttons
        print submit("Register"), reset("Start Over");

        print end_form();

        print end_html();

        }

Note the manner in which param() can be used to check whether or not the form has already been submitted; if a call to param() returns nothing, it implies that the form has not yet been submitted.

The Object Of This Exercise Is...

For those of you familiar with object-oriented programming in Perl, you can also use CGI.pm in object-oriented mode. The primary difference here is that you first create a new CGI object, and then invoke methods on this object to create the form.

I've re-written the previous example using CGI.pm's object-oriented mode to demonstrate the differences.

#!/usr/bin/perl

# this is script.cgi - accepts form input and displays welcome message

# note the difference in syntax when using CGI.pm as an object
use CGI;

# create a new CGI object
$regform = new CGI;

# if param() returns a result, it implies that the form has been submitted
if ($regform->param())
        {
        print $regform->header(), $regform->start_html(), "Hello, " .
$regform->param('fname'), $regform->end_html();
        }

# else display the initial page
else
        {

        # header
        print $regform->header();
        print $regform->start_html(-title=>'Personal Information');

        print $regform->center($regform->h1('So, Who Are You Anyway?'));

        print $regform->start_form(-method=>'post', -action=>'script.cgi');

        # text field
        print "First name: " . $regform->textfield(-name=>'fname');

        print $regform->br();

        print "Last name: " . $regform->textfield(-name=>'lname');

        print $regform->p();

        print "Desired user name: " . $regform->textfield(-name=>'username');

        print $regform->br();

        # password field
        print "Desired password: " .
$regform->password_field(-name=>'password')
;

        print $regform->p();

        # drop-down listbox
        print "Age: " . $regform->popup_menu(-name=>'age', -values=>['Under
18', '18-30', '30-45', 'Over 45'], -labels=>{'Under 18'=>'Young',
'18-30'=>'Older','30-45'=>'Middle-aged', 'Over 45'=>'Out to pasture'});

        print $regform->p();

        # radio buttons
        print "Sex: " . $regform->radio_group(-name=>'sex',
-values=>['Male', 'Female'], -default=>'Male');

        print $regform->p();

        # checkbox
        print $regform->checkbox(-name=>'married', -value=>'yes',
-label=>'Married?');

        print $regform->p();

        # file upload button
        print "Enter path to picture file: " .
$regform->filefield(-name=>'picture_file');

        # hidden form data
        print $regform->hidden(-name=>'timestamp', -value=>'200011011256');

        print $regform->p();

        # submit and reset buttons
        print $regform->submit("Register"), $regform->reset("Start Over");

        print $regform->end_form();

        print $regform->end_html();

        }

Obviously, you should use whichever one you're more comfortable with - although an object does offer certain traditional advantages.

You Have Mail!

Finally, I'll wrap this up by creating a simple form mailer using CGI.pm. This is similar to the one created in Perl 101, except that this version will be written entirely through CGI.pm constructs. If you compare the two, you'll see that this version is more compact and easier on the eye.

#!/usr/bin/perl

# feedback.cgi - sets up feedback form mailer

use CGI;

$feedback = new CGI;

# if form has been submitted
if ($feedback->param())
    {

    # open mail pipe
    open (MAIL,"|/usr/sbin/sendmail -t");
    print MAIL "To: <webmaster\@yoursite.com>\n";
    print MAIL "From: Feedback Form Mailer\n";
    print MAIL "Subject: Feedback on your site\n\n";
    print MAIL "Here is the result of your feedback form.\n\n";

    # insert values from form
    print MAIL "Name: " . $feedback->param('who') . "\n";
    print MAIL "Email address: " . $feedback->param('email') . "\n";
    print MAIL "Age: " . $feedback->param('age') . "\n";

    # close mail pipe
    close MAIL;

    # print thank-you message
    print $feedback->header(), $feedback->start_html(),
$feedback->center($feedback->h1('Thank you for your feedback!')),
$feedback->end_html();

    }
else
# if form has not been submitted
    {

    # print form
    print $feedback->header(), $feedback->start_html(-title=>'Feedback Form'),
$feedback->h1('Feedback Form'),
$feedback->start_form(-action=>'feedback.cgi', -method=>'post');

    print "Name: ", $feedback->textfield(-name=>'who', -size=>'30'),
$feedback->p();

    print "Email address: ", $feedback->textfield(-name=>'email',
-size=>'30'), $feedback->p();

    print "Age: ", $feedback->textfield(-name=>'age', -size=>'2'),
$feedback->p();

    print $feedback->submit('Send mail');

    print $feedback->end_form(), $feedback->end_html();
    }

# EOF

And that's about it. See you soon!

Note: All examples in this article have been tested on Linux/i586 with Apache 1.3.12 and Perl 5.004. Examples are illustrative only, and are not meant for a production environment. YMMV!

This article was first published on07 Nov 2000.