NAME
CGI::Session - Perl extension for persistent session management
SYNOPSIS
# OO interface
use CGI::Session::File;
use CGI;
$cgi = new CGI;
$sid = $cgi->cookie("SID") || $cgi->param("sid") || undef;
$session = new CGI::Session::File($sid,
{ Directory => "/tmp" });
# Let's update the session id:
$sid = $session->id;
# Let's get the first name of the user from the form
# and save it in the session:
$f_name = $cgi->param( "f_name" );
$session->param( "f_name", $f_name );
# Later we can just initialize the user's session
# and retrieve his first name from session data:
$f_name = $session->param( "f_name" );
# tie() interface:
use CGI::Session::File;
use CGI;
$cgi = new CGI;
$sid = $cgi->cookie("SID") || $cgi->param("sid") || undef;
tie %session, "CGI::Session::File", $sid,
{ Directory => "/tmp" };
# Let's update the session id:
$sid = $session{ _session_id };
# Let's get the first name of the user from the form
# and save it in the session:
$f_name = $cgi->param( "f_name" );
$session{f_name} = $f_name;
# Later we can just initialize the user's session
# and retrieve his first name from session data:
$f_name = $session{f_name};
# Tricks are endless!
NOTE
As of this release there seems to be two completely different CGI::Session libraries on CPAN. This manual refers to CGI::Session by Sherzod Ruzmetov
DESCRIPTION
CGI::Session
is Perl5 library that provides an easy persistent session management system across HTTP requests. Session persistence is a very important issue in web applications. Shopping carts, user-recognition features, login and authentication methods and etc. all require persistent session management mechanism, which is both secure and reliable. CGI::Session
provides with just that. You can read the whole documentation as a tutorial on session management. But if you are already familiar with CGI::Session
go to the methods section for the list of all the methods available.
DISTRIBUTION
Latest distribution includes:
CGI::Session - base class. Heart of the distribution.
CGI::Session::File - driver for storing session data in plain files.
CGI::Session::DB_File - driver for storing session data in Berkeley DB files. This is for Berkeley Database 1.x and 2.x version. For versions higher than 2.x see CGI::Session::BerkeleyDB
CGI::Session::MySQL - driver for storing session data in MySQL tables.
INSTALLATION
You can download the latest release of the library either from http://www.CPAN.org or from http://modules.ultracgis.com/dist. The library is distributed as .tar.gz file, which is a zipped tar-ball. You can unzip and unpack the package with the following single command (% is your shell prompt):
% gzip -dc CGI-Session-2.6.tar.gz | tar -xof -
It should create a folder named the same as the distribution name except the .tar.gz
extension. If you have access to system's @INC folders ( usually if you are a super user in the system ) you should go with standard installation. Otherwise custom installation is the way to go.
STANDARD INSTALLATION
The library is installed with just like other Perl libraries, or via CPAN interactive shell (Perl -MCPAN -e install CGI::Session).
Installation can also be done by following below instructions:
After downloading the distribution,
cd
to the distribution folderIn your shell type the following commands in the order listed:
Perl Makefile.PL
make
make test
If the tests show positive results, type:
make install
CUSTOM INSTALLATION
If you do not have access to install libraries in the system folders, you should install the library in your own private folder, somewhere in your home directory. For this purpose, first choose/create a folder where you want to keep your Perl libraries. I use perllib/
under my home folder. Then install the library following the below steps:
After downloading the distribution,
cd
to the distribution folderIn your shell type the following commands in the order listed:
Perl Makefile.PL INSTALLDIRS=site INSTALLSITELIB=/home/your_folder/perllib
make
make test
If the tests show positive results, type:
make install
Then in your Perl programs do not forget to include the following line at the top of your code:
use lib "/home/your_folder/perllib";
or the following a little longer alternative works as well:
BEGIN {
unshift @INC, "/home/your_folder/perllib";
}
WHAT YOU NEED TO KNOW FIRST
As of version 2.6 CGI::Session offers two distinct interfaces: 1) Object Oriented and 2) tied hash access. In Object Oriented Interface you will first create an object of CGI::Session's derived class (driver) and manipulate session data by calling special methods. Example:
# Creating a new session object:
$session = new CGI::Session::File(undef,
{ Directory=>"/tmp" });
print "Your session id is ", $session->id();
In tied hash method, you will tie() a regular hash variable to the CGI::Session's derived class and from that point on you will be treating a session just hash variable. And Perl will do all other job for you transparently:
# Creating a new session object:
tie %session, "CGI::Session::File", undef, {
{ Directory=>"/tmp" };
print "Your session id is ", $session{_session_id};
In the examples throughout the manual I will give syntax and notes for both interfaces. Which interface to use is totally up to you. I personally prefer Object Oriented Interface, for it is full of features.
Also, somewhere in this manual I will talk about "SPECIAL NAMES" which are not accessible via param()
method for neither writing nor reading. But this rule differ in tied hash access method, where all those "SPECIAL NAMES" but few are writable
REFRESHER ON SESSION MANAGEMENT
Since HTTP is a stateless protocol, web programs need a way of recognizing clients across different HTTP requests. Each click to a site by the same user is considered brand new request for your web applications, and all the state information from the previous requests are lost. These constraints make it difficult to write web applications such as shopping carts, users' browsing history, login/authentication routines, users' preferences among hundreds of others.
But all of these constraints can be overcome by making use of CGI::Session
WORKING WITH SESSIONS
Note: Before working with sessions, you will need to decide what kind of storage best suits your needs. If your application makes extensive use of MySQL, Oracle or other RDBMS, go with that storage. But plain file or DB_File should be adequate for almost all the situations. Examples in this manual will be using plain files as the session storage device for they are available for all the users. But you can choose any CGI::Session::* driver available.
CREATING A NEW SESSION
To create a new session, you will pass CGI::Session::File a false expression as the first argument:
# OO interface
$session = new CGI::Session::File(undef,
{
Directory => "/tmp/sessions",
});
# tie() interface
tie %session, "CGI::Session::File", undef, {
Directory => "/tmp/sessions" };
We're passing two arguments, the fist one is session id, which is undefined in our case, and the second one is the anonymous hash
{
Directory => "/tmp/sessions",
}
which points to the locations where session files and their lock files should be created. You might want to choose a more secure location than I did in this example.
Note: second hashref argument is dependant on the driver. Please check with the driver manual.
If the session id is undefined, the library will generate a new id, and you can access it's value via id() method: (see methods)
# OO:
$sid = $session->id();
# tie():
$sid = $session{_session_id};
INITIALIZING EXISTING SESSIONS
We create new sessions when new visitors visit our site. What if they click on a link in our site, should we create another session again? Absolutely not! The sole purpose of the session management is to keep the session open as along as the user is surfing our site. Sometimes we might want to choose to keep the session for several days, weeks or months so that we can recognize the user and/or re-create his preferences if required.
So how do we know if the user already opened a session or not? At their first visit, we should "mark" the users with the session id we created in the above example. So, how do we "mark" the user? There are several ways of "marking".
IDENTIFYING THE USER VIA CGI QUERY
One way of doing it is to append the session id to every single link in the web site:
# get the session id...
my $sid = $session->id();
# printing the link
print qq<a href="$ENV{SCRIPT_NAME}?sid=$sid">click here</a>~;
When the user clicks on the link, we just check the value of sid
CGI parameter. And if it exists, we consider it as an existing session id and pass it to the CGI::Session
's constructor:
use CGI::Session::File;
use CGI;
my $cgi = new CGI;
my $sid = $cgi->param("sid") || undef;
my $session = new CGI::Session::File($sid,
{
Directory => "/tmp/sessions"
});
If the sid
CGI parameter is present, the CGI::Session
will try to initialize the object with previously created session data. If sid
parameter is not present in the URL, it will default to undef, forcing the CGI::Session
to create a new session just like in our first example. Also, when the user is asked to submit a form, we should include the session id in the HIDDEN field of the form, so that it will be sent to the application and will be available via $cgi->param("sid") syntax: (see methods )
# get the session id...
my $sid = $session->id();
# print the hidden field
print qq~<input type="hidden" name="sid" value="$sid">~;
This session management technique stays good as long as the user is browsing our site. What if the user clicks on an external link, or visits some other site before checking out his shopping cart? Then when he comes back to our site within next 5 minutes or so, he will be surprised to find out that his shopping cart is gone! Because when they visit the site next time by typing the URL, sid
parameter will not be present in the URL, and our application will not recognize the user resulting in the creation of a new session id, and all the links in the web site will have that new session id appended to them. Too bad, because the client has to start everything over again.
INDENTIFYING THE USER VIA COOKIES
We can deal with the above problem by sending the client a cookie. This cookie will hold the session id only! Thus if the client visits some other site, or even closes the browser accidentally, we can still keep his session open till the next time he/she visits the site. While the implementation is concerned, it will not the different then the one above, with some minor changes:
use constant SESSION_COOKIE => "MY_SITE_SID";
use CGI::Session::File;
use CGI;
my $cgi = new CGI;
my $sid = $cgi->cookie(SESSION_COOKIE) || undef;
my $session = new CGI::Session::File($sid,
{
Directory => "/tmp/sessions"
});
# now, do not forget to send the cookie back to the client:
{
my $cookie = $cgi->param(-name => SESSION_COOKIE,
-value => $session->id,
-expires=> "+1M");
print $cgi->header(-cookie=>$cookie);
}
I can hear critics saying "what if the user disabled cookies? The above technique will fail!". Surprisingly, they are absolutely right. If the client disabled cookies in his/her browser (which is less likely) the above technique of ours is even worse than the previous one. It will not work AT ALL. So we should combine both of the techniques together. This will require the change only in one line from the above code:
my $sid = $cgi->cookie(SESSION_COOKIE) || $cgi->param("sid") || undef;
and the reset of the code stays the same. As you see, it will first try to get the session id from the cookie, if it does not exist, it will look for the sid
parameter in the URL, and if that fails, then it will default to undef, which will force CGI::Session
to create a new id for the client.
IDENTIFYING THE USER VIA PATH_INFO
The least common, but at the same time quite convenient way of marking
users with a session id is appending the session id to the url of the script as a PATH_INFO
. PATH_INFO
is somewhat similar to QUERY_STRING
, but unlike QUERY_STRING
it does not come after the question mark (?
), but it comes before it, and is separated from the script url with a slash (/
) just like a folder. Suppose our script resides in /cgi-bin/session.cgi. And after we append session id to the url as a PATH_INFO
if will look something like:
/cgi-bin/session.cgi/KUQa0zT1rY-X9knH1waQug
and the query string would follow PATH_INFO
:
/cgi-bin/session.cgi/KUQa0zT1rY-X9knH1waQug?_cmd=logout
You can see examples of this in the examples section of the manual.
And when it comes to initializing the session id from the PATH_INFO
, consider the following code:
my ($sid) = $cgi->path_info =~ /\/([^\?\/]+)/;
my $session = new CGI::Session::File($sid, {
Directory => "/tmp"});
CGI.pm's path_info()
method returns the PATH_INFO environmental variable with a leading slash (/
). And we are using regex to get rid of the leading slash and retrieve the session id only. The rest of the code is identical to the previous examples.
ERROR HANDLING
CGI::Session
itself never die()s (at least tries not to die), neither should its drivers. But the methods' return value indicates the success/failure of the call, and $CGI::Session::errstr
global variable will be set to an error message in case something goes wrong. So you should always check the return value of the methods to see if they succeeded:
my $session = new CGI::Session::File($sid,
{
Directory => "/tmp/sessions"
}) or die $CGI::Session::errstr;
STORING DATA IN THE SESSION
After you create a session id or initialize existing one you can now save data in the session using
param()
method: (see methods)$session->param('email', 'sherzodr@cpan.org');
This will save the email address sherzodr@cpan.org in the
email
session parameter. A little different syntax that also allows you to do this is:$session->param(-name=>'email', -value=>'sherzodr@cpan.org'); # tie() interface $session{email} = 'sherzodr@cpan.org';
If you want to store more values in the same session parameter, you can pass a reference to an array or a hash. This is the most frequently exercised technique in shopping cart applications, or for storing users' browsing history. Here is the example where we store the user's shopping cart as a reference to a hash-table, keys holding the item name, and their values indicating the number of items in the cart. ( I would go with item id rather than item names, but we choose item names here to make the things clearer for the reader):
# OO Interface $session->param(-name=>'cart', -value=>{ "She Bang Hat" => 1, "The Came T-shirt"=> 1, "Perl Mug" => 2 }); # tie() Interface $session{cart} = { "She Bang Hat" => 1, "The Came T-shirt" => 1, "Perl Mug" => 2 };
the same assignment could be performed in the following two steps as well:
my $cart = { "She Bang Hat" => 1, "The Came T-shirt"=> 1, "Perl Mug" => 2 }; # OO Interface $session->param(-name=>'cart', -value=>$cart); # tie() Interface $session{cart} = $cart;
Sometimes, you want to store the contents of a form the user submitted, or want to copy the contents of the CGI query into the session object to be able to restore them later. For that purpose
CGI::Session
provides withsave_param()
method which does just that.Suppose, a user filled in lots of fields in an advanced search form in your web site. After he submits the form, you might want to save the generated CGI query (either via GET or POST method) into the session object, so that you can keep the forms filled in with the previously submitted data throughout his session. Here is the portion of the code after the user submits the form:
# if the user submitted the form.. if ( $cgi->param("_cmd") eq "search") { # save the generated CGI query in the session: $session->save_param($cgi); } # tie() Interface: n/a
It means, if the search form had a text field with the name "keyword":
<input type="textfield" name="keyword" />
after calling save_param($cgi), the value of the text field will be available via $session-param("keyword")|"METHODS">, and you can re-write the above text field like the following:
my $keyword = $session->param("keyword"); print qq~<INPUT TYPE="textfield" name="keyword" value="$keyword" />~;
Sometimes you don't want to save all the CGI parameters, but want to pick from the list. save_param() method optionally accepts an arrayref as the second argument telling it which CGI parameters it should save in the session:
$session->save_param($cgi, [ "keyword", "order_by", "order_type", "category" ]);
Now only the above listed parameters will be saved in the session for future access. Inverse of the save_param() is load_param().
SPECIAL NAMES
When you create a fresh-blank session, it's not blank as it seems. It is initialized with the following 4 parameters, which are serialized together with other session data. We call these "SPECIAL NAMES".
_session_id
- stores the ID of the session_session_ctime
- stores the creation date of the session_session_atime
- stores the last access time for the session_session_etime
- stores expiration date for the session
So you shouldn't be using parameter names with leading underscore, because CGI::Session
preserves them for internal use. They are required for the library to function properly. Even though you try to do something like:
$session->param(-name=>"_some_param", -value=>"Some value");
param()
returns undef
, indicating the failure and assigns the error message in the $CGI::Session::errstr
, which reads:
Names with leading underscore are preserved for internal use by CGI::Session.
_some_param - illegal name
You cannot access these "SPECIAL NAMES" directly via param()
either, but you can do so by provided accessory methods, id()
, ctime()
, atime()
and expires()
(see methods).
If you are using tied hash access interface, these rules differ slightly, where you can read all these name from the hash, but you can set only _session_etime and _session_atime names:
printf ("Session last accessed on %s\n", localtime($session{_session_atime}));
# updating last access time. You don't have to do it, it will be done
# automatically though
$session{_session_atime} = time();
# setting expiration date, setting in 2 months
$session{_session_etime} = "2M";
ACCESSING SESSION DATA
Now the client has to check the items out of his/her shopping cart, and we need to access our session parameters.
The same method used for storing the data, param(), can be used to access them:
my $login = $session->param("login");
This example will get the user's login name from the previously stored session. This could be achieved with a slightly different syntax that
param()
supports:my $login = $session->param(-name=>"login");
Which syntax to use is up to you! Now let's dump the user's shopping cart that was created earlier:
# remember, it was a hashref? my $cart = $session->param(-name=>"cart"); while ( my ($name, $qty) = each %{$cart} ) { print "Item: $name ($qty)", "<br />"; }
Another commonly usable way of accessing the session data is via
load_param()
method, which is the inverse of save_param(). This loads the parameters saved in the session object into the CGI object. It's very helpful when you want CGI object to have access to those parameters:$session->load_param($cgi);
After the above line, CGI has access to all the session parameters. We talked about filling out the search form with the data user previously entered. But how can we present the user pre-checked group of radio buttons according to his/her previous selection? How about checkboxes or popup menus? This is quite challenging unless we call CGI library for help, which provides a sticky behavior for most of the form elements generated with CGI.pm
# load the session parameters into the CGI object first: $session->load_param($cgi, ["checked_items"]); # now print the group of radio buttons,it CGI.pm will check them # according to the previously saved session data print $cgi->group_radio(-name=>"checked_items", -values => ["eenie", "meenie", "minie", "moe"]);
Notice the second argument passed to the load_param(). In this case it is loading only the "checked_items" parameter from the session. If it were missing it would load the whole session data.
CLEARING THE SESSION DATA
When the user click on the "clear the cart" button or purchases the contents of the shopping cart, that's a clue that we have to clear the cart. CGI::Session
's clear() method deals with clearing the session data:
# OO Interface
$session->clear("cart");
# tie() Interface
delete $session{cart};
What happens is, it deletes the given parameter from the session for good. If you do not pass any arguments to clear()
, then all the parameters of the session will be deleted. Remember that clear()
method DOES NOT delete the session. Session stays open, only the contents of the session data will be deleted.
DELETING THE SESSOIN
If you want to delete the session for good together with all of its contents, then delete() method is the way to go. After you call this method, the CGI::Session
will not have access to the pervious session at all, because it was deleted from the disk for good. So it will have to generate a new session id instead:
# OO Interface
$session->delete();
# tie() Interface:
tied(%session)->delete();
CLEAR OR DELETE?
So, should we delete() the session when the user finishes browsing the site or clicks on the "sign out" link? Or should we clear()
it? And I'll answer the question with another question; bright mind should be able to see me through! If the user click on the "sign out" link, does it mean he is done browsing the site? Absolutely not. The user might keep surfing the site even after he signs out or clears his shopping cart. He might even continue his shopping after several hours, or several days. So for the comfort of the visitors of our site, we still should keep their session data at their fingertips, and only clear()
unwanted session parameters, for example, the user's shopping cart, after he checks out. Sessions should be deleted if they haven't been accessed for a certain period of time, in which case we don't want unused session data occupying the storage in our disk.
EXPIRING SESSIONS
While I was coding this feature of the CGI::Session
, I wasn't quite sure how to implement auto-expiring sessions in more friendly way. So I decided to leave to the programmer implementing CGI::Session
and introduced 3 brand new methods to the library, expires(), atime() and ctime();
ctime() method returns the time()
value when the session was created for the first time. atime()"METHODS" method returns the time()
value when then session data was accessed for the last time. If we use expires()
without any arguments, it returns the time() value of the date when the session should expire. Returns undef if it's non-expiring session. If you use it with an argument, then it will set the expiration date for the session. For the list of possible arguments expires() expects, please check out the "METHODS" section below.
Remember, even though you set an expiration date, CGI::Session 2.0
itself doesn't deal with expiring sessions, the above 3 method just provide you with all the required tools to implement your expiring sessions. I will put some more effort on this issue in the next releases of the library. But if you have any bright ideas or patches, scroll down to the "AUTHOR" section and get in touch.
The following script is best suited for a cron job, and will be deleting the sessions that haven't accessed for the last one year.
use constant YEAR => 86400 * 365; # in seconds
use CGI::Session::File;
use IO::Dir;
tie my %dir, "IO::Dir", "/tmp/sessions", DIR_UNLINK or die $!;
my $session;
my $options = {Directory=>"/tmp/sessions"};
while ( my ($file, $info) = each %db ) {
my ($sid) = $file =~ m/CGI-Session-([^\.]+)\.dat) or next;
$session = CGI::Session::File->new($sid, $options);
if ( (time - $session->atime) > YEAR ) {
$session->delete();
}
}
untie %dir;
EXAMPLES
SESSION PREFERENCES
This example will show how to remember users' preference/choices for the duration of their session while they are browsing the site.
- URL: http://modules.ultracgis.com/cgi-bin/session
- DESCRIPTION
-
This example is marking the user both with a cookie and with a
PATH_INFO
appended to every url. Thus even though the application is at /cgi-bin/session, the url looks something like /cgi-bin/session/KUQa0zT1rY-X9knH1waQug?_cmd=profile, which tricks untrained eyes into thinking that/cgi-bin/session
is a folder, and the script has a quite long and unpleasant name. ButCGI::Session
users should easily be able to guess that we're just appending a path to the url.
METHODS
- new()
-
constructor method. Requires two arguments, first one is the session id the object has to initialize, and the second one is the hashref to driver specific options. If session is evaluates to
undef
, the CGI::Session will generate a new session id stores it automatically. If defined session id is passed, but the library fails to initializes the object with that session, then new session will be created instead. If an error occurs either in storing the session in the disk, or retrieving the session from the disk,new()
returns undef, and sets the error message in the$CGI::Session::errstr
global variable. Example:use CGI::Session::DB_File; my $session = new CGI::Session::DB_File(undef, { LockDirectory=>'/tmp', FileName => '/tmp/sessions.db' }) or die "Session couldn't be initialized: $CGI::Session::errstr";
- id()
-
returns the session id for the current session.
- error()
-
returns the error message. Works only after the session object was initialized successfully. Other times, please use
$CGI::Session::errstr
variable to access the error message - param()
-
the most important method of the library. It is used for accessing the session parameters, and setting their values. It supports several syntax, all of which are discussed here.
param()
- if passed no arguments, returns the list of all the existing session parametersparam("first_name")
returns the session value for thefirst_name
parameter.param(-name=>"first_name")
- the same asparam("first_name")
, returns the value offirst_name
parameterparam("first_name", "Sherzod")
- assignsSherzod
to thefirst_name
session parameter. Later you can retrieve thefirst_name
with eitherparam("first_name")
orparam(-name=>"first_name")
syntax. Second argument can be either a string, or it can be reference to a more complex data structure like arrayref, hashref, or even a file handle. Example,$session->param("shopping_cart_items", [1, 3, 66, 2, 43]);
later, if you wish to retrieve the above arrayref form the
shopping_cart_items
parameter:my $cart = $session->param("shopping_cart_items");
now $cart holds
[1, 3, 66, 2, 43]
.param(-name=>"first_name", -value=>"Sherzod")
- the same asparam("first_name", "Sherzod")
, assignsSherzod
tofirst_name
parameter.
- save_param()
-
Saves the CGI object parameters into the session object. It's very helpful if you want to save the user's form entries, like email address and/or username in the session, for later use. The first argument has to be a CGI.pm object as returned from the
CGI->new()
method, and the second argument (optional) is expected to be a reference to an array holding the names of CGI parameters that need to be saved in the session object. If the second argument is missing, it will save all the existing CGI parameters, skipping the ones that start with an underscore (_). Example,$session->save_param($cgi, ["login_name", "email_address"]);
Now, if the user submitted the text field with his "login_name", his login is saved in our session already. Unlike CGI parameters, Session parameters do not disappear, they will be saved in the disk for a lot longer period unless you choose to delete them. So when the user is asked to login after several weeks, we just fill the login_name text field with the values of the field submitted when the user visited us previously. Example:
# let's get his login_name which was saved via save_param() method: my $login_name = $session->param("login_name"); # now let's present the text field with login_name already typed in: print $cgi->textfield(-name=>"login_name", -value=>$login_name);
- load_param()
-
This is the opposite of the above
save_param()
method. It loads the previously saved session parameters into the CGI object. The first argument to the method has to be a CGI.pm object returned from CGI->new() method. The second argument, if exists, is expected to be a reference to an array holding the names of the parameters that need to be loaded to the CGI object. If the second argument is missing, it will load all the session parameters into the CGI object, skipping the ones that start with an underscore (_). If we're using CGI.pm to produce our HTML forms, the above example could also be written like the following:$session->load_param($cgi); print $cgi->textfield(-name=>"login_name");
This method is quite handy when you have checkboxes or multiple section boxes which assign array of values for a single element. To keep those selection the way your visitor chooses throughout his/her session is quite challenging task. But CGI.pm's "sticky" behavior comes quite handy here.
All you need to do is to load the parameters from the session to your CGI object, then CGI.pm automatically restores the previous selection of checkboxes:
# load it from the disk to the CGI.pm object $session->load_param($cgi, "lists"); # now we can just print the checkbox, and previously saved checks # would remain selected: print $cgi->checkbox_group(-name=>"lists", -values=>["CGI-Perl", "Perl5-Porters", "CGI-Session"]);
Note:
load_param()
andsave_param()
methods didn't work on parameters that return multiple values. This problem was fixed in version 2.6 of the library. - clear()
-
this method clears the session data. Do not confuse it with
delete()
, which deletes the session data together with the session_id.clear()
only deletes the data stored in the session, but keeps the session open. If you want to clear/delete certain parameters from the session, you just pass an arrayref to the method. For example, here is the revised copy of the code I used in one of my applications that clear the contents of the user's shopping cart when he/she click on the 'clear the cart' link:$session->clear(["SHOPPING_CART"]); # tie() Interface delete $session{"SHOPPING_CART"};
I could as well use
clear()
with no arguments, in which case it would delete all the data from the session, not only the SHOPPING_CART:# OO Interface $session->clear(); # tie() Interface %session = ();
- expires()
-
When a session is created, it's expiration date is undefined, which means, it never expires. If you want to set an expiration date to a session,
expires()
method can be used. If it's called without arguments, will return the time() value of the expiration date, which is the number of seconds since the epoch. If you pass an argument, it will consider it either as a number of seconds, or a special shortcut for date values. For example:# will it ever expire? unless ( $session->expires ) { print "Your session will never expired\n"; } # how many seconds left? my $expires_in = $session->expires() - time(); print "Your session will expire in $expires_in seconds\n"; # when exactly will it expire? my $date = scalar(localtime( $session->expires )); print "Your session will expire on $date\n"; # let the session expire in 60 seconds... $session->expires(60); # the same $session->expires("60s"); # expires in 30 minutes $session->expires("30m"); #expires in 1 month $session->expires("1M");
For tie() interface you will need to update one of the "SPECIAL NAMES"
_session_etime
:$session{_session_etime} = "2d"; printf("Your session will expires in %d seconds\n", $session{_session_etime} - time);
Here is the table of the shortcuts available for
expires()
:+===========+===============+ | shortcut | meaning | +===========+===============+ | s | Second | | m | Minute | | h | Hour | | w | Week | | M | Month | | y | Year | +-----------+---------------+
see expiring sessions for more on this.
- ctime()
-
Returns the time() value of the date when the session was created:
# OO Interface printf("Session was created on %s\n", localtime($session->ctime)); # tie() Interface printf("Session was created on %s\n", localtime($session{_session_ctime});
- atime()
-
Returns the time() value of the date when the session was last accessed:
# OO Interface printf("Session was last accessed on %s\n", localtime($session->atime)); printf("Session was last accessed %d seconds ago\n", time() - $session->atime); # tie() Interface printf("Session was last accessed on %s\n", localtime($session{_session_atime})); printf("Session was last accessed %d seconds ago\n", time() - $session{_session_atime});
- delete()
-
deletes the session data from the disk permantly:
# OO Interface $session->delete(); # tie() Interface tied(%session)->delete();
DEVELOPER SECTION
If you noticed, CGI::Session
has never been accessed directly, but we did so using its available drivers. As of version 2.0 CGI:Session
comes with drivers for File, DB_File and MySQL databases. If you want to write your own driver for different storage type ( for example, Oracle, Sybase, Postgres so forth) or if you want to implement your own driver, this section is for you. Read on!
HOW IS THE LIBRARY DESIGNED?
CGI::Session
itself doesn't deal with such things as storing the data in the disk (or other device), retrieving the data or deleting it from the persistent storage. These are the issues specific to the storage type you want to use and that's what the driver is for. So driver is just another Perl library, which uses CGI::Session
as a base class, and provides three additional methods, store()
, retrieve()
and tear_down()
. As long as you provide these three methods, CGI::Session
will handle all the other part.
WHAT ARE THE SPECS?
store()
will receive four arguments,$self
, which is the object itself,$sid
, which is the session id,$hashref
, which is the session data as the reference to an antonymous hash, and <$options>, which is another hash reference that was passed as the second argument tonew()
.store()
's task is to store the hashref (which is the session data)in the disk in such a way so that it could be retrieved later. It should return true on success, undef otherwise, passing the error message to$self->error()
method.retrieve()
will receive three arguments,$self
, which is the object itself,$sid
, which is the session id and$options
, which is the hash references passed tonew()
as the first argument.retrieve()
's task is to access the data which was saved previously by thestore()
method, and re-create the same$hashref
asstore()
once received, and return it. Method should return the data on success, undef otherwise, passing the error message to$self->error()
method.tear_down()
is called whendelete()
is called. So its task is to delete the session data and all of its traces from the disk.tear_down()
will receive three arguments,$self
, which is the object itself,$sid
, which is the session id and$options
, which is the hash reference passed tonew()
as the second argument. The method should return true on success, undef otherwise, passing the error message to$self->error()
.
If you open the dev/ folder in the CGI::Session
distribution, you will find a blueprint for the driver, MyDriver.pm, which looks something like this:
package CGI::Session::MyDriver;
use strict;
use vars qw($VERSION);
use base qw(CGI::Session CGI::Session::MD5);
# all other driver specific libraries go below
sub store {
my ($self, $sid, $hashref, $options) = @_;
return 1;
}
sub retrieve {
my ($self, $sid, $options) = @_;
return {};
}
sub tear_down {
my ($self, $sid, $option) = @_;
return 1;
}
It is inheriting from two classes, CGI::Session
and CGI::Session::MD5
. The second library just provides generate_id()
method that returns a sting which will be used as a session id. Default generate_id()
uses Digest::MD5 library to generate a unique identifier for the session. If you want to implement your own generate_id()
method, you can override it by including one in your module as the fourth method.
generate_id()
receives only one argument,$self
, which is the object itself. The method is expected to return a string to be used as the session id for new sessions.
The challenging part might seem to store the $hashref
and to be able to restore it back. But there are already libraries that you can make use of to do this job very easily. Drivers that come with CGI::Session
depend on Data::Dumper, but you can as well go with Storable or FreezeThaw libraries which allow you to freeze()
the Perl data and thaw()
it later, thus you will be able to re-create the the $hashref
. The reason we preferred Data::Dumper is, it comes standard with Perl.
LOCKING
Writing and reading from the disk requires a locking mechanism to prevent corrupted data. Since CGI::Session itself does not deal with disk access, it's the drivers' task to implement their own locking. For more information please refer to the driver manuals distributed with the package.
OTHER NOTES
Don't forget to include an empty DESTROY() method in your library for CGI::Session's AUTOLOAD would be looking for it thus wasting its precious time.
TODO
I still have lots of features in mind that I want to add and/or fix. Here is a short list for now. Feel free to email me your fantasies and patches.
Fix
expires()
and implement expiring sessions in more friendly wayImplement more sophisticated locking mechanism for session data
Customizable session id generator
Combining passive client identification methods
FREQUANTLY ASKED QUESTIONS
The following section of the library lists answers to some frequently asked questions:
- Q: Can I use CGI::Session in my shell scripts, or is it just for CGI Applications?
- A: Yes, you can!
-
CGI::Session does not depend on the presence of the Web Server, so you can use it on all kinds of applications, crons, shell scripts, you name it
- Q: What if the user ask for the session which was deleted from the disk?
- A: New session will be initialized!
-
Previous version of CGI::Session had a bug, and returned no id for the session if the session didn't exist in the disk. But latest version of the library should create a new session if the session data cannot be initialized!
- Q: Is it safe to store sensitive information in the session?
- A: Yes, it is safe, but read on
-
If you noticed in the manual, we were sending on the session id to the client either in the form of cookie or a URL parameter. And all other session data is stored in the server side. So if you want to store sensitive information in your session, I advise you to pick a very safe location for your
Directory
so that no one will be able to access session files to find out the users' passwords, etc.But there are alternative ways of user authentication, which I will try to cover in my
cgiauth||CGI::Session::auth
tutorial soon - Q: Where can I get detailed information on managing user sessions in web applications?
- A: I myself did a lot of research on this, and the only article on session management in CGI/Perl was the CGI::Session manual at http://modules.ultracgis.com/CGI/Session.html (same as the current manual). You can also check out Apache::Session
SUPPORT
I might not be able to answer all of your questions regarding CGI::Session
, so please subscribe to CGI::Session mailing list. Visit http://ultracgis.com/mailman/listinfo/cgi-session_ultracgis.com to subscribe.
For commercial support and/or custom programming, contact UltraCgis team ( send email to support@ultracgis.com )
HISTORY
Initial release of the library was just a front-end to Jeffrey Baker <jwbaker@acm.org>'s Apache::Session and provided CGI.pm-like syntax for Apache::Session hashes. But as of version 2.0, the class is independent of third party libraries and comes with File, DB_File and MySQL drivers. It also allows developers to write their own drivers for other storage mechanisms very easily.
Since CGI::Session used to depend on Apache::Session, the session data used to be serialized using Storable. Now it relies on standard Data::Dumper module to "freeze" and "thaw" the data.
CREDITS
- Andy Lester <alester@flr.follett.com>
-
Thanks for his patience for helping me to fix the bug in CGI::Session::File, which kept failing in Solaris.
- Brian King <mrbbking@mac.com>
-
Helped to fix the t/mysql.t test suite that was failing on MacOS X
BUGS
Currently the only know bug is in the load_param()
and save_param()
methods. They cannot deal with the parameters that return other than strings ( for example, arrays ).
AUTHOR
Sherzod B. Ruzmetov <sherzodr@cpan.org>
SEE ALSO
CGI::Session::File, CGI::Session::DB_File, CGI::Session::MySQL Apache::Session, Data::Dumper, Digest::MD5, FreezeThaw, Storable, CGI