10.2 PHP Session Management

Developing applications that use PHP sessions is straightforward. The three important features of session management?identifying sessions, storing session variables, and cleaning up old sessions?are mostly taken care of by the PHP session management library.

In this section, we show you how sessions are started and ended, and how session variables are used, and provide strategies for designing session-based applications.

The out-of-the-box configuration of PHP session management uses disk-based files to store session variables, and our discussion in this section assumes this default PHP 4.3 behavior. Using files as the session store is adequate for most applications in which the number of concurrent sessions is limited. A more scalable solution that uses a MySQL database as a session store is provided in Appendix F.

10.2.1 Starting a Session

The session_start( ) function is used to create a new session. A session is unique to the interaction between a browser and a web database application. If you use your browser to access several sites at once, you'll have several unrelated sessions. Similarly, if several users access your application each has their own session. However, if you access an application using two browsers (or two browser windows) at the same time, in most cases the browsers will share the same session; this can lead to unpredictable behavior?that's the reason why many web sites warn against it.

The first time a user requests a script that calls session_start( ), PHP generates a new session ID and creates an empty file to store session variables. PHP also sends a cookie back to the browser that contains the session ID. However, because the cookie is sent as part of the HTTP headers in the response to the browser, you need to call session_start( ) before any other output is generated, just as with other functions that set HTTP header fields (this is a common source of error and it's discussed in more detail in Chapter 12).

The session identifier generated by PHP is a random string of 32 hexadecimal digits, such as fcc17f071bca9bf7f85ca281094390b4. When a new session is started, PHP creates a session file, using the session identifier, prefixed with sess_, for the filename. For example, the filename associated with our example session ID on a Unix system is /tmp/sess_fcc17f071bca9bf7f85ca281094390b4.

10.2.2 Using Session Variables

The session_start( ) function is also used to find an existing session. If a call is made to session_start( ), and a session has previously been started, PHP attempts to find the session file and initialize the session variables. PHP does this automatically by looking for the session cookie in the browser request whenever you call session_start( ). You don't need to do anything different when starting a new session or restoring an existing one. Even if the identified session file can't be found, session_start( ) simply creates a new session file.

Once a script has called session_start( ), PHP provides access to session variables through the superglobal associative array $_SESSION . When an existing session is found, PHP automatically reads the session variables from the session file into the array. PHP also automatically writes changes to the array back to the session file once the script ends. However, be careful: if your script doesn't call session_start( ), the $_SESSION array behaves like any other variable and any values are lost when the script ends.

The script shown in Example 10-1 uses session variables to record the number of times a user requests the page and the time of the first visit. The script is used with the template shown in Example 10-2.

Example 10-1. A simple PHP script that uses a session
<?php

  require_once "HTML/Template/ITX.php";



  // This call either creates a new session or finds an existing one.

  session_start( );



  // Check if the value for "count" exists in the session store

  // If not, set a value for "count" and "start"

  if (!isset($_SESSION["count"]))

  {

    $_SESSION["count"] = 0;

    $_SESSION["start"] = time( );

  }



  // Increment the count

  $_SESSION["count"]++;



  $template = new HTML_Template_ITX("./templates");

  $template->loadTemplatefile("example.10-2.tpl", true, true);



  $template->setVariable("SESSION", session_id( ));

  $template->setVariable("COUNT", $_SESSION["count"]);

  $template->setVariable("START", $_SESSION["start"]);

  $duration = time( ) - $_SESSION["start"];

  $template->setVariable("DURATION", $duration);



  $template->parseCurrentBlock( );



  $template->show( );

?>

Example 10-2. The session display template used with Example 10-1
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

                      "http://www.w3.org/TR/html401/loose.dtd">

<html>

<head>

  <meta http-equiv="Content-Type"

        content="text/html; charset=iso-8859-1">

  <title>Session State Test</title>

</head>

<body>

    <p>This page points at a session {SESSION}

    <br>count = {COUNT}

    <br>start = {START}

    <p>This session has lasted {DURATION} seconds.

</body>

</html>

Example 10-1 starts by initializing a session with a call to session_start( ). When the script is called for the first time, this creates a new session and initializes an empty $_SESSION array. When the script is called for the second or subsequent time, the stored values from the previous time the script was run are restored into the $_SESSION array. Whenever the script ends, the current values in the $_SESSION array are written back to the session store.

After initializing a session, the script tests if the $_SESSION array contains a value for the element count as follows:

// Check if the value for "count" exists in the session store

// If not, set a value for "count" and "start"

if (!isset($_SESSION["count"]))

{

  $_SESSION["count"] = 0;

  $_SESSION["start"] = time( );

}

If there's no value set, then this is the first time the script has been run and values for count and start are set up in the $_SESSION array. If count is set, then values have been read from the session store and these values are used instead.

The remainder of the script does the same thing whether or not this is the first time the script has been called. The value of count is incremented, and then the session ID (retrieved with the PHP library function session_id( )), count, start time, and session duration (the start time subtracted from the current time) are displayed.

The overall effect of the script is that each time you call it, the count increases by one and the duration updates to the length of time since you first called the script. However, as we explain later, if you don't request the script for a while, your session may be automatically destroyed by PHP's garbage collection and the count will begin again from one.

10.2.2.1 Unsetting session variables

To unset a session variable, you use the unset( ) function. To remove all the session variables, you can unset the whole $_SESSION array or re-assign a new array. Here are two examples:

// To remove the "count" session variable only

unset($_SESSION["count"]);



// To remove all the session variables without destroying the session

$_SESSION = array( );

10.2.2.2 Session variable types

Session variables can be of any type supported by PHP. However, if objects are saved as session variables, you should include class definitions for those objects in all scripts that call session_start( ), regardless of whether the scripts use the objects or not. This is needed so that PHP can correctly read and write objects from the session store (the file that stores the session variables).

To do this, you might store your class definition in an require file such as my_class.inc as shown in the following example:

require "my_class.inc";



// Find the session

start_session( );



// later on in the script, store an object as a session variable

$_SESSION["some_object"] = new my_class( );

Objects and classes are described in Chapter 4.

10.2.2.3 Serialization of session variables

PHP stores session variables in the session file by serializing the values. The serialized representation of a variable includes the name, the type, and the value as a stream of characters suitable for writing to a file. Here's an example of a file that was created when the script shown in Example 10-1 was run several times:

count|i:3;start|i:1049624957;

You don't need to worry how serialization occurs; PHP session management takes care of reading and writing session variables automatically.

10.2.3 Ending a Session

At some point in an application, sessions should be destroyed. For example, when a user logs out of an application, a call to the session_destroy( ) function should be made to clean-up the session variables and remove the session file. Be aware that while a call to session_destroy( ) removes the session file from the system, it doesn't remove the session cookie from the browser. In practice, it doesn't matter that the cookie is still there because PHP can transparently handle a session cookie being presented without a matching file on the server; in this case, it just creates a new session store with the name that matches the cookie value.

Example 10-3 shows how the session_destroy( ) function is called. Note that a session must be initialized with a call to session_start( ) before the session_destroy( ) call can be made. In the example, after destroying the session, the script redirects to a receipt page, logout.html. This avoids the reload problem discussed in Chapter 8.

Example 10-3. Ending a session
?php

  session_start( );

  session_destroy( );

  header("Location: logout.html");

?>

10.2.4 Designing Session-Based Applications

The PHP session management library provides a way of storing state but does not dictate how sessions are used in an application. When you design a session-based application, you therefore need to give thought to:

  • How and when a session is started

  • What data needs to be stored as session variables

  • When a session is destroyed

Sessions can be used in a variety of ways, and in this section, we describe two distinctly different types of session-based applications and provide design strategies for both. The following section uses a case study to show a third application of sessions.

10.2.4.1 Session to track authenticated users

Applications that require a user to log in?such as online banking?often use sessions to manage the user interaction. Figure 10-2 shows a typical flow of pages in such an application, and interaction with PHP session management.

Figure 10-2. Typical session-based application
figs/wda2_1002.gif


The login page collects the user credentials using an HTML form and passes these using the POST method to the setup script. The setup script is responsible for creating the new session with the first call to session_start( ) and for setting up the initial session variables. After the session is started, the setup script sends a Location header field, instructing the browser to relocate to the welcome page. Relocating to the welcome page prevents the setup script being re-run during the session, and avoids the reload problem we described in Chapter 8. The following fragment shows the sequence of code in the setup script:

// Process the POST variables

$username = $_POST["username"];



// Start the session

session_start( );



// Setup the session variables

$_SESSION["name"] = $username;

$_SESSION["counter"] = 0;



// Relocate to the welcome page

header("Location: welcome.php");

The welcome page, and the other application pages, begin by calling session_start( ) to set up the $_SESSION superglobal array with the session variables. Each page of the application then interacts with the $_SESSION variables as required. For example, consider the following fragment:

// Find the session

session_start( );



// Welcome the user to the application

print "Hi {$_SESSION["name"]), Welcome to my Application";

// ...



// Update session variables

$_SESSION["counter"]++;

// ...

Finally, the session is destroyed when the user requests the logout page. As with all the other pages that interact with the session, the logout script must begin by calling session_start( ). As discussed previously, a logout page should also redirect to a receipt page to avoid the reload problem. Here's an example logout script:

<?

// Find the session

session_start( );



// Destroy the session

session_destroy( );



// Redirect to a receipt page.

header("Location: logout.html");

?>

We develop a session-based user authentication framework that follows this pattern in Chapter 11.

10.2.4.2 Sessions to track anonymous users

Not all session-based applications follow the pattern presented in Figure 10-2, and not all require a user to log in. For example, consider an application that tracks the pages a user has visited. Multiple pages share information using session variables, however the user can enter the web site from any page. Each page in the application can potentially start the session, so each page should be written to test for the existence of the session variable.

The following fragment of code shows how a session variable $_SESSION["visited_pages"] is tested and set up prior to being used:

// Start or find the session

session_start( );



// Test for the required session variables and add

// them to the session store if they don't exist

if (!isset($_SESSION["visited_pages"]))

  $_SESSION["visited_pages"] = array( );



// Now use the session

// Add the name of this page to the end of the array

$_SESSION["visited_pages"][] = $_SERVER["PHP_SELF"];

If the variable isn't in the session store, it's initialized as a new array; if it is in the session store, it's restored automatically by the PHP session handler. After this first step, the name of the current page is added to the end of the array; the superglobal $_SERVER contains many elements that describe the server environment, including PHP_SELF which is the name of the current PHP script. This same fragment would be added to each script in the application. For compactness, you might put the fragment into a function in a require file.

The result of using this code is that, for every page the user visits, a new element is added to the array that contains the name of the page. If a user visits a page twice, a second element for that page is added. You could determine how many pages the user has visited by inspecting the size of the array with count( ), or you could print out the list of pages as in the following fragment:

// Print out all of the pages the user has visited

foreach ($_SESSION["visited_pages"] as $page)

  print "Thanks for visiting the {$page} page<br>";

For applications that record sensitive information in session variables, you should offer a page that destroys the session. However, for a non-sensitive application, setting a short session timeout and letting the PHP session management garbage collection remove the session is adequate. We discuss garbage collection and how to change the default session timeout later in this chapter.