Introduction to CGI (edited – lorraine)

Beginning Perl, Second Edition

Written by James Lee

Note – fix empty spaces

Published by Apress

Chapter 14



The Common Gateway Interface (CGI) is a method used by webservers to run external programs (known as CGI scripts), most often to generate web content dynamically. Whenever a web page queries a database, or a user submits a form, a CGI script is usually called upon to do the work.

CGI is simply a specification, which defines a standard way for webservers to run CGI scripts and for those programs to send their results back to the server. The job of the CGI script is to read information that the browser has sent (via the server), and to generate some form of valid response, usually (but not always) visible content. Once it has completed its task, the CGI script finishes and exits.

Perl is a very popular language for CGI scripting thanks to its unrivalled text-handling abilities, easy scripting, and relative speed. It is probably true to say that a large part of Perl’s current popularity is due to its success in dynamic web page generation. Also, there is an excel lent module available for Perl that makes writing CGI scripts easy— CGI.pm .


Note  Usually, when we refer to Perl modules we do not include the “ .pm ”. For instance, when we talk about the DBI module, we never call it “the DBI.pm module.” However, with the CGI module, we call it “the CGI.pm module.” We are not sure why this is, perhaps it is historical—once upon a time we CGI programmers used a set of functions in a file called cgi.pl . Since that file was named cgi.pl , we suppose everyone called the new object-oriented version CGI.pm . Another reason may be an effort to distinguish the CGI.pm module from the CGI protocol. Whatever the reason, we will continue the tradition of calling it CGI.pm .


In this chapter we will discuss the basics of CGI, how data is sent to CGI scripts, how the server responds back to the client, and how to make all this simple with the use of CGI.pm .

This chapter is not meant to be an exhaustive study of writing CGI scripts with Perl, but rather an introduction with enough information for you to get started and with pointers to where to look for more information. The best place to find out all you need to know is by picking up a copy of the book entitled Official Guide to Programming with CGI.pm written by Lincoln Stein (Wiley & Sons, 1998). Of course, the online documentation for CGI.pm is available by executing perldoc CGI . The most important thing to remember about CGI with Perl—it’s fun! So let’s get started.

We Need a Webserver

The first step in writing CGI programs is to obtain access to a webserver. While we have several options (including obtaining an account with an ISP that provides a webserver or convincing a friend with a computer that has a webserver and Internet connection to give us access—taking them out for a nice meal is good way to convince them), we will briefly discuss installing a webserver on our machine and working locally.

