Using MySQL-Based Storage with the PHP Session Manager

19.3.1 Problem

You want to use session storage for PHP scripts.

19.3.2 Solution

PHP 4 includes session managment. By default, it uses temporary files for backing store, but you can configure it to use MySQL instead.

19.3.3 Discussion

PHP 4 includes a native session manager. This section shows how to use it and how to extend it by implementing a storage module that saves session data in MySQL.[4] If your PHP configuration has both the track_vars and register_globals configuration directives enabled, session variables will exist as global variables of the same names in your script. (track_vars is enabled automatically for PHP 4.0.3 or later; for earlier versions, you should enable it explicitly.) If register_globals is not enabled, you'll need to access session variables as elements of the $HTTP_SESSION_VARS global array or the $_SESSION superglobal array. This is less convenient than relying on register_globals, but is also more secure. (Recipe 18.6 discusses PHP's global and superglobal arrays and the security implications of register_globals.)

[4] PHP 3 provides no session support. PHP 3 users who require session support may wish to look into PHPLIB or another package that includes a session manager.

19.3.4 The PHP 4 Session Management Interface

PHP's session management capabilities are based on a small set of functions, all of which are documented in the PHP manual. The following list describes those likely to be most useful for day-to-day session programming:

session_start ( )

Opens a session and extracts any variables previously stored in it, making them available in the script's global namespace. For example, a session variable named x becomes available as $_SESSION["x"] or $HTTP_SESSION_VARS["x"]. If register_globals is enabled, x also becomes available as the global variable $x.

session_register ( var_name)

Registers a variable in the session by setting up an association between the session record and a variable in your script. For example, to register $count, do this:

session_register ("count");

If you make any changes to the variable while the session remains open, the new value will be saved to the session record when the session is closed. Observe that variables are registered by name rather than by value or by reference:

session_register ($count); # incorrect session_register (&$count); # incorrect

Several variables may be registered at once by passing an array that contains multiple names rather than by passing a single name:

session_register (array ("count", "timestamp"));

Registering a variable implicitly starts a session, which means that if a script calls session_register( ), it need not call session_start( ) first. However, session_register( ) is effective only if register_globals is enabled. To avoid reliance on register_globals, you should call session_start( ) explicitly and get your session variables from either the $_SESSION or the $HTTP_SESSION_VARS array.

session_unregister ( var_name)

Unregisters a session variable so that it is not saved to the session record.

session_write_close ( )

Writes the session data and closes the session. Normally you need not call this function; PHP saves an open session automatically when your script ends. However, it may be useful to save and close the session explicitly if you want to modify session variables without having the changes tracked in the session data. In that case, you should call this function to close the session before making the changes.

session_destroy ( )

Removes the session and any data associated with it.

session_name ($name)

The PHP session manager knows which session to use by means of the session identifier. It looks for the identifier in a global variable named $PHPSESSID; in a cookie, GET, or POST variable named PHPSESSID; or in a URL parameter of the form PHPSESSID=value. (If none of these are found, the session manager generates a new identifier and begins a new session.) The default identifier name is PHPSESSID, but you can change it. To make a global (site-wide) change, edit the session.name configuration directive in php.ini. To make the change for an individual script, call session_name($name) before starting the session, where $name represents the session name to use. To find out the current session identifier name, call session_name( ) with no argument.

The following example demonstrates one of the simplest uses for a session, which is to display a counter showing the number of requests received so far during the course of the session:

session_start ( ); session_register ("count"); if (!isset ($count)) $count = 0; ++$count; printf ("This session has been active for %d requests.", $count);

session_start( ) opens the session and extracts its contents into the script's global namespace. (For the initial request, this has no effect because the session is empty.) session_register( ) registers the count session variable to cause changes to the corresponding PHP variable $count to be tracked in the session data. For the first request, no such variable will be present in the session. This is detected by the isset( ) test, which initializes the counter. (On subsequent requests, registering count will cause $count to have the value assigned to it during the previous request.) Next, the counter's value is incremented and printed. When the script ends, PHP implicitly invokes session_write_close( ), which saves the new counter value to the session automatically.

The example uses session_register( ) and thus assumes that register_globals is enabled. Later on, we'll discuss how to avoid this limitation.

19.3.5 Specifying a User-Defined Storage Module

The PHP session management interface just described makes no reference to any kind of backing store. That is, the description specifies nothing about how session information actually gets saved. By default, PHP uses temporary files to store session data, but the session interface is extensible so that other storage modules can be defined. To override the default storage method and store session data in MySQL, you must do several things:

19.3.5.1 Creating the session table

Any MySQL-based storage module needs a database table in which to store session information. Create a table named php_session that includes the following columns:

CREATE TABLE php_session ( id CHAR(32) NOT NULL, data BLOB, t TIMESTAMP NOT NULL, PRIMARY KEY (id) );

