ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


O'Reilly Book Excerpts: Web Database Applications with PHP & MySQL

PHP Session Management With Cookies

by David Lane and Hugh E. Williams

Related Reading

Web Database Applications with PHP, and MySQL
By Hugh E. Williams, David Lane

A fundamental characteristic of the Web is the stateless interaction between browsers and web servers. As discussed in Chapter 1, HTTP is a stateless protocol. Each HTTP request a browser sends to a web server is independent of any other request. The stateless nature of HTTP allows users to browse the Web by following hypertext links and visiting pages in any order. HTTP also allows applications to distribute or even replicate content across multiple servers to balance the load generated by a high number of requests. These features are possible because of the stateless nature of HTTP.

This stateless nature suits applications that allow users to browse or search collections of documents. However, applications that require complex user interaction can't be implemented as a series of unrelated, stateless web pages. An often-cited example is a shopping cart in which items are added to the cart while searching or browsing a catalog. The state of the shopping cart--the selected items--needs to be stored somewhere. When the user requests the order page, the items for that user need to be displayed.

Stateful web database applications can be built using sessions, and session management is the topic of this chapter. In this chapter we:

The focus of this chapter is on the session management provided by PHP. However, other techniques to keep state are briefly discussed, including the use of cookies.

Building Applications That Keep State

Applications sometimes need to use the result of one request when processing another. For example, a request that adds an item to a shopping cart needs to be remembered when the request is made to create the order. In other words, the state of the application needs to be stored between HTTP requests. There are two ways to achieve this: variables that hold the state can be stored in the browser and included with each request or variables can be stored on the server.

Most of this chapter is devoted to the second alternative, where the middle tier stores and manages the application state using sessions. However, in this section we briefly discuss solutions that store state in the client tier. One technique described in this section is the use of cookies. While cookies can store state in the client tier, they are also used in middle-tier session management, as described later in this chapter.

Managing State in the Client Tier

Data sent with the GET or POST methods can include the application state with each HTTP request. An illustration of this approach can be seen in the previous and next browsing features developed in Chapter 5. In this example, there are two pieces, or states, that need to be considered when a page is browsed: the query parameters the user provided and which page should be displayed.

The solution developed in Chapter 5 encodes the query and an offset as an embedded link. An example URL that displays the fourth page of results may be as follows:

http://localhost/example.5-10.php?regionName=All&offset=40

This solution allows navigation through large search result sets. Similar solutions are used in the URLs generated to jump between the results pages of web search engines such as Google or Altavista. Cookies can be used for the same purpose.

Encoding the variables that hold state with each HTTP request increases the amount of data that has to be transmitted over the Web, and when data is encoded using the GET method, applications can generate long URLs. While HTTP doesn't restrict the length of URLs, some older browsers and proxy servers do enforce limits.

When state variables are encoded as part of the URL, or even when they are included as cookies, it is possible for the user to change the values that are sent with the request. For example, a user can enter the following URL manually if she wants to see the records starting from row #7 in the result set:

http://localhost/example.5-10.php?regionName=All&offset=7

Changing the offset in a results page is harmless, but changing the item price of a bottle of wine is more serious. As discussed in Chapters 6 and 7, an application can't rely on data that is sent from the browser.

Cookies

Cookies are often used to store application state in a web browser. As with data sent with the GET or POST methods, cookies are sent with HTTP requests made by a browser. A cookie is a named piece of information that is stored in a web browser. A browser can create a cookie using JavaScript, but a cookie is usually sent from the web server to the client in the Set-Cookie header field as part of an HTTP response. Consider an example HTTP response:

HTTP/1.0 200
Content-Length: 1276
Content-Type: text/html
Date: Tue, 06 Nov 2001 04:12:49 GMT
Expires: Tue, 06 Nov 2001 04:12:59 GMT
Server: simwebs/3.1.6
Set-Cookie: animal=egg-laying-mammal
 
<html>...</html>

The web browser that receives this response remembers the cookie and includes it as the header field Cookie in subsequent HTTP requests to the same web server. For example, if a browser receives the response just shown, a subsequent request has the following format:

GET /duck/bill.php HTTP/1.0
Connection: Keep-Alive
Cookie: animal=egg-laying-mammal
Host: www.webdatabasebook.com
Referer: http://www.webdatabasebook.com/

There are several additional parameters used with the Set-Cookie header that define when a cookie can be included in a request:

Cookies can be included in an HTTP response using the header( ) function; however, the developer needs to know how to encode the cookie name, value, and the other parameters described earlier in the Set-Cookie header field. To simplify cookie creation, PHP provides the setcookie( ) function that generates a correct header field.