The webserver that we are going to suggest is the most popular server used on the Internet: Apache. As this chapter is being written (May 2004), Apache has more than 67% of the webserver market (you can see a comparison of all the different servers at http://news.netcraft.com/ archives/web_server_survey.html)—more than twice the number of all the other servers combined. Apache is popular because it is a solid program, extensible, highly securable, and open source (meaning free!).

The Apache webserver can be found at http://httpd.apache.org/. To download a version for your machine, visit http://httpd.apache.org/ and click the download link. The latest version as of May 2004 is version 2.0.49, but version 1.3.x is suggested for beginners because it is very stable and is much easier to use than version 2.0.x. As of May 2004, 1.3.31 is the latest and greatest. Follow the installation directions found at http://httpd.apache.org/docs/install.html to install it on your machine. Or, better yet, go out and get a copy of Apache Essentials: Install, Configure, Maintain by Darren James Harkness (friends of ED, 2004) for a simple and concise guide to Apache. This book will help you get Apache up and running in no time.

A note to Unix users: chances are, Apache is installed and is running on your machine—yet another reason to like Unix! To verify if your machine is ready to serve a web client, point your browser to http://127.0.0.1/.

127.0.0.1 is a special address that points to the machine that you are sitting in front of. This address is also known as localhost. If you see a response page that tells you Apache is up and running, you are ready to go! 127.0.0.1 can be replaced by localhost, as you can see in the examples in this chapter, which all use http://localhost/. But beware—if your machine does not have a webserver running, you are likely to end up at http://www.localhost.com/.

Creating a CGI Directory

Once Apache is installed, a CGI directory must be created and configured. To learn how to do this, see http://httpd.apache.org/docs-2.0/howto/cgi.html. The location of this directory will vary from machine to machine, but common Unix locations are /usr/local/apache/cgi-bin/ and /home/httpd/cgi-bin/ . Your mileage may vary on this location, however.  

Writing CGI Programs

Now that Apache is installed and the CGI directory is configured, it is time to write some CGI scripts. We often say that writing CGI in Perl is so easy—if you can print “hello, world!” you are halfway there! We will illustrate this with our first example.

All the examples in this chapter will be placed in the directory that /cgi-bin/ points to. So, for instance, if we write a program named foo.pl and place it in our CGI bin directory, we can execute the program by loading this URL in our browser:

http://localhost/cgi-bin/foo.pl

“hello, world!” in CGI

Our first example will be a program that sends “hello, world!”, our zen-like greeting, to the browser:

#!/usr/bin/perl – w
# hello.pl

use strict;

print "Content-Type: text/plainn";
print "n";
print "hello, world!n";

If this program were located in the CGI directory and had the proper executable permissions (755 in the Unix world), we could view the result by loading http://localhost/cgi-bin/hello.pl in our browser. It would produce this response:

The first few lines of this program are familiar. Then we see the statement

print "Content-Type: text/plainn";

This print is very important—it prints the content type of the information that follows. This is sent to the client in what is known as the HTTP header. The header is the mechanism the server uses to inform the client what kind of information it is sending. One piece of
infor mation in the header is the type of content—here we are telling the client that what follows is to be treated as plain text, and to deal with it appropriately which is normally by displaying it in fixed width font.

After we print the content type, we see this line:

print "n";

This prints a blank line. Printing a blank line is very important! The header must be followed by a blank line so that the client knows that the header is complete and the following information is the body, or the main part of the information that is sent. If we forget to print the blank line, we will normally get a “Server Error” message on the browser.

Then we print the message that we want the world to see:

print "hello, world!n";

What to Do If Things Go Wrong

If you don’t see the warm, friendly greeting in your browser, then you might suspect something is not working correctly. There are many things that can go wrong with CGI scripts; here are some things you can do to troubleshoot the problem:

  1. The file must be located in the directory in which Apache is looking. Double-check that it is, and if not, move it over to the appropriate place and try again.
  2. Make sure the permissions on the file are set so that the server can execute the program. In Unix, the permissions are usually set to 755, so chmod the file and test it again. 
     
  3. The script might have a syntax error (hey, it happens!). An easy way to check this is to execute the program on the command line using the -c option:

    $ perl -c hello.pl
    hello.pl syntax OK

This should tell you that the syntax is OK. If not, fix the problem and try again.

  • That blank line is really important—if you forgot to print it a server error is the normal result. A simple way to make sure there is the all-important-gotta-have-it blank line, simply execute the program from the command line:

    $ perl hello.pl
    Content-Type: text/plain

    hello, world!

Do you see the blank line? If not, print that extra n .

  1. Make sure that content type is something expected. For this program, it should be text/plain .
  2. It is possible that Apache is not configured properly. This is harder to fix—you will have to read through the Apache configuration documents to troubleshoot this problem. 
     
  3. When all else fails, have a look in Apache’s error log file (normally found in a directory named logs in the file named error_log ). Apache reports errors to this file—it might tell you why the script failed.

If there is still a problem that you can’t figure out, head over to www.perlmonks.org. This is a great website created as a place where Perl programmers can ask other Perl programmers questions and expect polite, useful answers. Emphasis on polite! There are many places on the web where a question can result in a fiery response—Perl Monks is not one of them.

The CGI Environment

When the webserver executes a CGI program, it makes available to that program a considerable amount of information through exported environment variables. To grab the environment variables in a Perl program, we look in %ENV . Here is a program showing several important things that the program knows about the client requesting the CGI program:  

#!/usr/bin/perl -w
# env1.pl

use strict;

print "Content-Type: text/plainn";
print "n";

print "your hostname is:      $ENV{REMOTE_ADDR}n";
print "your outbound port is: $ENV{REMOTE_PORT}n";
print "your browser is:       $ENV{HTTP_USER_AGENT}n";

If we point our browser to http://localhost/cgi-bin/env1.pl, we would see

As this program shows, the server knows the Internet address of the client, the port number that the client is coming from on their machine, and the web browser they are using.

You may be wondering what other environment variables that the server has available. This program will display them to you:

#!/usr/bin/perl -w
# env2.pl

use strict;

print "Content-Type: text/plainn" ;
print "n";

foreach (sort keys %ENV) {
    print "$_ = $ENV{$_}n";
}

Loading http://localhost/cgi-bin/env2.pl will display all our environment variables as shown in this example:

Generating HTML

Our CGI programs will not normally print plain text. Instead, they will print HTML to the client and the browser will render that text appropriately. To tell the client that our CGI program is generating HTML is as simple as changing the Content-Type :  

#!/usr/bin/perl -w
# env3.pl

use strict;

print "Content-Type: text/htmln";
print "n";  

foreach (sort keys %ENV) {
    print "$_ = $ENV{$_}";
    print "<br />";
}

Changing the content type to text/html tells the browser that what follows is HTML and to handle the text as it would any other HTML. Note that while we are printing the environment variables and their values, we don’t need to print newline characters since the browser treats those characters in HTML as simple whitespace characters. To get the line break we want we must print the break tag <br /> .

If we look at http://localhost/cgi-bin/env3.pl, we might see

Since we can generate arbitrary HTML, let’s make this output a bit more readable by putting it in a table:

#!/usr/bin/perl -w
# env4.pl

use strict;

print "Content-Type: text/htmln";
print "n";

print "<table border="1">";
foreach (sort keys %ENV) {
    print "<tr><th>$_</th><td>$ENV{$_}</td>";
}
print "</table>";

Ah, that’s better (http://localhost/cgi-bin/env4.pl ):

Take a moment to view the output from the program that the browser sees and renders by viewing the source: select the View menu on the browser and pull down to Source (it might be Page Source or View Source, depending on your browser). This pops up a window that contains the HTML source code, which is the output from the CGI script. Handy tool, this View ➤ Source—the web is what it is in large part due to the fact that you can see the HTML that others create. This is an excellent way to learn what it takes to code good web pages.

Introducing CGI.pm

As we can see from these examples, writing CGI programs is straightforward: generate standard output with print() function calls, remembering to start the output with the Content-Type line followed by a blank line (don’t forget the blank line!). That blank line is followed by what ever we want to display on the browser: text, HTML, etc.  

We will now introduce one of the most popular Perl modules (perhaps the most popular module): CGI.pm . This module, written by Lincoln Stein, makes it easy for us to generate HTML and process form data (we’ll talk about form data a little later in this chapter) by providing some helpful methods.

Let’s jump right into this module by looking at an example. First, we will present a program to generate a web page by printing plain HTML without the use of CGI.pm , then we’ll see exactly the same web page generated by a program using CGI.pm .

Here is the non- CGI.pm version:

#!/usr/bin/perl -w
# html1.pl

# this program generates HTML without the use
# of CGI.pm

use strict;

print "Content-Type: text/htmln" ;
print "n";
print "<html>n";
print "<head>n";
print "<title>Generating HTML</title>n"; print "</head>n";
print "<body>n";
print "<h1>Now Is:</h1>n";
print "<p>n";
print "The current date and time is: ";

print scalar(localtime);

print "</p>n";
print "<hr />n";
print "<h1>Our CGI Scripts</h1>n";
print "<p>n";
print "By the time this chapter is over, you will write all of n";
print "these scripts:n";

print "<br />$_n" foreach <*.pl>;

print "</p>n";
print "<h1>Go Here For Excellent Books!</h1>n";
print "<p>n";
print "Check out the n";
print "<a href="
http://www.apress.com/">Apress Home Page</a>.n";
print "</p>n";
print "</body>n";
print "</html>n";

Most of this code is simply printing HTML. Let’s look at two lines of this program in detail. First:

print scalar(localtime);

The localtime() function called in scalar context returns a nice readable string showing the current date/time stamp on the computer.1 This program prints that string to standard output, which will be displayed in the body of the browser. The next line of interest is

print "<br />$_n" foreach <*.pl>;

This code uses the expression modifier form of the foreach loop, looping through all files that match the glob pattern *.pl . Each of the files is printed with a preceding <br /> tag.

Loading this page (http://localhost/cgi-bin/html1.pl) into your browser should display something that resembles the following:

As you can see, all those print() functions with the double quotes, newlines, and semicolons make the code a bit hard to read. This program might be better written with a here-document as shown in this example ( html2.pl ):

#!/usr/bin/perl -w
# html2.pl

# this program generates HTML without the use
# of CGI.pm – this time with a here document

use strict;

print "Content-Type: text/htmln";
print "n";

print <<EOHTML;
<html>
<head>
<title>Generating HTML</title>
</head>
<body>
<h1>Now Is:</h1>
<p>
The current date and time is:
EOHTML

print scalar(localtime);

print <<EOHTML;
</p>
<hr />
<h1>Our CGI Scripts</h1>
<p>
By the time this chapter is over, you will write all of
these scripts:
EOHTML

print "<br />$_n" foreach <*.pl>;

print <<EOHTML;
</p>
<h1>Go Here For Excellent Books!</h1>
<p>
Check out the
<a href="http://www.apress.com/">Apress Home Page</a>.
</p>
</body>
</html>
EOHTML

Notice that the here-documents are useful when printing static text. However, when we print scalar(localtime) and loop through all the .pl files, we need to make sure those executable statements are outside of a here-document.

This content can be generated by using CGI.pm and its methods. First, let’s look at the program ( cgi1.pl ) and then we’ll step through the code line by line:

#!/usr/bin/perl -w
# cgi1.pl

# this program generates HTML with the use
# of CGI.pm

use strict;
use CGI ‘:standard’;

print header();
print start_html(‘Generating HTML’);
print h1(‘Now Is:’);
print p(‘The current date and time is:’, scalar(localtime));
print hr();
print h1(‘Our CGI Scripts’);

my $file_listing = ”;
$file_listing .= "<br />$_" foreach <*.pl>;

print p(‘By the time this chapter is over, you will write all of’,
        ‘these scripts:’, $file_listing);
print h1(‘Go Here For Excellent Books!’);
print p(‘Check out the’,
        a({ href => ‘http://www.apress.com/’ }, ‘Apress Home Page’));
print end_html();

The first part of this program is like any other Perl program: the shebang followed by optional comments followed by use strict; . Then we see the line that makes the use of CGI.pm possible:

use CGI ‘:standard’;

This statement looks slightly different than other use statements we have seen since there is the additional ‘:standard’ . This string tells CGI.pm that we want to use the methods in the module without having to call them using an object. In other words, if we use the :standard mode, we can call the header() method like this:

print header();

instead of like this:

my $q = new CGI;
print $q->header();

The header() method is very convenient—it prints the header (clever name, eh?). In other words, in generates the text

Content-Type: text/html

Yep, there is a blank line after the Content-Type line, so the header() generates that all-important, gotta-have-it blank line for us, so we don’t have to remember to do it ourselves. Then we see a method that generates the start of the HTML:

print start_html(‘Generating HTML’);

This invocation of the start_html() method generates this HTML:

<html><head><title>Generating HTML</title> </head><body>

Notice that the argument to start_html() is placed between the <title>…</title> tags for us.

Next up is

print H1(‘Now Is:’);

This method generates the <h1>…</h1> tags with the argument to the method placed between the open and close tags.

<h1>Now Is:</h1>

Then we see

print p(‘The current date and time is:’, scalar(localtime));

Like h1() , p() generates <p>…</p> with the argument(s) between the tags. This method has two arguments—a string and a function call comma separated. CGI.pm is smart enough to take these two arguments and concatenate the string and the function return value together with a space automatically added to separate them. Therefore, this method generates some thing that resembles

<p>The current date and time is: Fri Jun 18 19:02:48 2004</p>

The next method:

print hr();

generates <hr /> . This is similar to h1() and p() .

A couple of lines later we see this code:

my $file_listing = ” ;
$file_listing .= "<br />$_" foreach <*.pl>;

print p(‘By the time this chapter is over, you will write all of’,
        ‘these scripts:’, $file_listing);

This creates a variable $file_listing that will be all the Perl scripts in the current direc tory. For each Perl file, we append a <br /> tag along with the file’s name to $file_listing . That variable is then included as an argument to p() . This generates the text:

<p>By the time this chapter is over, you will write all of these scripts: <br />cgi1.pl<br />cgi2.pl<br />dynamic.pl<br /> env1.pl<br />env4.pl <br /> form.pl<br />html1.pl<br />html2.pl<br />widgets.pl</p>

Next is a call to p() that includes a call to a() :

print p(‘Check out the’,
        a({ href => ‘http://www.apress.com/’ }, ‘Apress Home Page’));

This method generates

<p>Check out the <a href="http://www.apress.com/">Apress Home Page</a></p>

We will discuss the first argument to the a() method in detail a little later in this chapter. Finally, we see

print end_html();

This generates the ending HTML tags:

</body></html>

Conventional Style of Calling Methods

The cgi1.pl program has several different print() function calls—one for each method that produces HTML. This is way too many print() function calls. The conventional style of using CGI.pm is to combine all the prints into one as shown in cgi2.pl :  

#!/usr/bin/perl -w
# cgi2.pl

# this program generates HTML with the use
# of CGI.pm using the conventional style

use strict;
use CGI ‘:standard’;

print
    header(),
    start_html(‘Generating HTML’),
    h1(‘Now Is:’),
    p(‘The current date and time is:’, scalar(localtime)),
    hr(),
    h1(‘Our CGI Scripts’);

my $file_listing = ”;
$file_listing .= "<br />$_" foreach <*.pl>;

print
     p(‘By the time this chapter is over, you will write all of’,
        ‘these scripts:’, $file_listing),
     h1(‘Go Here For Excellent Books!’),
     p(‘Check out the’,
       a({ href => ‘http://www.apress.com/’ }, ‘Apress Home Page’)),
     end_html();

This generates exactly the output as the other examples we have seen before. Note how all the methods are arguments to a single print() function call. This is considered by many to be more readable than having a separate print() for each method call—if you agree then feel free to use this style.


Note  A word of caution: if the h1() method, for instance, were followed by a semicolon instead of a comma (a very common mistake!):

h1(‘Here Is An Example Web Page’);

that semicolon would terminate the print() , so the following methods ( p() , hr() , etc.) would not be printed. It is our experience that if only a portion of the web page is displayed, it is often the result of a misplaced semicolon.


CGI.pm Methods

There are basically two types of CGI.pm methods: those that generate only one tag, and those that generate more than one tag.  

Methods That Generate More Than One Tag

We have seen a couple of examples of methods that generate more than one tag. One was start_html() , which generates <html><head><title>…</title></head><body> . This method is used to generate all the HTML that is at the top of the HTML content. The way we invoked the method was with one argument, and that argument was understood by the method to be the title of the web page and was placed between the <title>…</title> tags.  

Named Parameters

There is another way to invoke the method: using named parameters. Since start_html() generates the <body> tag, we would like to be able to easily set certain attributes of that tag when we invoke the method. Here is an example of giving our web page a title, background color, and text color:

  start_html(
     
-title   => ‘Generating HTML’,
     
-bgcolor => ‘#cccccc’,
      -text    => ‘#520063’
  ),

This sets the title to “Generating HTML”, the background color to a nice gray, “#cccccc”, and the text color to an excellent shade of purple, “#520063.”2

There are many different named parameters available to start_html() —check out perldoc CGI for a complete list.

Methods That Generate One Tag

We saw several examples of methods that generate one tag including h1() , p() , and hr() . These methods generate the tags that share their name: h1() produces <h1>…</h1>, p() produces <p>…</p>, and hr() generates <hr />  

There are many other examples of these methods—more-or-less one for each tag avail able in HTML. Examples include i() (<i>…</i>), b() (<b>…</b>), li() (<li>…</li>), and many others.

Providing Attributes

Many of these tags have optional attributes. Providing attributes to these methods involves passing an anonymous hash containing the attributes and their values as the first argument to the method. For instance, let’s say we want the <h1> tag to be aligned in the center of the page; we would call the h1() method in this fashion:

h1({ align => ‘center’}, ‘Here Is An Example Web Page’),

which generates

<h1 align="center">Here Is An Example Web Page</h1>

Providing more than one attribute is simply a matter of specifying more than one key/value pair to the anonymous hash. This method invocation generates an <hr /> tag with a pixel size of 10 and no shading set:

hr({ size => 10, noshade => 1 }),

which generates

<hr size="10" noshade="1" />

Now we see the magic involved in the a() method that we have seen before.

a({ href => ‘http://www.apress.com/’ }, ‘Apress Home Page’),

generates

<a href="http://www.apress.com/">Apress Home Page</a>

Processing Form Data

CGI scripts don’t merely generate HTML—they usually do some back-end, server-side processing. One major category of such processing is to gather information from the user by grabbing the data posted from a form. A form is a collection of widgets that allow the user to input data, select some options, click buttons, etc.

Most of us have filled out forms on the web: when we have purchased books from an online store, filled out a survey, or created an account at www.slashdot.org.

Forms are created by using <form> through </form> surrounding one or more widgets. The most important attribute to the <form> tag is the action, or CGI script, that is to be invoked when the user submits the form by clicking the submit button.

Here is an example of a simple form that will allow the user to enter their name and age ( form.html ):

<html>
  <head>
    <title>A Simple Form</title>
  </head>
  <body>
    <h1>Please Enter Your Name</h1>
    <form action="
http://localhost/cgi-bin/form.pl">
      First name: <input type="text" name="firstname">
      <br>
      Last name: <input type="text" name="lastname">
      <br>
      <input type="submit">
    </form>
  </body>
</html>

This generates the form shown here (after a user enters some text):

The CGI program associated with this form, as indicated by the action= attribute, is http://localhost/cgi-bin/form.pl. This program will obtain the information entered by the user into the form and process it. The key to this program will be the important param() method.

The param() Method

The param() method is invoked in one of two ways: with no arguments and with one argument.

When invoked with no argument, the param() method returns back a list of all the parameters in the form. A parameter is simply the name of the widget indicated by the name= attribute. In the form.html example previously, we see that there are two named widgets: firstname and lastname . The param() method invoked with no arguments would produce the list

(‘firstname’, ‘lastname’)

When invoked with one argument, the param() method returns back the value of that parameter. Therefore, if the user enters their first name as “Frodo”, then param(‘firstname’) returns “Frodo”. If they enter their first name as “Bilbo”, the method returns back “Bilbo”. Likewise, param(‘lastname’) returns back the last name entered by the user into the form.

And now the code to process the form ( form.pl ):

#!/usr/bin/perl -w
# form.pl

use strict;
use CGI ‘:standard’;

my @params    = param();
my $firstname = param(‘firstname’) || ‘you have no first name!’;
my $lasthame  = param(‘lastname’)  || ‘you have no last name!’;

print
    header(),
    start_html(
        -title   => ‘Welcome!’,
        -text    => ‘#520063’
    ),
    h1("Hello, $firstname $lastname!"),
    end_html();

This program produces this result (assuming the user enters “Frodo” for the first name and “Baggins” for the last name):

The important lines in this program are

my @params    = param();
my $firstname = param(‘firstname’) || ‘you have no first name!’;
my $lastname  = param(‘lastname’)  || ‘you have no last name!’;

The first statement assigns @params a list of all the parameters, or widget names, in the form that was posted. In our example, they are ‘firstname’ and ‘lastname’ . This line is followed by two assignments that are very similar. The first assigns $firstname the value that was entered into the name field in the form. The || operator ensures that the variable $firstname has a meaningful value—if the user does not enter a name, param(‘firstname’) will be the empty string, which is false, so the || will evaluate the operand on the right and assign ‘you have no first name!’ to $firstname . A similar assignment is made to $lastname .

Then we see the two variables printed in the print() function.

Dynamic CGI Program

The example we just looked at had an HTML file that was separate from the CGI program. The HTML was stored in form.html and the CGI program was stored in form.pl . This is known as a static CGI program—loading in a static HTML file that has as its action a separate CGI program.  

Sometimes keeping the HTML and CGI program in separate files creates a situation that is more difficult to manage. Perhaps it is better to have the HTML generated by the same program that processes the posted data. Enter a dynamic CGI program.

A dynamic CGI program does one of two things:

  1. If there is posted data, process the data.
  2. If there is no posted data, build the form.

Recall that the param() method returns a list of all the parameters posted by the form. If this list is empty, then the CGI program was invoked with no parameters. Therefore, a dynamic CGI program has this general form:

if (param()) {
    # the program was invoked with parameters,
    # process the posted data
} else {
    # the program was invoked with no parameters,
    # build the form
}

Here is an example of a dynamic CGI program that implements the scenario we saw previously: it either builds a form that accepts a user’s name and age, or it processes the form data (dynamic.pl):

#!/usr/bin/perl -w
# dynamic.pl

use strict;
use CGI ‘:standard’;

if (param()) {
    # we have parameters, so process the form data

    my @params    = param();
    my $firstname = param(‘firstname’) || ‘you have no first name!’;
    my $lastname  = param(‘lastname’)  || ‘you have no last name!’;

    print
        header(),
        start_html(
            -title  => ‘Welcome!’,
            -text   => ‘#520063’
        ),
        h1("Hello, $firstname $lastname!"),
        end_html();

} else {
    # no parameters, so build the form

    print
        header(),
        start_html(‘A Simple Form’),
        h1(‘Please Enter Your Name’),
        start_form(),
        ‘First name: ‘,
        textfield(-name => ‘firstname’),
        br(),
        ‘Last name: ‘,
        textfield(-name => ‘lastname’),

        br(),
       
submit(),
        end_form(),
        end_html();

}

This program generates the same form as form.html and it processes the form the same as form.pl . Note that it builds the form using CGI.pm methods. There are a few methods used that are worth noting. First:

start_form(),

The start_form() method builds the beginning <form> tag. The action can be specified with { -action => ‘/cgi-bin/whatever.pl’ } , but we are using the default action, which is the same CGI program that built the form. The form is eventually closed with

end_form(),

The text form widget is created with

textfield(-name => ‘lastname’),

The textfield() method is one of many methods that are used to create form widgets. This chapter is not meant to be an exhaustive study of forms and form widgets, but there is a method for each different type of widget available. For a complete list, see perldoc CGI .

Let’s Play Chess!

It’s time to roll all the topics we have discussed into a single example. This CGI script will be a web implementation of the chess program, chess.pl , that we discussed in Chapter 11. Since we are playing chess on the web, we’ll call this CGI script webchess.pl . This program will illus trate that with just a little bit of additional code we can web-enable a program that we wrote for the shell.  

Before we look at the program, we need to talk about an important concept when it comes to CGI. A CGI script is stateless. That means the CGI script in and of itself cannot remember anything about the most recent execution, or state, of the script. As a result, we will somehow have to remember the recent state of the chessboard so that we can pick up the game from the last move the user made. This is different than the chess.pl program—each move was made within the same execution of the program, so chess.pl always knew the state from move to move.

We will keep track of the state of the chessboard in a file named webchess.dat . This file will be an eight-line file, with each line being one row on the board. Each row will have its eight pieces, colon separated. Here is the initial state of the chessboard:

BR:BN:BB:BQ:BK:BN:BB:BR BP:BP:BP:BP:BP:BP:BP:BP
:::::::
:::::: :
:::::::
:::::::
WP:WP:WP:WP:WP:WP:WP:WP WR:WN:WB:WQ:WK:WN:WB:WR

We can see that the first, second, seventh, and eighth rows have pieces. The middle four rows are empty—if two colons are right next to one another that means that square does not have a piece on it.

If webchess.pl is going to keep its state in webchess.dat , we will need some code to read from the data file and to write to the data file. These operations are placed within two func tions: read_in_chessboard() , which will, you guessed it, read in the chessboard. The equally well-named function write_out_chessboard() will write it out.

It’s time to jump into the code:

#!/usr/bin/perl -w
# webchess.pl

use strict;
use CGI ‘:standard’;

my @chessboard = read_in_chessboard();

# grab the posted data, if any:
my $start = param(‘start’) || ”;
my $end   = param(‘end’)   || ”;

my $startx = ”;
my $starty = ”;
my $endx   = ”;
my $endy   = ”;

# time to make our move!
if ($start and $end) {
    if ($start =~ /^s*([1-8]),([1-8])/) {
        $startx = $1 – 1;
        $starty = $2 – 1;
    }
    if ($end =~ /^s*([1-8]),([1-8])/) {
        $endx = $1 – 1;
        $endy = $2 – 1;
    }
    if ($startx ne ” and $starty ne ” and
        $endx ne ”   and $endy ne ” ) {
        # put starting square on ending square
        $chessboard[$endy]->[$endx] = $chessboard[$starty]->[$startx];
        # remove from old square
        undef $chessboard[$starty]->[$startx];
       
# we have changed the chessboard, so write
        # back out
        write_out_chessboard(@chessboard);
    }
}

# time to print to the browser
print
    header(),
    start_html(‘Web Chess’),
    h1(‘Web Chess’);

# start the table that will contain the board
print ‘<table>’;

# loop, printing each piece
foreach my $i (reverse (0..7)) { # row
    print ‘<tr>’;
    foreach my $j (0..7) {       # column
        print ‘<td>’;
        if (defined $chessboard[$i]->[$j]) {
            print $chessboard[$i]->[$j];
        } elsif ( ($i % 2) == ($j % 2) ) {
            print "..";
        }
        print ‘</td>’;
    }
    print "</tr>";    # end of row
}

# we are done with our table
print ‘</table>’;

# print a form for the next move
# and end the html
print
    hr(),
    start_form(),
    ‘Starting square [x,y]:’,
    textfield(-name => ‘start’),
    br(),
    ‘Ending square [x,y]:’,
    textfield(-name => ‘end’),
    br(),
    submit(),
    end_form(),
    end_html();

### function definitions ###

sub read_in_chessboard {
    # this function opens webchess.dat and builds
    # the chessboard
    # an example line from webchess.dat is:
    # BR:BN:BB:BQ:BK:BN:BB:BR

    # this is our local copy of the chessboard,
    # we’ll return this later
    my @cb;
    open FH, ‘<‘, ‘webchess.dat’;

    foreach my $i (0..7) {
        my $line = <FH>;
        # split the line on a : or any whitespace
        # which will take care of the n at the
        # end of the line
        my @linearray = split /[:s]/, $line;
        # $#linearray should be 7!
        foreach my $j (0..$#linearray) {
            # if the text between the colons is
            # not the empty string, we have a piece,
            # so assign it to our chessboard
            if ($linearray[$j]) {
                $cb[$i]->[$j] = $linearray[$j];
            }
        }
    }
    close FH;

    # time to return back the chessboard
    return @cb;
}

sub write_out_chessboard {
    # the chessboard is passed in as our
    # argument
    my @cb = @_;

    # write the chessboard to webchess.dat
    # so that each piece on a row is colon separated
    open FH, ‘>’, ‘webchess.dat’;
    foreach my $i (0..7) {
        foreach my $j (0..7) {
            if (defined $chessboard[$i]->[$j]) {
                print FH $chessboard[$i]->[$j];
            }

            if ($j < 7) {
                print FH ‘:’;
            }
        }
        print FH "n";
    }
}

Wow, that’s a lot of code. Let’s look at it a chunk at a time. We’ll start at the bottom of the program with the functions to read from and write to the input data file. First, the relevant code in read_in_chessboard() :

  # this is our local copy of the chessboard,
  # we’ll return this later
  my @cb;
  open FH, ‘<‘, ‘webchess.dat’;

  foreach my $i (0..7) {
      my $line = <FH>;
      # split the line on a : or any whitespace
      # which will take care of the n at the
      # end of the line
      my @linearray = split /[:s]/, $line;
      # $#linearray should be 7!
      foreach my $j (0..$#linearray) {
          # if the text between the colons is
          # not the empty string, we have a piece,
          # so assign it to our chessboard
          if ($linearray[$j]) {
              $cb[$i]->[$j] = $linearray[$j];
          }
      }
  }
  close FH;

  # time to return back the chessboard
  return @cb;

This function creates a my() variable @cb that will hold a local copy of the chessboard. The input data file is opened in read mode. Then, for the eight rows on the board, a line of text is read from the input file and split on either the colon or whitespace character. This split() will break the line into eight parts—the pieces for that row. Then we loop for each square on the row—if there is a piece in the square (no piece will be represented by the empty string, which is false, so any true value indicates a piece is present), the square on the chessboard is assigned the piece. After each square in each row is assigned, the input file is closed and the chessboard is returned to whoever called it.

Then we see the function to write the chessboard back out to the file:

sub write_out_chessboard {
    # the chessboard is passed in as our
    # argument
    my @cb = @_;

    # write the chessboard to webchess.dat
    # so that each piece on a row is colon separated
    open FH, ‘>’, ‘webchess.dat’;
    foreach my $i (0..7) {
        foreach my $j (0..7) {
            if (defined $chessboard[$i]->[$j]) {
                print FH $chessboard[$i]->[$j];
            }
            if ($j < 7) {
                print FH ‘:’;
            }
        }
        print FH "n";
    }
}

This function opens the data file in write mode. It then loops eight times, once for each row. For each row, it loops eight times, once for each square in the row. If there is a defined value, it is printed (it will either be the piece such as “WB” or the empty string). Then a colon is printed after all but the last square on the row. After the row is printed, we end the line with n . After all rows are printed, the output file is closed.

Now, on to the main code in the program. First, we create a variable to hold the chessboard by calling the function that reads from the data file:

my @chessboard = read_in_chessboard();

Then, we read in the posted data, if there is any. This data will be the starting and ending coordinates (such as 4,2). Note that if there is no posted data for either the start or end square, the variable will be assigned the empty string:

# grab the posted data, if any:
my $start = param(‘start’) || ”;
my $end   = param(‘end’)   || ”;

Now that $start and $end have the starting and ending square if they were entered, let’s break those up into the X and Y coordinates. Note that we first check to make sure we have both a starting and ending pair, otherwise there is no reason to do this work:

my $startx = ”;
my $starty = ”;
my $endx   = ”;
my $endy   = ”;

# time to make our move!
if ($start and $end) {
    if ($start =~ /^s*([1-8]),([1-8])/) {
        $startx = $1 – 1;
        $starty = $2 – 1;
    }
    if ($end =~ /^s*([1-8]),([1-8])/) {
        $endx = $1 – 1;
        $endy = $2 – 1;
    }
    if ($startx ne ” and $starty ne ” and
        $endx ne ”   and $endy ne ” ) {
        # put starting square on ending square
        $chessboard[$endy]->[$endx] = $chessboard[$starty]->[$startx];
        # remove from old square
        undef $chessboard[$starty]->[$startx];

        # we have changed the chessboard, so write
        # back out
        write_out_chessboard(@chessboard);
    }
}

It is important to note that we are doing several checks here. First, we check to see if the user entered any coordinates. Then, we make sure that we have good values for the X and Y values for both the starting and ending square. Only when we determine that we have to make a move do we modify the chessboard. And only when the chessboard has been modified do we write the chessboard back out to the data file.

Next, we start printing to the browser. First, the beginning HTML stuff:

# time to print to the browser
print
    header(),
    start_html(‘Web Chess’),
    h1(‘Web Chess’);

Then we print the chessboard. It is almost identical to the code that prints the chessboard in chess.pl except that we are going to put this into an HTML table, so we have to print the necessary table tags:

# start the table that will contain the board
print ‘<table>’;

# loop, printing each piece
foreach my $i (reverse (0..7)) { # row
    print ‘<tr>’;
    foreach my $j (0..7) {       # column
        print ‘<td>’;

        if (defined $chessboard[$i]->[$j]) {
            print $chessboard[$i]->[$j];
        } elsif ( ($i % 2) == ($j % 2) ) {
            print "..";
        }
        print ‘</td>’;
    }
    print "</tr>";    # end of row
}

# we are done with out table
print ‘</table>’;

First, we start the table. Then, for each row, we print <tr> to start the row. Then for each column in the row we wrap either the chessboard piece, "..", or nothing at all inside <td>..</td> . Then we end the row with </tr> . Finally, the table is finalized with </table> .

Next we see the code to print the form to read in the user’s move:

# print a form for the next move
# and end the html
print
    hr(),
    start_form(),
    ‘Starting square [x,y]:’,
    textfield(-name => ‘start’),
    br(),
    ‘Ending square [x,y]:’,
    textfield(-name => ‘end’),
    br(),
    submit(),
    end_form(),
    end_html();

Whew! That was a long program. Enough talk—now it is time to play chess. Load http://localhost/cgi-bin/webchess.pl into your browser and you will see

Let’s make an opening move: the white pawn from 4,2 to 4,4:

Improvements We Can Make

There are many enhancements we can make to this script beyond the fact that there are no built-in chess rules. This program is a good start in a chess game, but we should consider the following:

  1. More error checking: Error checking is good, especially for web programs—the last thing we want is a user to come to our website, run a program, and have that program fail. One thing we should do is handle the failure of opening the file when we read and when we write. This requires more than simply using the die() function because the output from die() goes to standard error, which does not end up in the browser. There are several ways to address this including a helpful module called CGI::Carp .
  2. The design of the web page: This page is OK, for geeks. But for consumption by the general public, we will want a slick, professional-looking website that is easy to navigate and pleasant to look at. This requires the help of a graphic artist and web designer—more art than HTML. To illustrate the difference, check out www.bware.org—that is a web page designed by a geek. Compare that to www.onsight.com, which was developed by a graphic artist.
     
  3. An even more appealing design: Speaking of an appealing web page, it would be nice to replace those letters with pictures. Wouldn’t it be cool if instead of seeing BP we saw a picture of a black pawn? Again, we need an artist! 
     
  4. Every user gets their own game: As this program is written, there is only one game. If you are playing, and you make a move, your friend can run the program and see the result. He can then make a move, which you will see the next time you run the program. Then another friend can come along, run the program, and you and your initial friend would see this new move. Not such a great thing. To resolve this we could add authentication with a username/password and store a unique copy of the game state for each user.

Hopefully this example has shown how easy it is to write CGI scripts in Perl. By adding a little bit of code, we were able to transform a program that ran in a shell to a program that has a web interface. Not only was it easy, it was fun! Speaking of fun, it’s time to play some chess . . .

What We Did Not Talk About

Since this chapter is not meant to cover everything there is to know about CGI programming (if it were, it would end up being its own book), there are several things that we did not talk about. They are very important topics that you should take time to learn about, eventually:

  • Web security: Running a webserver that is connected to the Internet allows anyone who can reach your site to run your program. If the program is written insecurely, then anyone with the mind to can execute that insecure program, possibly doing nasty things. And there are individuals in the world that like to try to break CGI scripts and crack into machines—that is the reality of the world we live in.4 The good news is that it is possible to write secure CGI programs applying just a few techniques. For a thorough discussion of this topic, check out CGI Programming with Perl by Scott Guelich, et al. (O’Reilly & Associates, 2000).
  1. HTML: This chapter is not meant to be a primer on HTML, so we did not discuss all the available tags and form widgets. There are many books and websites devoted to HTML—read one and learn all about it. Then check out Official Guide to Programming with CGI.pm written by Lincoln Stein, the author of CGI.pm , to see how to use CGI.pm to build any HTML you want.
  2. Many other features: There are lots of other features of CGI and HTTP that we didn’t cover including JavaScript, SSL, authentication, and mod_perl
     
  3. Database access: Most modern websites contain content that is generated dynamically by reading the data out of a database. In order to achieve any level of sophistication with Perl and databases, we need the excellent Perl module DBI . And that is the topic of the next chapter. 
     
  4. Templating: Most modern websites have a consistent look and feel. In other words, every page in the website has the same general layout—perhaps the same logo and links along the top, the same navigation links on the left side of the page, and the same information on the bottom of the page. If this were hard-coded for each page, then changing the look and feel of the website would require a change to every single CGI script on the website. The solution is to create a template, or a general layout for every page. Each CGI script would then use the template for the basic look of the page, then add the specific content for its purpose. Then, when the look and feel changes (it probably will, eventually), the changes are made in one place—the template—and they are immediately applied to every CGI program. Perl offers many ways to template your website including HTML::Template , the Template Toolkit, Mason, and Embperl.

Summary

CGI is the cornerstone of programming for the web, and Perl is the language to use to write CGI programs. In this chapter we discussed the CGI protocol, CGI.pm , forms, and form data. We learned that CGI programs are essentially a bunch of print() functions that generate standard output. We learned that CGI.pm can help make our life easier by providing helpful methods to generate this output.  

Form data can be processed using the param() function. Dynamic CGI scripts generate the form and/or process the form, depending on how they were invoked: without form data or with form data.

You now know enough about CGI programming to get started. So get started! And happy hacking.

Exercises

  1. Write a CGI script that asks a user to enter their name, address, and phone number. Respond to them with a nice message thanking them for filling out the form, and append their information to a file.
  2. Make the changes to webchess.pl that were made to chess.pl in the exercises at the end of Chapter 11.

1. In list context, localtime() returns something similar but different. See perldoc -f localtime for all the useful information that localtime() returns.

2. How excellent? Go to www.nwu.edu for a demonstration.

3. Brought to you by the fine folks at BDGI (www.bdgi.com).

4. These individuals are often called hackers, but that is a misuse of the term. A hacker is one who creates a useful program, usually quickly, in an artistic way–what many of us programmers aspire to be. A person who breaks into other computers is called a cracker.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye