Session Handling with PHP 4

February 15, 2000

Tutorials

Target Audience

Introduction
PHP’s Built-in Session Library
Goals of the Tutorial
Basic Terms
class="bodyLink">Background Information
class="bodyLink">Preliminary Tips and Prerequisites

   class="bodyLink">Starting a Session
   class="bodyLink">Ending a Session
class="bodyLink">Storage Modules
   class="bodyLink">Session ID Propagation
Example Code

Target Audience
This tutorial is designed for an advanced PHP programmer. It assumes you know
how to handle cookies. (See Feedback with a Cookie
to learn about using cookies.) You should also know how to pass data from one
page to another using the POST and GET methods.

Introduction
Unlike PHP 3, PHP 4.0 has built-in capabilities to handle session management.
The session management functionality in PHP 4.0 is easy to use, powerful and
open for custom modifications. Creating sessions allows you to keep track of
the actions of a particular user over the period of time she is viewing your
web site.

To associate session data with a user, you need a session identity number,
that is, a key that ties the user to his data. This tutorial discusses how to
open sessions, track session data, and clear the data when you no longer need
it.

PHP’s Built-in Session Library
Luckily, PHP 4.0 has basic session management built in, which frees you from
the task of inventing session IDs, serializing and storing session data. While
it’s very easy and straightforward to use and may suffice for your needs, it
lacks some of the advanced features that the PHPLib provides.

Goals of the Tutorial

In this tutorial you can learn the following:

  • What a session is
  • How to use persistent variables
  • How session IDs are passed from page to page
  • How to end a session and handle clearing of data (garbage collection)
  • How to register variables to refer to during user sessions
  • How to use the PHP 4.0 session management functions, including:
    • session_register()
    • session_start()
    • session_destroy()
    • gc_probability()
    • gc_maxlifetime
    • serialize()
    • deserialize()
    • session_save_path()
  • How PHP stores session data.

Basic Terms
Session management is a mechanism to maintain state about a series of requests
from the same user across some period of time. That is, the term “session” refers
to the time that a user is at a particular web site. The problem is, that HTTP
has no mechanism to maintain state. Individual requests aren’t related to each
other. The Web server can’t easily distinguish between single users and doesn’t
know about user sessions. Session management refers to the way that associate
data with a user during a visit to a Web page. This tutorial uses the term session
for a single visit of a user. For example, a typical online shopping session
might include logging in, putting an item into the shopping cart, going to the
checkout page, entering address and credit card data, submitting the order,
and closing the browser window. PHP 4.0 includes native session management functions
to ease the task of managing user sessions.

The “life: of a session refers to the amount of time the session is active.
“Serializing” means the transformation of variables to a byte-code representation
that can be stored anywhere as a normal string. Without the serializing feature,
it wouldn’t be possible, for example, to store PHP arrays into a database. Serializing
data is very useful for preserving data across requests, an important facet
of a session library. You can use serialize() and deserialize(),
but note that in PHP 3 these functions don’t work correctly on objects (classes);
class functions will be discarded.

Background Information
PHP’s session management library offers the key characteristics required of
a session management library:

  • It stores session data on the server. Because the library uses different
    storage modules, you can keep the data in plain text files, shared memory, or
    databases. The exact location of data is not really important (as long the
    performance of the medium is sufficient).

  • It uses a cryptographically random session ID to identify a user.
  • It saves the session ID (and only the session ID) on the client side using
    cookies, GET/POST, or the script path. (The PHP library provides all of these
    methods; we show how to use them a little later.)

  • If the user disables cookies, the application can use other means of
    session propagation.

Preliminary Tips and Prerequisites

To associate session data with a user, you need a session identity number: a
key that ties the user to his data. PHP 4.0′s session management frees you from
the task of inventing session IDs, serializing and storing session data.

Note: PHPlib offers advanced session management functions if you require
something beyond the standard PHP session functionality.

Starting a Session
A PHP 4 session is started either explicitly by session_start(),
or implicitely by registering a variable for the session, using session_register().
Usually, you will call session_start() on top of the page, so that
session variables are available to your script, and register variables to the
session later in the script. It wouldn’t make a difference though, if you registered
your session variables with session_register() in the head of the
script and left out the session_start() call – session_register()
calls session_start() internally, if the session isn’t started
yet. When you start a session either way, the following happens:

  • PHP checks whether a valid session ID exists.
  • If there is no session ID, PHP creates a new ID.
  • If a valid ID exists, the frozen variables of that session are reactivated
    and introduced back to the global namespace.