When an HTTP request that contains cookies is processed, PHP makes the values of the cookies available to the script in the global associative array $HTTP_COOKIE_VARS. If register_globals is enabled, a variable with the name of the cookie is also initialized by PHP; the register_globals feature in the php.ini file is discussed in Chapter 5. Example 8-1 tests to see if the variable $count has been set from a cookie, and either sets the value to 0 or increments $count accordingly. The script also creates a cookie named start, with the value set to the current time, when the $count is set to 0. The cookie start is set only at the beginning of this stateful interaction.


Example 8-1: Setting a cookie using PHP

<?php
 
// See if the HTTP request has set $count as the 
// result of a Cookie called "count"
if(!isset($count)) {
 // No cookie called count, set the counter to zero  
 $count = 0;
 
 // .. and set a cookie with the "start" time
 // of this stateful interaction
 $start = time(  );
 setcookie("start", $start, time(  )+600, "/", "", 0);
 
} else {
 $count++;
}
 
// Set a cookie "count" with the current value
setcookie("count", $count, time(  )+600, "/", "", 0);
 
?>
<!DOCTYPE HTML PUBLIC 
 "-//W3C//DTD HTML 4.0 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd" >
<html>
 <head><title>Cookies</title></head>
 <body>
 <p>This page comes with cookies: Enjoy! 
 <br>count = <?=$count ?>.
 <br>start = <?=$start ?>.
 <p>This session has lasted 
 <?php 
 $duration = time(  ) - $start; 
 echo "$duration"; 
 ?> 
 seconds.
 </body>
</html>

The setcookie( ) function is called with six arguments, although only the first--the name--is required:

int setcookie(string name, [string value], [int expire], [string path], string domain, [int secure])

The two calls to setcookie( ) in Example 8-1 add the Set-Cookie header field to the HTTP response. The first encodes the start cookie with the value of the current time as an integer returned from the time( ) function. The second encodes the count cookie with the value of the variable $count. Both cookies are set with the expiry date of the current time plus 600 seconds; that is, 10 minutes. With the path parameter set to /, the browser includes the cookies with all requests to the site. By passing an empty string for the domain, the browser includes the cookies only with requests to the domain of the machine serving this page. The final parameter 0 allows the browser to transmit the cookies over both secure and insecure connections.

Cookies can be used for simple applications that don't require complex data to be kept between requests. However, there is a limit on the number and size of cookies that can be set: a browser can keep only the last 20 cookies sent from a particular domain, and the values that a cookie can hold are limited to 4 KB in size. Also, there are arguments about both the privacy and the security of applications that use cookies, and users often disable cookie support in their browsers. We discuss some of the security issues of cookies in Chapter 9.

Session Management Over the Web

Storing the state in the web server -- the middle tier -- can solve the problem of increased request size and protect the state of an application from accidental or intentional changes a user might make.

A session is a way to identify and manage the state--the session variables--for a particular user. When a user sends an HTTP request, the middle tier must process the current request in the context of the user's session. When a session is started, the client is given a session identifier--often a cookie--that is included with subsequent requests to the server. The server uses the session identifier to locate the corresponding session before processing the request.

Rather than storing all the variables needed to maintain state and include them with each request, the browser stores a single session identifier that finds and initializes the variables stored on the server. The session identifier is like the ticket given at a cloak room. The ticket is much easier to carry around and ensures that the holder gets her own hat and coat.

One implication of storing session variables in the middle tier is that data needs to be stored for each session. The question is, for how long? Because HTTP is stateless, there is no way to know when a user has finished with a session. Ideally, the user logs out of an application, and the logout script ends the session. However, because a server can never be sure if a user is still there, the server needs to clean up old sessions that have not been used for a period of time. This last point is important, because sessions consume resources on the server, and dormant sessions may present a security risk. How long the timeout should be depends on the needs of the application, and we discuss this in more detail later in this chapter.

In summary, there are three characteristics session management over the Web must exhibit:

PHP Session Management

With the release of PHP4, session management was introduced as an extension to the PHP language. PHP provides several session-related functions, and developing applications that use PHP sessions is straightforward. The three important features of session management are mostly taken care of by the PHP scripting engine.

In this section, we present how to use PHP sessions, showing how sessions are started and ended and how session variables are used. We list the PHP functions for building session-based web applications. Because not all browsers support cookies, and some users actively disable them, we describe how to use PHP sessions without relying on cookies. Finally, we show how to configure PHP session management with a discussion on the garbage collection used to remove old sessions and other configuration parameters.

Overview

