How to maintain data in CGI Perl

Posted on 2004-09-13
Medium Priority
Last Modified: 2013-12-25
How can I maintain data inputed by user (i.e. username) throughout the session? I'm a java programmer and I'm just starting to program CGI Perl and I just wanted to know if there's something like session object in Perl that I could keep my data. Thanks
Question by:zorlac
  • 3
  • 2
LVL 85

Expert Comment

ID: 12049532
<input type=hidden name=username value= zorlac>
LVL 85

Expert Comment

ID: 12049568
LVL 14

Expert Comment

ID: 12049584
This is a basic, but fundamental issue when designing for CGI, because as you've already noticed, by default it's a stateless model. Each request-answer is self contained and doesn't intrinsically reference previous states. This can be quite frustrating if your're an application developer because applications maintain their own state as the user follows the application workflow. In Perl, PHP and other CGI environments it's up to the programmer to maintain this state - and to manage the workflow.

So, having said that rather pedantic preamble: to brass tacks. There are a good many ways this can be done.

Let's get the easy one over with:

use strict; # you always use strict, don't you?
use CGI; # use the Perl module called CGI.pm. Nowadays, this is standard with your perl installation, if not, it can be found at www.cpan.org

open STATE_FILE, "my_state_file.txt" || die ("Couldn't open STATE FILE: $!"); # open the file or complain
my $cgi = CGI->new(STATE_FILE); # create a new CGI object and read in the previous state's variables (the user's input)

# ... and then do some stuff

# and just before exiting your script:
open STATE_OUT, ">my_state_file.txt" || die ("Couldn't write STATE FILE: $!");
close STATE_OUT;

# ---- end 1 --------

Now this assumes you want to let CGI.pm handle all this for you. Personally, I don't use this method, because A) i like more control, B) i don't like the idea of saving state to a file, especially in the context of a highly-trafficked site that could be handling hundreds of transactions more-or-less simultaneously and C) i'm stubborn and like doing things myself!

So, a few other options:

As you no doubt know, workflow is usually initiated by a call to your script (let's call it "my_script.pl"), either through a form's "action" parameter or directly via a hyperlink. Parameters are passed to the script either via POST (from your form), or via GET (embedded in the URL). Either way, somewhere near the start of your script, you want to read these params in so that you can decide where in the workflow you are and what you're going to do next.

State can be controlled using any number of schemes. I often like to do something simple like using a parameter called "cmd" or "c" (to be somewhat more cryptic) and pass the "state" in the workflow. For example in a typical application you might:

1. display a "registration" form which is submitted
2. validate the from and send it back if it is incorrect
3. create a new record and show the user the login page
4. authenticate the user and toss them back the login page if they don't authenticate
5. show them the "goodies" once they've authenticated.

So here we have some distinct states in the workflow which can be summarized thusly:

The first call to this script would be to the registration page so our "cmd" variable would be "show_registration"
You could call the script via "my_script.pl?cmd=show_registration". When the script encounters this parameter it knows where in the workflow it's supposed to be and thus displays the registration form.

Hidden in the registration form would be the "cmd" parameter, presumably in a hidden field like this:
<form name="regForm" action="my_script.pl" method="POST"><input type="hidden" name="cmd" value="register_user">...form stuff</form>

So now when the user completes the form and hits "Submit" the cmd parameter is "register_user" and the perl scipt knows that it has to validate the form, and if it's valid, create the new user record and spit out the login page.

So on the Perl side, you create some kind of control structure that switches based on the cmd parameter:

use CGI; # why? because it's a darned convenient way of reading user input from forms.
# Alternatively you can require "cgi-lib". Google cgi-lib.pl for more info.
my $cgi = new CGI;
my %params = %{$cgi->Vars}; # read all the params from your input form into a hash in the form of $hash{name} = 'value';

# simplest method:
if ($params{'cmd'} eq 'show_registration') {
  # display the registration form.....
elsif ($params{'cmd'} eq 'register_user') {
  # validate the user and then spit out the login page if valid
#.... etc
else {
  # some default action, like show the main menu or whatever

There are plenty of ways you can do this, one favourite being to create a hash of function references.

But this might not be getting to your question. Or well, it's only part of it. What about keeping track of the user's input? Like a username?

The key here, if you want to avoid the CGI->save() route is to keep passing the parameters back to the script every time you call the script. It's not as cumbersome as you might think, especially if your script is form-driven (as opposed to URL driven).

Let's say the registration process has 3 pages. On the first page, the user creates a name and password, on the second and third pages she then enters additional information. How do you maintain the name and password so that the script knows that it's that particular user who is submitting pages 2 and 3 of the form? Simple: using Perl, re-embed the username and password into the form (or use cookies, but that's another story for another day).

Let's say that the user has created a username and password. We're on page 2 of the form, and we're creating the form using Perl:

# display page 2 of the form
print qq { <html><head><title>page 2</title></head><body> }; # hey, we want well-formed html here, right?
# Note the qq { } format - great for embedding HTML because you don't have to worry about escaping your " and '

# start the form, embed the "cmd" parameter
print qq { <form name="page2" action="my_script.pl" method="POST"><input type="hidden" name="cmd" value="regstration_2"> };

# now, embed the user's previous input (ie: the username and password). They go back into hidden fields.
print qq { <input type="hidden" name="username" value="$params{'username'}"> }; # the qq { } will automagically expand the variable
print qq { <input type="hidden" name="password" value="$params{'password'}"> };
# if there is more data you want to pass, then add it here using the same format as above
# and then add the "new" form fields, for example:
print qq { <p>Company name: <input type="text" name="company"></p> };
# etc... you get the picture
print qq { </form></body></html> };

Pretty simple, huh? Now, when the user hits the SUBMIT button, the old data (username and password) are sent to the script along with the new data.

Hope this helps!

Tom Auger

PS: one last note. Sometimes, to make your design more flexible you want to use an external file to define your forms, rather than embedding the whole darn thing in Perl, which makes it nearly impossible for other non-programmer designers to modify the forms. In that case you read in the external html file, spit out the appropriate MIME-headers and then the file contents using a loop and "print". But how do you embed user data in there?

use strict;
use CGI;
my $cgi = new CGI;
my %params = %{$cgi->Vars};

# print headers
print $cgi->header();

# now, read in the external "template" file and spit it out - we'll be re-using this, so let's create a function for it.
output_template("my_template_html_file.html", \%params);

# ...
sub output_template {
  my $template_file = shift;
  my $params_ref = shift;

  # read the file
  open TEMPL, $template_file || die ("Couldn't access the template file : $!");
  while (<TEMPL>) {
    # now, for each line of the template file, look for "special" tags that we define as looking like this: <*my_user_input_parameter>
    print $_;
  close TEMPL;

And our template form might look like this:
<html><head><title>templated form</title></head>
<form name="myForm" action="my_script.pl" method="POST">
<p><input type="text" name="username" value="<*username>"></p>

<p>Yes, your name is: <*username></p>
<p>And your password is: <*password></p>

So as the form is being read in by the output_template function in your Perl script, it's looking for those "special" tags that we've defined of the form <*paramter_name>. When it encounters one of them, it will automatically substitute in the actual value from the user's previous input (the stuff that's stored in the %params hash). If it doesn't match, a nice side benefit is that it substitutes with "" so either way the "special" tag never appears to people sniffing around your source code.

Whew! I need a beer.
LVL 14

Accepted Solution

tomaugerdotcom earned 750 total points
ID: 12049593
Jeez. And now for my next book.... sorry about the long-winded answer.

LVL 14

Expert Comment

ID: 12102243
Thanks for the grade of "B". I wonder what else I could have done to deserve an "A" - perhaps write a Master's Thesis?

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Active Directory replication delay is the cause to many problems.  Here is a super easy script to force Active Directory replication to all sites with by using an elevated PowerShell command prompt, and a tool to verify your changes.
A quick Powershell script I wrote to find old program installations and check versions of a specific file across the network.
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
In a recent question (https://www.experts-exchange.com/questions/29004105/Run-AutoHotkey-script-directly-from-Notepad.html) here at Experts Exchange, a member asked how to run an AutoHotkey script (.AHK) directly from Notepad++ (aka NPP). This video…

589 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question