CHAPTER 18

Session Handlers

These days, using HTTP sessions to track persistent information such as user preferences within even the simplest of applications is more the rule than the exception. Therefore, no matter whether you are completely new to Web development or are a grizzled veteran hailing from another language, you should take time to carefully read this chapter.

Available since the version 4.0 release, PHP's session-handling capabilities remain one of the coolest and most discussed features. In this chapter you'll learn all about the feature, including:

  • Why session handling is necessary, and useful
  • How to configure PHP to most effectively use the feature
  • How to create and destroy sessions, and manage session variables
  • Why you might consider managing session data in a database, and how to do it

What Is Session Handling?

The Hypertext Transfer Protocol (HTTP) defines the rules used to transfer text, graphics, video, and all other data via the World Wide Web. It is a stateless protocol, meaning that each request is processed without any knowledge of any prior or future requests. Although HTTP's simplicity is a significant contributor to its ubiquity, its stateless nature has long been a problem for developers who wish to create complex Web-based applications that must be able to adjust to user-specific behavior and preferences. To remedy this problem, the practice of storing bits of information on the client's machine, in what are commonly called cookies, quickly gained acceptance, offering some relief to this conundrum. However, limitations on cookie size and the number of cookies allowed, and various inconveniences surrounding their implementation, prompted developers to devise another solution: session handling.

Session handling is essentially a clever workaround to this problem of statelessness. This is accomplished by assigning to each site visitor a unique identifying attribute, known as the session ID (SID), and then correlating that SID with any number of other pieces of data, be it number of monthly visits, favorite background color, or middle name—you name it. In relational database terms, you can think of the SID as the primary key that ties all the other user attributes together. But how is the SID continually correlated with the user, given the stateless behavior of HTTP? It can be done in two different ways:

  • Cookies: One ingenious means for managing user information actually builds upon the original method of using a cookie. When a user visits a Web site, the server stores information about the user, such as their preferences, in a cookie and sends it to the browser, which saves it. As the user executes a request for another page, the server retrieves the user information and uses it, for example, to personalize the page. However, rather than storing the user preferences in the cookie, the SID is stored in the cookie. As the client navigates throughout the site, the SID is retrieved when necessary, and the various items of data correlated with that SID are furnished for use within the page. In addition, because the cookie can remain on the client even after a session ends, it can be read in during a subsequent session, meaning that persistence is maintained even across long periods of time and inactivity. However, keep in mind that because cookie acceptance is a matter ultimately controlled by the client, you must be prepared for the possibility that the user has disabled cookie support within the browser or has purged the cookies from their machine.
  • URL rewriting: The second method used for SID propagation simply involves appending the SID to every local URL found within the requested page. This results in automatic SID propagation whenever the user clicks one of those local links. This method, known as URL rewriting, removes the possibility that your site's session-handling feature could be negated if the client disables cookies. However, this method has its drawbacks. First, URL rewriting does not allow for persistence between sessions, because the process of automatically appending a SID to the URL does not continue once the user leaves your site. Second, nothing stops a user from copying that URL into an e-mail and sending it to another user; as long as the session has not expired, the session will continue on the recipient's workstation. Consider the potential havoc that could occur if both users were to simultaneously navigate using the same session, or if the link recipient was not meant to see the data unveiled by that session. For these reasons, the cookie-based methodology is recommended. However, it is ultimately up to you to weigh the various factors and decide for yourself.

The Session-Handling Process

Because PHP can be configured to autonomously control the entire session-handling process with little programmer interaction, you may consider the gory details somewhat irrelevant. However, there are so many potential variations to the default procedure that taking a few moments to better understand this process would be well worth your time.

The very first task executed by a session-enabled page is to determine whether a valid session already exists or a new one should be initiated. If a valid session doesn't exist, one is generated and correlated with that user, using one of the SID propagation methods described earlier. PHP determines whether a session already exists by finding the SID either within the requested URL or within a cookie. However, you're also capable of doing so programmatically. For instance, if the session name is sid and it's appended to the URL, you could retrieve the value with the following variable:

$_GET['sid']

If it's stored within a cookie, you can retrieve it like this:

$_COOKIE['sid']

Once retrieved, you can either begin correlating information with that SID or retrieve previously correlated SID data. For example, suppose that the user is browsing various news articles on the site. Article identifiers could be mapped to the user's SID, allowing you to compile a list of articles that the user has read, and display that list as the user continues to navigate. In the coming sections, you'll learn how to store and retrieve this session information.


Tip You can also retrieve cookie information via the $_REQUEST superglobal. For instance, if the session name is sid, $_REQUEST['sid'] will retrieve the SID, just as $_COOKIE['sid'] would. However, for purposes of clarity, consider using the superglobal that best matches the variable's place of origin.


This process continues until the user either closes the browser or navigates to an external site. If you use cookies, and the cookie's expiration date has been set to some date in the future, should the user return to the site before that expiration date, the session could be continued as if the user never left. If you use URL rewriting, the session is definitively over, and a new one must begin the next time the user visits the site.

In the coming sections, you'll learn about the configuration directives and functions responsible for carrying out this process.

Configuration Directives

Almost 30 configuration directives are responsible for tweaking PHP's session-handling behavior. Because many of these directives play such an important role in determining this behavior, you should take some time to become familiar with the directives and their possible settings. The most relevant are introduced in this section.

Managing the Session Storage Media

The session.save_handler directive determines how the session information will be stored. Its prototype looks like this:

session.save_handler = files|mm|sqlite|user

Session data can be stored in four ways: within flat files (files), within volatile memory (mm), using the SQLite database (sqlite), or through user-defined functions (user). Although the default setting, files, will suffice for many sites, keep in mind for active Web sites that the number of session-storage files could potentially run into the thousands, and even the hundreds of thousands over a given period of time.

The volatile memory option is the fastest for managing session data, but also the most volatile because the data is stored in RAM. The sqlite option takes advantage of the new SQLite extension to manage session information transparently using this lightweight database (see Chapter 22 for more information about SQLite). The fourth option, although the most complicated to configure, is also the most flexible and powerful, because custom handlers can be created to store the information in any media the developer desires. Later in this chapter you'll learn how to use this option to store session data within a MySQL database.

Setting the Session Files Path

If session.save_handler is set to the files storage option, then the session.save_path directive must be set in order to identify the storage directory. Its prototype looks like this:

session.save_path = string

By default session.save_path is set to /tmp. Keep in mind that this should not be set to a directory located within the server document root, because the information could easily be compromised via the browser. In addition, this directory must be writable by the server daemon.

For reasons of efficiency, you can define session.save_path using the syntax N;/path, where N is an integer representing the number of subdirectories N-levels deep in which session data can be stored. This is useful if session.save_handler is set to files and your Web site processes a large number of sessions, because it makes storage more efficient since the session files will be divided into various directories rather than stored in a single, monolithic directory. If you do decide to take advantage of this feature, PHP will not automatically create these directories for you. However, Linux users can automate the process by executing a script named mod_files.sh, located in the ext/session directory. If you're using Windows, this shell script isn't supported, although writing a compatible script using VBScript should be fairly trivial.

Automatically Enabling Sessions

By default a page will be session-enabled only by calling the function session_start() (introduced later in the chapter). However, if you plan on using sessions throughout the site, you can forgo using this function by setting session.auto_start to 1. Its prototype follows:

session.auto_start = 0|1

One drawback to enabling this directive is that it prohibits you from storing objects within sessions, because the class definition would need to be loaded prior to starting the session in order for the objects to be re-created. Because session.auto_start would preclude that from happening, you need to leave this disabled if you want to manage objects within sessions.

Setting the Session Name

By default PHP will use a session name of PHPSESSID. However, you're free to change this to whatever name you desire using the session.name directive. Its prototype follows:

session.name = string

Choosing Cookies or URL Rewriting