An overview of PHP session management is shown in Figure 8-1. When a user first enters the session-based application by making a request to a page that starts a session, PHP generates a session ID and creates a file that stores the session-related variables. PHP sets a cookie to hold the session ID in the response the script generates. The browser then records the cookie and includes it in subsequent requests. In the example shown in Figure 8-1, the script welcome.php records session variables in the session store, and a request to next.php then has access to those variables because of the session ID.

Diagram.
Figure 8-1. The interaction between the browser and the server when initial requests are made to a session-based application

The out-of-the-box configuration of PHP session management uses disk-based files to store session variables. Using files as the session store is adequate for most applications in which the numbers of concurrent sessions are limited. A more scalable solution that uses a MySQL database as a session store is provided in Appendix D.

Starting a Session

PHP provides a session_start( ) function that creates a new session and subsequently identifies and establishes an existing one. Either way, a call to the session_start( ) function initializes a session.

The first time a PHP script calls session_start( ), a session identifier is generated, and, by default, a Set-Cookie header field is included in the response. The response sets up a session cookie in the browser with the name PHPSESSID and the value of the session identifier. The PHP session management automatically includes the cookie without the need to call to the setcookie( ) or header( ) functions.

The session identifier (ID) is a random string of 32 hexadecimal digits, such as fcc17f071bca9bf7f85ca281094390b4. As with other cookies, the value of the session ID is made available to PHP scripts in the $HTTP_COOKIE_VARS associative array and in the $PHPSESSID variable.

When a new session is started, PHP creates a session file. With the default configuration, session files are written in the /tmp directory using the session identifier, prefixed with sess_, for the filename. The filename associated with our example session ID is /tmp/sess_fcc17f071bca9bf7f85ca281094390b4.

If a call is made to session_start( ), and the request contains the PHPSESSID cookie, PHP attempts to find the session file and initialize the associated session variables as discussed in the next section. However, if the identified session file can't be found, session_start( ) creates an empty session file.

Using Session Variables

Variables need to be registered with the session_register( ) function that's used in a session. If a session has not been initialized, the session_register( ) function calls session_start( ) to open the session file. Variables can be registered -- added to the session file -- with the session_register( ) call as follows:

// Register the variable named "foo"
session_register("foo");
$foo = "bar";

Note that it is the name of the variable that is passed to the session_register( ) function, not the variable itself. Once registered, session variables are made persistent and are available to scripts that initialize the session. PHP tracks the values of session variables and saves their values to the session file: there is no need to explicitly save a session variable before a script ends. In the previous example, the variable $foo is automatically saved in the session store with its value bar.

Variables can be removed from a session with the session_unregister( ) function call; again, the name of the variable is passed as the argument, not the variable itself. A variable that is unregistered is no longer available to other scripts that initialize the session. However, the variable is still available to the rest of the script immediately after the session_unregister( ) function call.

Scripts that initialize a session have access to the session variables through the associative array $HTTP_SESSION_VARS, and PHP automatically initializes the named session variables if register_globals is enabled.

Example 8-2 shows a simple script that registers two variables: an integer $count, which is incremented each time the script is called, and $start, which is set to the current time from the library function time( ) when the session is first initialized. The script tests if the variable $count has been registered to determine if a new session has been created. If the variable $count has been registered already, the script increments its value.

Do not use the existence of $PHPSESSID as indicative of a new session, or as a method to access the session ID. The first time a script is called and the session is created, the PHPSESSID cookie may not be set. Only subsequent requests are guaranteed to contain the PHPSESSID cookie. PHP provides a session_id( ) function that returns the session ID for the initialized session.

The script shown in Example 8-2 displays both variables: $count shows how many times the script has been called, and time( ) - $start shows how many seconds the session has lasted.


Example 8-2: Simple PHP script that uses a session

<?php
 // Initialize a session. This call either creates 
 // a new session or re-establishes an existing one.
 session_start(  );
 
 // If this is a new session, then the variable
 // $count will not be registered
 if (!session_is_registered("count")) 
 {
 session_register("count");
 session_register("start");
 
 $count = 0;
 $start = time(  );
 } 
 else 
 {
 $count++;
 }
 
 $sessionId = session_id(  );
 
?>
<!DOCTYPE HTML PUBLIC 
 "-//W3C//DTD HTML 4.0 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd" >
<html>
 <head><title>Sessions</title></head>
 <body>
 <p>This page points at a session 
 (<?=$sessionId?>)
 <br>count = <?=$count?>.
 <br>start = <?=$start?>.
 <p>This session has lasted 
 <?php 
 $duration = time(  ) - $start; 
 echo "$duration"; 
 ?> 
 seconds.
 </body>
