The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

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!

WARNING

If you have a version prior to 2.91 please upgrade it to the latest release ASAP!

NOTE

As of this release there seems to be two 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.

INTERFACE

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. Usage of the library if similar to that of CGI.pm. 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 as a 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 wherever applicable. 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 differs 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.

IDENTIFYING 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->cookie(-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

  1. 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';
  2. 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;
  3. 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 with save_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" />~;
  4. 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

  1. 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 />";
        }
  2. 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. But CGI::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 parameters

  • param("first_name") returns the session value for the first_name parameter.

  • param(-name=>"first_name") - the same as param("first_name"), returns the value of first_name parameter

  • param("first_name", "Sherzod") - assigns Sherzod to the first_name session parameter. Later you can retrieve the first_name with either param("first_name") or param(-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 as param("first_name", "Sherzod"), assigns Sherzod to first_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() and save_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 permanantly:

    # 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?

Specifications for those methods we a little complex in previous versions of CGI::Session. But as of 2.8 I decided to pick a slightly different approach. Now all the driver methods receive two arguments, $self, and $sid. To give access to driver developers for other underlying data, I added two more methods to the library: <opions()> and raw_data(). options() returns a hashref that contains the options passed to the constructor. For CGI::Session::DB_File driver for example, this has contains:

    $options = {
        sid => $sid,
        FileName => 'some-file.db',
        LockDirectory => '/tmp'
    };

Although it contains session id, DO NOT use that session id from w/in your methods.

raw_data() method on the other hand provides the driver methods with a raw data. Mostly this data is required for the store() method, which needs to store the data in the disk. Raw data is an antonymous hash which holds key/value pairs of the session data. As for the session id, it could be acquired via the public id() method.

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) = shift;

        my $options = $self->options();
        my $data    = $session->raw_data();

        return 1;
    }


    sub retrieve {
        my ($self, $sid)    = @_;
        my $options = $self->options();
        my $sid     = $self->id();

        return {};
    }


    sub tear_down {
        my ($self, $sid) = @_;

        my $options = $self->raw_data();

        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 ra_data 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 driver 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.

  1. Fix expires() and implement expiring sessions in more friendly way

  2. Implement more sophisticated locking mechanism for session data.

  3. Customizable session id generator. It currently uses b64digest(). But some people might prefer hexdigest(). So we have to make them happy too.

  4. Making use of some 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.

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

Following people contributed with their patches and/or ideas to the development of this library. List is in ascending chronological order.

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

Olivier Dragon <dragon@shadnet.shad.ca>

Fixed expires() method.

BUGS

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