Registering a session variable is done through the session_register()
command. This allows you to create (register) variables which are stored throughout
the session, and can be referred to during the session. All variables you want
to preserve across page requests must be registered to the session library with
the session_register() function. Note that this function takes the
name of a variable as argument, not the variable itself. You can use session_unregister()
to remove variables from the session, for example, when the user removes a product
item from the shopping cart.

Syntax Example

This is an example of a counter.

  • Start a session
  • Print the most recent value of the counter
  • Increment the counter
  • Register the counter


session_start
(); 
print(
$counter color="#007700">); 

$counter++;
href="/manual/function.session-register.php">session_register color="#007700">("counter" color="#007700">);




Of course, this example is different from a normal page counter: the session
(and thus the counter) is tied to one specific user. With PHP’s default
configuration, the session cookie has a lifetime of 0; if you close the browser
and reopen it, the counter restarts from zero, as the cookie has been deleted.

Syntax Example

  • Register the variable $foo



href="/manual/function.session-register.php">session_register
color="#007700">(
"foo" color="#007700">);


Syntax Example

  • Register a previously defined variable $bar



$bar 
color="#007700">= 
color="#DD0000">"This is a string" color="#007700">;
$foo  color="#007700">= "bar" color="#007700">;

href="/manual/function.session-register.php">session_register color="#007700">($foo color="#007700">);


The difference in the last two syntax examples is that in the first a
variable named “foo” is registered as session variable, in the latter actually
a variable named “bar” is registered. Session_register() takes the name of a
variable as argument, not the variable itself – this is easily confused, but
the examples show the difference clearly.

It’s as easy to handle session variables as it is to handle GET/POST variables.
If you register a variable named foo, $foo is accessible automatically after
calling session_start(). Because the serialize() function was improved in PHP
4, it’s also feasible to treat objects (classes) as session variables.

Tips

Note: At the start of a session, a new ID may be created if the session is
refused and marked as invalid because the HTTP referrer for the page comes from
a non-local site and extern_referer_check (note the single “r”) is enabled in
the PHP configuration. This introduces some additional security, as it prevents
users coming from other PHP sites taking over a session (which is still highly
improbable, however, due to the algorithm used for the generation of the
session ID).

See also Ending a Session



Ending a Session

Session ending is not automatic, because it is difficult for the system to tell
when the user is finished the session. Several commands help you control how
the system determines when to end a session for a user.

  • You can force a session end with the command session_destroy().
  • If you propagate the session ID via cookies, the default cookie lifetime is
    0, meaning that the cookie is deleted as soon as the user closes the browser.
    You can influence the cookie’s lifetime with the configuration value lifetime.

  • You can use the gc_maxlifetime configuration directive to determine
    how long after the last access to this session the data should be destroyed.
    This is used because the server doesn’t know whether the cookie still exists
    on the client side. However, performing such a cleanup of old sessions (called
    “garbage collection”) on every page request would cause considerable overhead.
    Therefore, in tangent with the gc_maxlifetime, you should use
    gc_probability. This specifies with what probability the garbage
    collection routine should be invoked. If gc_probability
    is 100, the cleanup is performed on every request (that is, with a probability
    of 100%); if it’s 1 as by default, old sessions will be removed with a probability
    of 1% per request.

If you don’t use cookies but pass the session ID via GET or POST instead, you
need to pay special attention to the garbage collection routines. Users might
bookmark URLs containing the session ID, so you need to make sure that sessions
are cleared frequently. If the session data still exists when the user accesses
the page with the session ID at a later time, PHP simply resumes the previous
session instead of starting with a new session, which may not be your intention.
A value of 10 to 20 for gc_probability would better fit this scenario
than the default value of 1.


Tips

You might ask yourself why PHP allows you to specificy a probability (gc_probability)
which determines when garbage collection will occur, rather than a function
which cleans up every n times. If PHP used a counting function, the server would
need to track the number of opened sessions somehow. Using the probability function
means that the server does not have to store counters for cleanup, which translates
to cleaner and faster execution.

Storage Modules

To read and save session data, PHP uses storage modules, thus abstracting the
back end of the library. There are currently three storage modules available:

  • Files. By default, PHP uses the files module
    to save the session data to disk. It creates a text file named after the
    session ID in /tmp. You probably won’t ever need to access this file directly.
    In the example of the session counter, the content of this file would look like
    this, which is a serialized representation of the variable:
    counter|i:4;

  • mm. If you need higher performance, the mm
    module is a viable alternative; it stores the data in shared memory and is
    therefore not limited by the hardware I/O system.

  • User. Used internally to realize user-level
    callback functions that you define with session_set_save_handler().