If you'd like to maintain a user's session over multiple visits to the site, you should use a cookie so the SID can be later retrieved. If user data is to be used only over the course of a single site visit, then URL rewriting will suffice (although you should keep in mind the URL rewriting security issues mentioned earlier in this chapter). You can choose the method using session.use_cookies. Setting this directive to 1 (the default) results in the use of cookies for SID propagation; setting it to 0 causes URL rewriting to be used. Its prototype follows:

session.use_cookies = 0|1

Keep in mind that when session.use_cookies is enabled, there is no need to explicitly call a cookie-setting function (via PHP's set_cookie(), for example), because this will be automatically handled by the session library. If you choose cookies as the method for tracking the user's SID, then there are several other directives that you must consider, each of which is introduced in the following entries.

Automating URL Rewriting

If session.use_cookies is disabled, the user's unique SID must be attached to the URL in order to ensure ID propagation. This can be handled either explicitly, by manually appending the variable $SID to the end of each URL, or automatically, by enabling the directive session.use_trans_sid. Its prototype follows:

session.use_trans_sid = 0|1

Not surprisingly, if you commit to using URL rewrites, you should enable this directive to eliminate the possibility of human error during the rewrite process.

Setting the Session Cookie Lifetime

The session.cookie_lifetime directive determines the session cookie's period of validity. Its prototype follows:

session.cookie_lifetime = integer

The lifetime is specified in seconds, so if the cookie should live 1 hour, then this directive should be set to 3600. If this directive is set to 0 (the default), then the cookie will live until the browser is restarted.

Setting the Session Cookie's Valid URL Path

The directive session.cookie_path determines the path in which the cookie is considered valid. The cookie is also valid for all child directories falling under this path. Its prototype follows:

session.cookie_path = string

For example, if it is set to /, then the cookie will be valid for the entire Web site. Setting it to /books causes the cookie to be valid only when called from within the http://www.example.com/books/ path.

Setting the Session Cookie's Valid Domain

The directive session.cookie_domain determines the domain for which the cookie is valid. This directive is necessary because it prevents other domains from reading your cookies. Its prototype follows:

session.cookie_domain = string

The following example illustrates its use:

session.cookie_domain = www.example.com

If you'd like a session to be made available for site subdomains, say customers. example.com, intranet.example.com, and www2.example.com, set this directive like this:

session.cookie_domain = .example.com

Validating Sessions Using a Referer

Using URL rewriting as the means for propagating session IDs opens up the possibility that a particular session state could be viewed by numerous individuals simply by copying and disseminating a URL. The session.referer_check directive lessens this possibility by specifying a substring that each referrer is validated against. If the referrer does not contain this substring, the SID will be invalidated. Its prototype follows:

session.referer_check = string

Setting Caching Directions for Session-Enabled Pages

When working with sessions, you may want to exert greater control over how session-enabled pages are cached by the user's browser and by any proxies residing between the server and user. The session.cache_limiter directive modifies these pages' cache-related headers, providing instructions regarding caching preference. Its prototype follows:

session.cache_limiter = string

Five values are available:

none:
This setting disables the transmission of any cache control headers along with the session-enabled pages.
nocache:
This is the default setting. This setting ensures that every request is first sent to the originating server before a potentially cached version is offered.
private:
Designating a cached document as private means that the document will be made available only to the originating user. It will not be shared with other users.
private_no_expire:
This is a variation of the private designation, resulting in no document expiration date being sent to the browser. This was added as a workaround for various browsers that became confused by the Expire header sent along when this directive is set to private.
public:
This setting deems all documents as cacheable, even if the original document request requires authentication.

Setting Cache Expiration Time for Session-Enabled Pages

The session.cache_expire directive determines the number of seconds (180 by default) that cached session pages are made available before new pages are created. Its prototype follows:

session.cache_expire = integer

If session.cache_limiter is set to nocache, this directive is ignored.

Setting the Session Lifetime

The session.gc_maxlifetime directive determines the duration, in seconds (by default 1440), for which a session is considered valid. Its prototype follows:

session.gc_maxlifetime = integer

Once this limit is reached, the session information will be destroyed, allowing for the recuperation of system resources.

Working with Sessions

This section introduces many of the key session-handling tasks, presenting the relevant session functions along the way. Some of these tasks include the creation and destruction of a session, designation and retrieval of the SID, and storage and retrieval of session variables. This introduction sets the stage for the next section, in which several practical session-handling examples are provided.

Starting a Session

Remember that HTTP is oblivious to both the user's past and future conditions. Therefore, you need to explicitly initiate and subsequently resume the session with each request. Both tasks are done using the session_start() function. Its prototype looks like this:

boolean session_start()

Executing session_start() will create a new session if no SID is found, or continue a current session if an SID exists. You use the function simply by calling it like this:

session_start();

Note that the session_start() function reports a successful outcome regardless of the result. Therefore, using any sort of exception handling in this case will prove fruitless.

You can eliminate execution of this function altogether by enabling the configuration directive session.auto_start. Keep in mind, however, that this will start or resume a session for every PHP-enabled page.

Destroying a Session

Although you can configure PHP's session-handling directives to automatically destroy a session based on an expiration time or probability, sometimes it's useful to manually cancel out the session yourself. For example, you might want to enable the user to manually log out of your site. When the user clicks the appropriate link, you can erase the session variables from memory, and even completely wipe the session from storage, done through the session_unset() and session_destroy() functions, respectively.

The session_unset() function erases all session variables stored in the current session, effectively resetting the session to the state in which it was found upon creation (no session variables registered). Its prototype looks like this:

void session_unset()

While executing session_unset() will indeed delete all session variables stored in the current session, it will not completely remove the session from the storage mechanism. If you want to completely destroy the session, you need to use the function session_destroy(), which invalidates the current session by completely removing the session from the storage mechanism. Keep in mind that this will not destroy any cookies on the user's browser. Its prototype looks like this:

boolean session_destroy()

If you are not interested in using the cookie beyond the end of the session, just set session.cookie_lifetime to 0 (its default value) in the php.ini file.

Setting and Retrieving the Session ID

Remember that the SID ties all session data to a particular user. Although PHP will both create and propagate the SID autonomously, there are times when you may wish to manually set or retrieve it. The function session_id() is capable of carrying out both tasks. Its prototype looks like this:

string session_id([string sid])

The function session_id() can both set and get the SID. If it is passed no parameter, the function session_id() returns the current SID. If the optional sid parameter is included, the current SID will be replaced with that value. An example follows:

<?php
    session_start();
    echo "Your session identification number is ".session_id();
?>

This results in output similar to the following:


Your session identification number is 967d992a949114ee9832f1c11c


Creating and Deleting Session Variables

Session variables are used to manage the data intended to travel with the user from one page to the next. These days, however, the preferred method involves simply setting and deleting these variable just like any other, except that you need to refer to it in the context of the $_SESSION superglobal. For example, suppose you wanted to set a session variable named username:

<?php
    session_start();
    $_SESSION['username'] = "jason";
    printf("Your username is %s.", $_SESSION['username']);
?>

This returns the following:


Your username is jason.

To delete the variable, you can use the unset() function:

<?php
   session_start();
   $_SESSION['username'] = "jason";
   printf("Your username is: %s <br />", $_SESSION['username']);
   unset($_SESSION['username']);
   printf("Username now set to: %s", $_SESSION['username']);
?>

This returns:


Your username is: jason
Username now set to:


Caution You might encounter older learning resources and newsgroup discussions referring to the functions session_register() and session_unregister(), which were once the recommended way to create and destroy session variables, respectively. However, because these functions rely on a configuration directive called register_globals, which was disabled by default as of PHP 4.2.0, and removed entirely as of PHP 6.0, you should instead use the variable assignment and deletion methods as described in this section.


Encoding and Decoding Session Data

Regardless of the storage media, PHP stores session data in a standardized format consisting of a single string. For example, the contents of a session consisting of two variables, namely username and loggedon, is displayed here:

username|s:5:"jason";loggedon|s:20:"Feb 16 2008 22:32:29";

Each session variable reference is separated by a semicolon and consists of three components: the name, length, and value. The general syntax follows:

name|s:length:"value";

Thankfully, PHP handles the session encoding and decoding autonomously. However, sometimes you might wish to execute these tasks manually. Two functions are available for doing so: session_encode() and session_decode(), respectively.

Encoding Session Data

session_encode() offers a particularly convenient method for manually encoding all session variables into a single string. Its prototype follows:

string session_encode()

You might then insert this string into a database and later retrieve it, decoding it with session_decode(), for example.

As an example, assume that a cookie containing that user's SID is stored on his computer. When the user requests the page containing the following listing, the user ID is retrieved from the cookie. This value is then assigned to be the SID. Certain session variables are created and assigned values, and then all of this information is encoded using session_encode(), readying it for insertion into a database.

<?php
    // Initiate session and create a few session variables
    session_start();

    // Set a few session variables.
    $_SESSION['username'] = "jason";
    $_SESSION['loggedon'] = date("M d Y H:i:s");

    // Encode all session data into a single string and return the result
    $sessionVars = session_encode();
    echo $sessionVars;
?>

This returns:

username|s:5:"jason";loggedon|s:20:"Feb 16 2008 22:32:29";

Keep in mind that session_encode() will encode all session variables available to that user, not just those that were registered within the particular script in which session_encode() executes.

Decoding Session Data

Encoded session data can be decoded with session_decode(). Its prototype looks like this:

boolean session_decode(string session_data)

The input parameter session_data represents the encoded string of session variables. The function will decode the variables, returning them to their original format, and subsequently return TRUE on success and FALSE otherwise. Continuing the previous example, suppose that some session data was encoded and stored in a database, namely the SID and the variables $_SESSION['username'] and $_SESSION['loggedon']. In the following script, that data is retrieved from the table and decoded:

<?php
    session_start();
    $sid = session_id();

    // Encoded data retrieved from database looks like this:
    // $sessionVars = username|s:5:"jason";loggedon|s:20:"Feb 16 2008 22:32:29";

    session_decode($sessionVars);

   echo "User ".$_SESSION['username']." logged on at ".$_SESSION['loggedon'].".";

?>

This returns:


User jason logged on at Feb 16 2008 22:55:22.

This hypothetical example is intended solely to demonstrate PHP's session encoding and decoding function. If you would like to store session data in a database, there's a much more efficient method that involves defining custom session handlers, and tying those handlers directly into PHP's API. How this is accomplished is demonstrated later in this chapter.

Practical Session-Handling Examples

Now that you're familiar with the basic functions that make session handling work, you are ready to consider a few real-world examples. The first example shows you how to create a mechanism that automatically authenticates returning registered site users. The second example demonstrates how session variables can be used to provide the user with an index of recently viewed documents. Both examples are fairly commonplace, which should not come as a surprise given their obvious utility. What may come as a surprise is the ease with which you can create them.


Note If you're unfamiliar with the MySQL database and are confused by the syntax found in the following examples, consider reviewing the material found in Chapter 30.


Automatically Logging In Returning Users

Once a user has logged in, typically by supplying a username and password combination that uniquely identifies that user, it's often convenient to allow the user to later return to the site without having to repeat the process. You can do this easily using sessions, a few session variables, and a MySQL table. Although there are many ways to implement this feature, checking for an existing session variable (namely $username) is sufficient. If that variable exists, the user can automatically log in to the site. If not, a login form is presented.


Note By default, the session.cookie_lifetime configuration directive is set to 0, which means that the cookie will not persist if the browser is restarted. Therefore, you should change this value to an appropriate number of seconds in order to make the session persist over a period of time.


The MySQL table, users, is presented in Listing 18-1.

Listing 18-1. The users Table

CREATE TABLE users (
   id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
   username VARCHAR(10) NOT NULL,
   pswd VARCHAR(10) NOT NULL,
   PRIMARY KEY(id)
);

A snippet (login.html) used to display the login form to the user if a valid session is not found is presented next:

<p>
    <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
        Username:<br /><input type="text" name="username" size="10" /><br />
        Password:<br /><input type="password" name="pswd" SIZE="10" /><br />
        <input type="submit" value="Login" />
    </form>
</p>

Finally, the logic used to manage the auto-login process follows:

<?php

    session_start();

    // Has a session been initiated previously?
    if (! isset($_SESSION['username'])) {

        // If no previous session, has the user submitted the form?
        if (isset($_POST['username']))
        {
            $username = mysqli_real_escape_string($_POST['username']);
            $pswd = mysqli_real_escape_string($_POST['pswd']);

            // Connect to the MySQL server and select the database
            mysql_connect("localhost","webuser","secret");
            mysql_select_db("chapter18");

            // Look for the user in the users table.
            $query = "SELECT username FROM users
                WHERE username='$username' AND pswd='$pswd'";
            $result = mysql_query($query);

            // Has the user been located?
            if (mysql_numrows($result) == 1)
            {
                $_SESSION['username'] = mysql_result($result,0,"username");
                echo "You've successfully logged in. ";
            }

        // If the user has not previously logged in, show the login form
        } else {
            include "login.html";
        }

    // The user has returned. Offer a welcoming note.
    } else {
        printf("Welcome back, %s!", $_SESSION['username']);
    }
?>

At a time when users are inundated with the need to remember usernames and passwords for every imaginable type of online service, from checking e-mail to library book renewal to reviewing a bank account, providing an automatic login feature when the circumstances permit will surely be welcomed by your users.

Generating a Recently Viewed Document Index

How many times have you returned to a Web site, wondering where exactly to find that great PHP tutorial that you nevertheless forgot to bookmark? Wouldn't it be nice if the Web site were able to remember which articles you read, and present you with a list whenever requested? This example demonstrates such a feature.

The solution is surprisingly easy, yet effective. To remember which documents have been read by a given user, you can require that both the user and each document be identified by a unique identifier. For the user, the SID satisfies this requirement. The documents can be identified really in any way you wish, but this example uses the article's title and URL, and assumes that this information is derived from data stored in a database table named articles, displayed here:

CREATE TABLE articles (
   id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
   title VARCHAR(50),
   content MEDIUMTEXT NOT NULL,
   PRIMARY KEY(id)
);

The only required task is to store the article identifiers in session variables, which is implemented next:

<?php

    // Start session
    session_start();

    // Connect to server and select database
    mysql_connect("localhost","webuser","secret");
    mysql_select_db("chapter18");

    // Retrieve requested article id
    $articleid = mysqli_real_escape_string($_GET['id']);

    // User wants to view an article, retrieve it from database
    $query = "SELECT title, content FROM articles WHERE id='$id'";
    $result = mysql_query($query);

    // Retrieve query results
    list($title, $content) = mysql_fetch_row($result);

    // Add article title and link to list
    $articlelink = "<a href='article.php?id=$id'>$title</a>";

    if (! in_array($articlelink, $_SESSION['articles']))
        $_SESSION['articles'][] = $articlelink;

    // Output list of requested articles
    echo "<p>$title</p><p>$content</p>";
    echo "<p>Recently Viewed Articles</p>";
    echo "<ul>";

    foreach($_SESSION['articles'] as $doc) echo "<li>$doc</li>";
    echo "</ul>";
?>

The sample output is shown in Figure 18-1.

image

Figure 18-1. Tracking a user's viewed documents

Creating Custom Session Handlers

User-defined session handlers offer the greatest degree of flexibility of the four storage methods. Implementing custom session handlers is surprisingly easy, done by following just a few steps. To begin, you'll need to tailor six tasks (defined below) for use with your custom storage location. Additionally, parameter definitions for each function must be followed, again regardless of whether your particular implementation uses the parameter. This section outlines the purpose and structure of these six functions. In addition, it introduces session_set_save_handler(), the function used to magically transform PHP's session-handler behavior into that defined by your custom handler functions. Finally, this section concludes with a demonstration of this great feature, offering a MySQL-based implementation. You can immediately incorporate this library into your own applications, using a MySQL table as the primary storage location for your session information.

session_open($session_save_path, $session_name):
This function initializes any elements that may be used throughout the session process. The two input parameters $session_save_path and $session_name refer to the namesake configuration directives found in the php.ini file. PHP's get_cfg_var() function is used to retrieve these configuration values in later examples.
session_close():
This function operates much like a typical handler function does, closing any open resources initialized by session_open(). As you can see, there are no input parameters for this function. Keep in mind that this does not destroy the session. That is the job of session_destroy(), introduced at the end of this list.
session_read($sessionID):
This function reads the session data from the storage media. The input parameter $sessionID refers to the SID that will be used to identify the data stored for this particular client.
session_write($sessionID, $value):
This function writes the session data to the storage media. The input parameter $sessionID is the variable name, and the input parameter $value is the session data.
session_destroy($sessionID):
This function is likely the last function you'll call in your script. It destroys the session and all relevant session variables. The input parameter $sessionID refers to the SID in the currently open session.
session_garbage_collect($lifetime):
This function effectively deletes all sessions that have expired. The input parameter $lifetime refers to the session configuration directive session.gc_maxlifetime, found in the php.ini file.

Tying Custom Session Functions into PHP's Logic

After you define the six custom handler functions, you must tie them into PHP's session-handling logic. This is accomplished by passing their names into the function session_set_save_handler(). Keep in mind that these names could be anything you choose, but they must accept the proper number and type of parameters, as specified in the previous section, and must be passed into the session_set_save_ handler() function in this order: open, close, read, write, destroy, and garbage collect. An example depicting how this function is called follows:

session_set_save_handler("session_open", "session_close", "session_read",
                         "session_write", "session_destroy",
                         "session_garbage_collect");

The next section shows you how to create handlers that manage session information within a MySQL database.

Using Custom MySQL-Based Session Handlers

You must complete two tasks before you can deploy the MySQL-based handlers:

  1. Create a database and table that will be used to store the session data.
  2. Create the six custom handler functions.

The following MySQL table, sessioninfo, will be used to store the session data. For the purposes of this example, assume that this table is found in the database sessions, although you could place this table where you wish.

CREATE TABLE sessioninfo (
   SID CHAR(32) NOT NULL,
   expiration INT NOT NULL,
   value TEXT NOT NULL,
   PRIMARY KEY(SID)
);

Listing 18-2 provides the custom MySQL session functions. Note that it defines each of the requisite handlers, making sure that the appropriate number of parameters is passed into each, regardless of whether those parameters are actually used in the function.

Listing 18-2. The MySQL Session Storage Handler

<?php

    /*
     * mysql_session_open()
     * Opens a persistent server connection and selects the database.
     */

    function mysql_session_open($session_path, $session_name) {

        mysql_pconnect("localhost", "sessionmanager", "secret")
            or die("Can't connect to MySQL server! ");

        mysql_select_db("sessions")
            or die("Can't select MySQL sessions database");

    } // end mysql_session_open()

    /*
     * mysql_session_close()
    * Doesn't actually do anything since the server connection is
    * persistent. Keep in mind that although this function
    * doesn't do anything in my particular implementation, it
    * must nonetheless be defined.
    */

    function mysql_session_close() {

        return 1;

    } // end mysql_session_close()

    /*
     * mysql_session_select()
     * Reads the session data from the database
     */
    function mysql_session_select($SID) {

        $query = "SELECT value FROM sessioninfo
                  WHERE SID = '$SID' AND
                  expiration > ". time();

        $result = mysql_query($query);

        if (mysql_num_rows($result)) {

            $row=mysql_fetch_assoc($result);
            $value = $row['value'];
            return $value;

        } else {

            return "";

        }

    } // end mysql_session_select()

    /*
     * mysql_session_write()
     * This function writes the session data to the database.
     * If that SID already exists, then the existing data will be updated.
     */

    function mysql_session_write($SID, $value) {

        // Retrieve the maximum session lifetime
        $lifetime = get_cfg_var("session.gc_maxlifetime");

        // Set the session expiration date
        $expiration = time() + $lifetime;

        // Insert the session data into the database
        $query = "INSERT INTO sessioninfo
                  VALUES('$SID', '$expiration', '$value')";

        $result = mysql_query($query);

        // If the query fails, the session already exists.
        // Therefore, update the session instead.

        if (! $result) {

            $query = "UPDATE sessioninfo SET
                      expiration = '$expiration',
                      value = '$value' WHERE
                      SID = '$SID' AND expiration >". time();

            $result = mysql_query($query);

        }

    } // end mysql_session_write()

    /*
     * mysql_session_destroy()
     * Deletes all session information having input SID (only one row)
     */

    function mysql_session_destroy($SID) {

        // Delete all session information having a particular SID
        $query = "DELETE FROM sessioninfo
                  WHERE SID = '$SID'";

        $result = mysql_query($query);

    } // end mysql_session_destroy()
    /*
     * mysql_session_garbage_collect()
     * Deletes all sessions that have expired.
     */

    function mysql_session_garbage_collect($lifetime) {

        // Delete all sessions older than a specific age
        $query = "DELETE FROM sessioninfo
                  WHERE sess_expiration < ".time() - $lifetime;

        $result = mysql_query($query);

        return mysql_affected_rows($result);

   } // end mysql_session_garbage_collect()

?>

Once these functions are defined, they can be tied to PHP's handler logic with a call to session_set_save_handler(). The following should be appended to the end of the library defined in Listing 18-2:

session_set_save_handler("mysql_session_open", "mysql_session_close",
                         "mysql_session_select",
                         "mysql_session_write",
                         "mysql_session_destroy",
                         "mysql_session_garbage_collect");

To test the custom handler implementation, start a session and register a session variable using the following script:

<?php
    INCLUDE "mysqlsessionhandlers.php";
    session_start();
    $_SESSION['name'] = "Jason";
?>

After executing this script, take a look at the sessioninfo table's contents using the mysql client:

mysql> select * from sessioninfo;

+---------------------------------------+-------------------+-------------------
+
| SID                                  | expiration        | value             |
+---------------------------------------+-------------------+-------------------
+
| f3c57873f2f0654fe7d09e15a0554f08     | 1068488659        | name|s:5:"Jason"; |
+---------------------------------------+-------------------+-------------------
+
1 row in set (0.00 sec)

As expected, a row has been inserted, mapping the SID to the session variable "Jason". This information is set to expire 1,440 seconds after it was created; this value is calculated by determining the current number of seconds after the Unix epoch, and adding 1,440 to it. Note that although 1,440 is the default expiration setting as defined in the php.ini file, you are free to change this value to whatever you deem appropriate.

Note that this is not the only way to implement these procedures as they apply to MySQL. You are free to modify this library as you see fit.

Summary

This chapter covered the gamut of PHP's session-handling capabilities. You learned about many of the configuration directives used to define this behavior, in addition to the most commonly used functions that are used to incorporate this functionality into your applications. The chapter concluded with a real-world example of PHP's user-defined session handlers, showing you how to turn a MySQL table into the session-storage media.

The next chapter addresses another advanced but highly useful topic: templating. Separating logic from presentation is a topic of constant discussion, as it should be; intermingling the two practically guarantees you a lifetime of application maintenance anguish. Yet actually achieving such separation seems to be a rare feat when it comes to Web applications. It doesn't have to be this way!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.103.206