</html>

Session variables can be of the type Boolean, integer, double, string, object, or arrays of those variable types. Care must be taken when using object session variables, because PHP needs access to the class definitions of registered objects when initializing an existing session. If objects are to be stored as session variables, you should include class definitions for those objects in all scripts that initialize sessions, whether the scripts use the class or not. Objects and classes are described in Chapter 2.

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 8-2 was run several times:

count|i:6;start|i:986096496;

A PHP developer need not worry how serialization occurs; PHP session management takes care of reading and writing session variables automatically.

Ending a Session

At some point in an application, sessions may need to be destroyed. For example, when a user logs out of an application, a call to the session_destroy( ) function can be made. A call to session_destroy( ) removes the session file from the system but doesn't remove the PHPSESSID cookie from the browser.

Example 8-3 shows how the session_destroy( ) function is called. A session must be initialized before the session_destroy( ) call can be made. You should also test to see if $PHPSESSID is a set variable before killing the session. This prevents the code from creating a session, then immediately destroying it if the script is called without identifying a session. However, if the user has previously held a session cookie, PHP initializes the $PHPSESSID variable, and the code redundantly creates and destroys a session.


Example 8-3: Ending a session

<?php
 // Only attempt to end the session if there 
 // is a $PHPSESSID set by the request.
 if(isset($PHPSESSID)) {
 $message = "<p>End of session ($PHPSESSID).";
 session_start(  );
 session_destroy(  );
 } else {
 $message = "<p>There was no session to destroy!";
 }
?>
<!DOCTYPE HTML PUBLIC 
 "-//W3C//DTD HTML 4.0 Transitional//EN"
 "http://www.w3.org/TR/html4/loose.dtd" >
<html>
 <head><title>Sessions</title></head>
 <body>
 <?=$message?>
 </body>
</html>

Functions for Accessing Sessions in PHP

In this section we list the key functions used to build session-based applications in PHP. Greater control over sessions can be achieved through the configuration of PHP--as we discuss in the "Configuration of PHP Session Management" section -- or by using GET variables to encode the session ID, as discussed in the next section.

Boolean session_start( )

Initializes a session by either creating a new session or using an identified one. Checks for the variable $PHPSESSID in the HTTP request. If a session identifier isn't included in the request, or an identified session isn't found, a new session is created. If a session ID is included in the request, and a session isn't found, a new session is created with the PHPSESSID encoded in the request. When an existing session is found, the session variables are read from the session store and initialized. Using PHP's default settings, a new session is created as a file in the /tmp directory. This function always returns true.

string session_id([string id])

Can be used in two ways: to return the ID of an initialized session and to set the value of a session ID before a session is created. When used to return the session ID, the function must be called without arguments after a session has been initialized. When used to set the value of the session ID, the function must be called with the ID as the parameter before the session has been initialized.

Boolean session_register(mixed name [, mixed ...])

Registers one or more variables in the session store. Each argument is the name of a variable, or an array of variable names, not the variable itself. Once a variable is registered, it becomes available to any script that identifies that session. This function calls the session_start( ) code internally if a session has not been initialized. The session_unregister( ) function is called to remove a variable from the session. Returns true when the variables are successfully registered.

Boolean session_is_registered(string variable_name)

Returns true if the named variable has been registered with the current session and false otherwise. Using this function to test if a variable is registered is a useful way to determine if a script has created a new session or initialized an existing one.

session_unregister(string variable_name)

Unregisters a variable with the initialized session. Like the session_register( ) function, the argument is the name of the variable, not the variable itself. Unlike the session_register( ) function, the session needs to be initialized before calling this function. Once a variable has been removed from a session with this call, it is no longer available to other scripts that initialize the session. However, the variable is still available to the rest of the script that calls session_unregister( ).

session_unset( )

Unsets the values of all session variables. This function doesn't unregister the actual session variables. A call to session_is_registered( ) still returns true for the session variables that have been unset.

Boolean session_destroy( )
Removes the session from the PHP session management. With PHP's default settings, a call to this function removes the session file from the /tmp directory. Returns true if the session is successfully destroyed and false otherwise.

David Lane is a software engineer and IT manager with the Multimedia Database Systems group at RMIT University in Melbourne, Australia.

Hugh E. Williams is a software design engineer at Microsoft's Windows Live Search in Redmond, WA. Previously, he was the Associate Professor in Information Retrieval at RMIT University in Melbourne, Australia.


View catalog information for Web Database Applications with PHP & MySQL

Return to PHP DevCenter.

Copyright © 2009 O'Reilly Media, Inc.