You'll recognize the structure of this table as quite similar to the sessions table used by the Apache::Session Perl module. The id column holds session identifiers, which are unique 32-character strings (they look suspiciously like Apache:Session identifiers, which is not surprising, given that PHP uses MD5 values, just like the Perl module). data holds session information. PHP serializes session data into a string before storing it, so php_session needs only a large generic string column to hold the resulting serialized value. The t column is a TIMESTAMP that MySQL updates automatically whenever a session record is updated. This column is not required, but it's useful for implementing a garbage collection policy based on each session's last update time.

A small set of queries suffices to manage the contents of the php_session table as we have defined it:

These queries form the basis of the routines that make up our MySQL-backed storage module. The primary function of the module is to open and close MySQL connections and to issue the proper queries at the appropriate times.

19.3.5.2 Writing the storage management routines

User-defined session storage modules have a specific interface, implemented as a set of handler routines that you register with PHP's session manager by calling session_set_save_handler( ). The format of the function is as follows, where each argument is a handler routine name specified as a string:

session_set_save_handler ( "mysql_sess_open", # function to open a session "mysql_sess_close", # function to close a session "mysql_sess_read", # function to read session data "mysql_sess_write", # function to write session data "mysql_sess_destroy", # function to destroy a session "mysql_sess_gc" # function to garbage-collect old sessions );

You can name the handler routines as you like; they need not necessarily be named mysql_sess_open( ), mysql_sess_close( ), and so forth. They should, however, be written according to the following specifications:

mysql_sess_open ($save_path, $sess_name)

Performs whatever actions are necessary to begin a session. $save_path is the name of the location where sessions should be stored; this is useful for file storage only. $sess_name indicates the name of the session identifier (for example, PHPSESSID). For a MySQL-based storage manager, both arguments can be ignored. The function should return TRUE or FALSE to indicate whether or not the session was opened successfully.

mysql_sess_close ( )

Closes the session, returning TRUE for success or FALSE for failure.

mysql_sess_read ($sess_id)

Retrieves the data associated with the session identifier and returns it as a string. If there is no such session, the function should return an empty string. If an error occurs, it should return FALSE.

mysql_sess_write ($sess_id, $sess_data)

Saves the data associated with the session identifier, returning TRUE for success or FALSE for failure. PHP itself takes care of serializing and unserializing the session contents, so the read and write functions need deal only with serialized strings.

mysql_sess_destroy ($sess_id)

Destroys the session and any data associated with it, returning TRUE for success or FALSE for failure. For MySQL-based storage, destroying a session amounts to deleting the record from the php_session table that is associated with the session ID.

mysql_sess_gc ($gc_maxlife)

Performs garbage collection to remove old sessions. This function is invoked on a probabilistic basis. When PHP receives a request for a page that uses sessions, it calls the garbage collector with a probability defined by the session.gc_probability configuration directive in php.ini. For example, if the probability value is 1 (that is, 1%), PHP calls the collector approximately once every hundred requests. If the value is 100, it calls the collector for every requestwhich probably would result in more processing overhead than you'd want.

The argument to gc( ) is the maximum session lifetime in seconds. Sessions older than that should be considered subject to removal. The function should return TRUE for success or FALSE for failure.

The handler routines are registered by calling session_set_save_handler( ), which should be done in conjunction with informing PHP that you'll be using a user-defined storage module. The default storage management method is defined by the session.save_handler configuration directive. You can change the method globally by modifying the php.ini initialization file, or within individual scripts:

To make it easy to access an alternative session storage module, it's useful to create a library file, Cookbook_Session.php. Then the only thing a script need do to use the library file is to include it prior to starting the session. The outline of the file looks like this:

The library file includes Cookbook.php so that it can access the connection routine for opening a connection to the cookbook database. Then it defines the handler routines (we'll get to the details of these functions shortly). Finally, it initializes the connection identifier, tells PHP to get ready to use a user-defined session storage manager, and registers the handler functions. Thus, a PHP script that wants to store sessions in MySQL performs all the necessary setup simply by including the Cookbook_Session.php file:

include_once "Cookbook_Session.php";

The interface provided by the Cookbook_Session.php library file exposes a global database connection identifier variable ($mysql_sess_conn_id) and a set of handler routines named mysql_sess_open( ), mysql_sess_close( ), and so forth. Scripts that use the library should avoid using these global names for other purposes.

Now let's see how to implement each handler routine.

19.3.5.3 Using the storage module

Install the Cookbook_Session.php file in a public library directory accessible to your scripts. (On my system, I put PHP library files in /usr/local/apache/lib/php.) To try out the storage module, install the following example script, sess_track.php, in your web tree and invoke it a few times to see how the information display changes (or, rather, to see if it changes; under some circumstances, the script will fail, as we'll discuss shortly):

= 10) # destroy session variables after 10 invocations { session_unregister ("count"); session_unregister ("timestamp"); } # Produce the output page ?>

Категории