The real power lies in the capacity to specify user callbacks as storage
modules. Because you can write your functions to handle sessions while still
being able to rely on the standardized PHP API, you can store sessions wherever
and however you want: in a database like MySQL, XML files, on a remote FTP
server (an FTP server is unlikely, but you get the idea).

The function session_set_save_handler() takes six
strings as arguments, which must be your callback functions.

The syntax of the function is as follows:

void session_set_save_handler(string open, string close,
string read, string write, string destroy, string gc);


Tip

To leave out one argument, pass an empty string (“”) to
session_set_save_handler().

The functions are defined as follows:
bool open (string save_path, string sess_name);

This function is executed on the initialization of a session; you should use it
to prepare your functions, to initialize variables, or the like. It takes two
strings as arguments. The first is the path where sessions should be saved. This
variable can be specified in php.ini or by the session_save_path()
function – you can use this variable as a joker and use it for module-specific
configuration. The second argument is the session’s name, by default PHPSESSID.
Returns true on success and false on error.

bool close ();

This function is executed on shutdown of a session. Use it to free memory or to
destroy your variables. It takes no arguments and returns true on success and
false on error.

mixed read (string sess_id, );

This important function is called whenever a session is started. It must read
out the data of the session identified with sess_id and return it as a
serialized string. If there’s no session with this ID, an empty string “” is
returned. If there is an error, false is returned.

bool write (string sess_id, , string value);

When the session needs to be saved, this function is invoked. The first
argument is a string containing the session’s ID; the second argument is the
serialized representation of the session variables. This function returns true
on success and false on error.

bool destroy (string sess_id, );

When the developer calls session_destroy(), this function is executed.
It destroys all data associated with the session sess_id and returns true on success
and false on error.

bool gc (int max_lifetime, );

This function is called on a session’s start-up with the probability specified
in gc_probability. It’s used for garbage collection; that is, to
remove sessions that weren’t updated for more than gc_maxlifetime

seconds. This function returns true on success and false on error.

If you want to setup your own storage modules, for example to store session
data in a MySQL database, you need to provide PHP implementations of these
functions. The prototypes will look similar to these examples:


function  Color="#0000BB">sess_open
( color="#0000BB">$save_path color="#0000BB">$sess_name)
{

}

function sess_read color="#007700">($sess_id color="#007700">)
{
}

function sess_write color="#007700">($sess_id color="#007700">, $val color="#007700">)

{
}

function sess_destroy color="#007700">($sess_id color="#007700">)
{
}

function sess_gc color="#007700">($max_lifetime color="#007700">)

{
}



To register these calback functions, you use
session_set_save_handler():

session_set_save_handler(“sess_open”, “”, “sess_read”, “sess_write”,
“sess_destroy”, “sess_gc”);

Session ID Propagation

PHP 4 sessions support the following methods of passing the session ID:

  • Cookies (default)
  • GET/POST
  • Hidden in the URL, either done manually or by automatic URL rewriting

Cookies are the default way to pass the session ID between pages. If you’re
happy with cookies, you don’t have to worry about any special configuration.
Another common way is to pass the ID is with GET/POST. Your URL would then be
similar to script.php3?<session-name>=<session-id>. You can create such URLs by
using the global constant SID:

printf(‘<a href=”script.php?%s”>Link</a>’,
SID);

Automatic URL rewriting is one of the very cool new features of PHP 4, allowing
you to add the session ID to all the links within the page. To enable it, you
need to configure PHP with --enable-trans-id and recompile it. Then
the session ID in the form = will be added to all relative
links within your PHP-parsed pages. While this is a handy feature, it should be
used with caution on high-performance sites. PHP has to look at each individual
page, analyze it to see whether it contains relative links, and eventually add
the ID to the links. This obviously introduces a performance penalty. Cookies,
on the other hand, are set only once, avoiding the overhead of URL rewriting.

Example Code

The following is completely commented sample code.
The popular hangman game is a great way to show how to use persistent
variables. In this game, the computer chooses a random five-letter word and the
player needs to figure it out by choosing letters to fill in. The user has only
six guesses, or he’ll end up on the gallows. We’ve chosen a database of common
English female names, but you’re free to use any other word list you want.
Simply replace the existing words.txt file with your own version; the only
constraint is that each word must be on one line.
Of course, this game cannot work without keeping state information. the player
would always have five tries left, could never win without guessing the whole
word correctly the first time, and generally, it wouldn’t be much fun at all.
If you think for a moment about the game’s logic, you’ll come up with three
variables that need to be remembered from request to request:

  • The random word the user is to guess.
  • The characters the user has already guessed.
  • How many tries have failed so far.

The number of tries could be calculated with some tricks, but for clarity’s
sake this example stores it separately.

The example uses the default method of passing the session ID, cookies, so no
tricks are necessary in the use of the session library. It follows the
following program logic:

  • The three session variables are registered.
  • The script checks whether the form is invoked by a Post operation, meaning
    that the user has submitted a guess.

  • If this is the case, the script processes the input, updates the game state
    (won, lost, correct guess, incorrect guess) and outputs a message accordingly.

  • If not, a new game is started, and a random word is extracted from the file
    words.txt.

  • A string, which hides the not-yet-guessed characters in the guess word, is
    created and printed to the browser.

  • The rest of the page is drawn.

All HTML output is handled by a separate template class. This allows the separation
of code and layout needed often in professional web applications, but that will
be the topic of another tutorial.

The code is simple, but easy to understand therefore and can give you a good
idea how and for what tasks to use the session library. And you could even
extend the application; for example, you could calculate the player’s highscore
and store it in a database. If you do such fancy things with this game, be sure
to let us know and we’d be happy to feature your versions on our web site!

NOTE:

The code contains comments preceded by “//” marks.

require( color="#DD0000">"EasyTemplate.inc.php3"
);
href="/manual/function.define.php">define color="#007700">("HANG_MAX_TRIES" color="#007700">, 6 color="#007700">);

$guess  color="#007700">=  href="/manual/function.strtolower.php">strtolower color="#007700">($guess color="#007700">);

function hang_get_random_line color="#007700">($file color="#007700">)
{

     color="#FF8000">// Try to open the file
    if(! Color="#0000BB"> href="/manual/function.file-exists.php">file_exists color="#007700">($file color="#007700">) || !( color="#0000BB">$fp  Color="#0000BB"> href="/manual/function.fopen.php">fopen color="#007700">($file color="#007700">, "r" color="#007700">)))

    {
        die( color="#DD0000">"Could not open file  color="#007700">\"$file color="#007700">\" color="#DD0000"> fo reading.");
    }
    

     color="#FF8000">// Get file size
    $size  color="#007700">=  href="/manual/function.filesize.php">filesize color="#007700">($file color="#007700">);
    
     color="#FF8000">// Init randomizer, get a random value in the range 0..filesize

     href="/manual/function.srand.php">srand color="#007700">((double) href="/manual/function.microtime.php">microtime color="#007700">()*1000000 color="#007700">);
    $randval  color="#007700">=  href="/manual/function.rand.php">rand color="#007700">(0 color="#007700">, $size color="#007700">);
    

     color="#FF8000">// Seek to a random position in the file - possible this is EOF.
     href="/manual/function.fseek.php">fseek color="#007700">($fp color="#007700">, $randval color="#007700">);

    
     color="#FF8000">// Get line - possibly empty (if at EOF)
    $line  color="#007700">=  href="/manual/function.trim.php">trim color="#007700">( href="/manual/function.fgets.php">fgets color="#007700">($fp color="#007700">, 1024 color="#007700">));

    $line  color="#007700">=  href="/manual/function.trim.php">trim color="#007700">( href="/manual/function.fgets.php">fgets color="#007700">($fp color="#007700">, 1024 color="#007700">));
    
     color="#FF8000">// Close file
     href="/manual/function.fclose.php">fclose color="#007700">($fp color="#007700">);

    
     color="#FF8000">// If line was empty, try again
    if(empty( color="#0000BB">$line))
    {
        
color="#0000BB">$line  Color="#0000BB">hang_get_random_line( color="#0000BB">$file);

    }  
    
    
color="#FF8000">// Return random line
    return( color="#0000BB">$line);
}

// Initialize session

href="/manual/function.session-start.php">session_start color="#007700">();

// Register our variables
href="/manual/function.session-register.php">session_register color="#007700">("num_of_tries" color="#007700">);
href="/manual/function.session-register.php">session_register color="#007700">("guessed_chars" color="#007700">);

href="/manual/function.session-register.php">session_register color="#007700">("word" color="#007700">);

if($REQUEST_METHOD  color="#007700">== "POST"  color="#007700">&& !empty( color="#0000BB">$word color="#007700">) && !empty( color="#0000BB">$guess))

{
    
$guessed_chars color="#007700">[] = $guess color="#007700">;
    if(! href="/manual/function.strstr.php">strstr color="#007700">($word color="#007700">, $guess color="#007700">))
    {

         color="#FF8000">// Character not found - one try less left
         color="#0000BB">$num_of_tries++;
        
color="#0000BB">$message  color="#DD0000">"Wrong guess.";

    }
    else
    {
        
color="#0000BB">$message  color="#DD0000">"Correct guess!" color="#007700">;            
    }
 
}

else
{
    $word  color="#007700">=  href="/manual/function.strtolower.php">strtolower color="#007700">(hang_get_random_line color="#007700">("words.txt" color="#007700">));
    $message  color="#007700">=  color="#DD0000">"Welcome to Hangman!" color="#007700">;

     color="#0000BB">$num_of_tries  color="#007700">= 1 color="#007700">;
     color="#0000BB">$guessed_chars  color="#007700">= array();
}

$guessed_word  color="#007700">= $word color="#007700">;

color="#FF8000">// Construct the template guess word ("_" in place of not-yet-guessed characters)
for($i color="#007700">=0 color="#007700">; $i color="#007700">< href="/manual/function.strlen.php">strlen color="#007700">($word color="#007700">); $i color="#007700">++)

{
    if(! href="/manual/function.strstr.php">strstr color="#007700">( href="/manual/function.implode.php">implode color="#007700">("" color="#007700">, $guessed_chars color="#007700">), $word color="#007700">[$i color="#007700">]))
    {

         color="#0000BB">$guessed_word  color="#007700">=  href="/manual/function.str-replace.php">str_replace color="#007700">($word color="#007700">[$i color="#007700">], "_ " color="#007700">, $guessed_word color="#007700">);
    }
}

$button  color="#007700">= "Go" color="#007700">;

color="#FF8000">// Check whether the user has won or lost the game

if( color="#0000BB">$guessed_word  color="#007700">== $word  color="#007700">|| $word  color="#007700">== $guess color="#007700">)
{
    $message  color="#007700">=  color="#DD0000">"Correct guess - you've won!" color="#007700">;

     href="/manual/function.session-destroy.php">session_destroy color="#007700">();
    $button  color="#007700">= "New Game" color="#007700">;
     color="#0000BB">$num_of_tries  color="#007700">= 1 color="#007700">;
}
elseif($num_of_tries  color="#007700">==  color="#0000BB">HANG_MAX_TRIES  color="#007700">||  href="/manual/function.strlen.php">strlen color="#007700">($guess color="#007700">) > 1 color="#007700">)

{
    $message  color="#007700">=  color="#DD0000">"You've lost.  The word was  color="#007700">\"$word color="#007700">\"." color="#007700">;
     href="/manual/function.session-destroy.php">session_destroy color="#007700">();

    $button  color="#007700">=  color="#DD0000">"Try Again" color="#007700">;    
     color="#0000BB">$num_of_tries  color="#007700">= HANG_MAX_TRIES color="#007700">;
}

color="#FF8000">// Create new template instance

$tpl  color="#007700">= new  Color="#0000BB">EasyTemplate( color="#DD0000">"template.inc.html");

// Assign template variables
$tpl-> Color="#0000BB">assign( color="#DD0000">"ACTION" Color="#0000BB"> href="/manual/function.basename.php">basename color="#007700">($PHP_SELF color="#007700">));

$tpl-> Color="#0000BB">assign( color="#DD0000">"NUM_OF_TRIES" color="#0000BB">$num_of_tries);
$tpl-> Color="#0000BB">assign( color="#DD0000">"BUTTON" color="#0000BB">$button);

$tpl-> Color="#0000BB">assign( color="#DD0000">"MESSAGE" color="#0000BB">$message);
$tpl-> Color="#0000BB">assign( color="#DD0000">"WORD" color="#0000BB">$guessed_word);

$tpl-> Color="#0000BB">assign( color="#DD0000">"GUESSED_CHARS" color="#007700">, @ href="/manual/function.implode.php">implode color="#007700">("" color="#007700">, $guessed_chars color="#007700">));

// Output template

$tpl-> Color="#0000BB">easy_print();


Tobias Ratschiller is a New Media Consultant in Italy, specializing in the
creation of large scale dynamic websites.
He has provided consulting and implementation work for some of the world’s
largest websites and has contributed to several PHP titles.
Together with Till Gerken, he’s currently writing a book titled “Advanced Web
Application Development with PHP”, which will be published in July 2000 by New
Riders.
Apart from that, he teaches at seminars about usability, user interface design
and content management systems.

Tobias runs target="_blank">http://phpwizard.net/

2 Responses to “Session Handling with PHP 4”

  1. _____anonymous_____ Says:

    Best information I read so far about session management. Thanks a lot!

  2. _____anonymous_____ Says:

    I just wanted to say that it is clearly explained. Cool.