Tuesday, July 14, 2009

Beginning PHP and Oracle From Novice to Professional by W. Jason Gilmore and Bob Bryla Chapter 18

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 the 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 the following:

• 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 such HTTP’s simplicity is a signifi- cant contributor to its ubiquity, its stateless nature has long been a problem for developers who want 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, as well as 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 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:

319

• Cookies: One ingenious means for managing user information actually builds upon the orig- inal 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 pref- erences 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 propa- gation 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.

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 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 can 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 could display that list as the user continues to navigate. In the coming sections, you’ll learn how to store and retrieve this session information.
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 the following sections.

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 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 an Oracle 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, although a script named mod_files.sh located in the ext/session directory that will automate the process is available for Linux users. 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 forego 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

You can modify the default value at run time as needed using the session_name() function, introduced later in this chapter.

Choosing Cookies or URL Rewriting

Using cookies, it’s possible to maintain a user’s session over multiple visits to the site. Alternatively, if the user data is to be used over the course of only a single site visit, then URL rewriting will suffice. However, for security reasons you should always opt for the former approach. 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 you must consider several other directives, each of which is introduced in the following entries.
Using the session.use_only_cookies directive, you can also ensure that cookies will be used to
maintain the SID, ignoring any attempts to initiate an attack by passing a SID via the URL. Its proto- type follows:

session.use_only_cookies = 0 | 1

Setting this directive to 1 causes PHP to use only cookies, and setting it to 0 (the default) opens up the possibility for both cookies and URL rewriting to be considered.

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 explicitly by manually appending the variable $SID to the end of each URL or handled 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 one 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

However, the default setting of an empty string will cause the server’s hostname to be used, meaning you probably won’t need to set this at all.

Validating Sessions Using a Referrer

Using URL rewriting as the means for propagating session IDs opens up the possibility that a partic- ular 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 invali- dated. 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 instruc- tions 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 recupera- tion 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, the designation and retrieval of the SID, and the 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 will continue a current session if a 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 the execution of this function altogether by enabling the configuration direc- tive 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 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, using 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()

Although 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, sometimes you may want 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 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 the following:

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 2006 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, some- times you might want to execute these tasks manually. Two functions are available for doing so: session_encode() and session_decode().

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 code, 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 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 the following:

username|s:5:"jason";loggedon|s:20:"Feb 16 2007 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 func- tion will decode the variables, returning them to their original format and will 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 2007 22:32:29";

session_decode($sessionVars);

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

?>

This returns the following:

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

This hypothetical example is intended solely to demonstrate PHP’s session encoding and decoding function. If you want to store session data in a database, there’s a much more efficient method involving defining custom session handlers and tying those handlers directly into PHP’s API. You’ll learn how to accomplish this 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 you can use session variables 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 Oracle database and are confused by the syntax found in the following examples,
consider reviewing the material in Chapter 31 and Chapter 32.

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 an Oracle table. Although you can implement this feature in many ways, 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 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 Oracle table, users, is presented here:

CREATE SEQUENCE users_seq start with 1
increment by 1 nomaxvalue;

CREATE TABLE users (
user_id NUMBER PRIMARY KEY, commonname VARCHAR2(35) NOT NULL, username VARCHAR2(8) NOT NULL, pswd CHAR(32) NOT NULL
);

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

<p>

</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>

Finally, the logic used to manage the autologin 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 = htmlentities($_POST['username']);
$pswd = htmlentities($_POST['pswd']);

// Connect to the Oracle database
$conn = oci_connect('WEBUSER', 'oracle123', '//127.0.0.1/XE')
or die("Can't connect to database server!");

// Create query
$query = "SELECT username, pswd FROM users
WHERE username=:username AND pswd=:pswd";

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind PHP variables and execute query oci_bind_by_name($stmt, ':username', $username, 8); oci_bind_by_name($stmt, ':pswd', $pswd, 32);

oci_execute($stmt);

// Has a row been returned?
list($username, $pswd) = oci_fetch_array($stmt, OCI_NUM);

// Has the user been located?
if ($username != "")
{
$_SESSION['username'] = $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 renewing library books 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 want, although for the purposes of this example, we’ll just use the article’s title and URL and assume that this information is derived from data stored in a database table named articles, shown here:

CREATE SEQUENCE articles_seq start with 1
increment by 1
nomaxvalue;

CREATE TABLE articles (
article_id NUMBER PRIMARY KEY, title VARCHAR2(50) NOT NULL, content VARCHAR2(8) NOT NULL
);

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

<?php

// Start session session_start();

// Connect to the Oracle database
$conn = oci_connect('WEBUSER', 'oracle123', '//127.0.0.1/XE')
or die("Can't connect to database server!");

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

// User wants to view an article, retrieve it from database
$query = "SELECT title, content FROM articles WHERE id=:articleid ";

$stmt = oci_parse($conn, $query); oci_bind_by_name($stmt, ':articleid', $articleid); oci_execute($stmt);
// Has a row been returned?
list($title, $content) = oci_fetch_array($stmt, OCI_NUM);

// 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 printf("<p>%s</p><p>%s</p>", $title, $content); echo "<p>Recently Viewed Articles</p><ul>";

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

Figure 18-1 shows the sample output.

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; you can do it by following just a few steps. To begin, you’ll need to tailor six tasks (defined next) for use with your custom storage loca- tion. 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 an Oracle-based implementation. You can immediately incorporate this library into your own applica- tions, using an Oracle 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.

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. You accomplish this by passing their names into the function session_set_save_handler(). Keep in mind that these names can 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 an
Oracle database.

Custom Oracle-Based Session Handlers

You must complete two tasks before you can deploy the Oracle-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 Oracle 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 want.

CREATE TABLE sessions (
sessionID VARCHAR2(32) NOT NULL PRIMARY KEY, expiration NUMBER NOT NULL,
value VARCHAR2(1000)
);

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

Listing 18-1. The Oracle Session Storage Handler

<?php

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

function oracle_session_open($session_path, $session_name) { GLOBAL $conn;
// Connect to the Oracle database
$conn = oci_pconnect('WEBUSER', 'oracle123', '//127.0.0.1/XE')
or die("Can't connect to database server!");

} // end oracle_session_open()

/*
* oracle_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 oracle_session_close() {

return 1;

} // end oracle_session_close()

/*
* oracle_session_select()
* Reads the session data from the database
*/

function oracle_session_select($SID) { GLOBAL $conn;
$query = "SELECT value FROM sessions WHERE sessionID = :SID AND expiration > ". time();

$stmt = oci_parse($conn, $query);

// Bind value
oci_bind_by_name($stmt, ':SID', $SID, 32);

// Execute statement oci_execute($stmt);

// Has a row been returned?
$row = oci_fetch_array($stmt, OCI_NUM);

if (isset($row[0])) {

return $row[0];

} else {

return "";

}

} // end oracle_session_select()

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

function oracle_session_write($SID, $value) { GLOBAL $conn;
// Retrieve the maximum session lifetime
$lifetime = get_cfg_var("session.gc_maxlifetime");

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

$query = "UPDATE sessions SET expiration = :expiration, value = :value WHERE
sessionID = :SID AND expiration >". time();

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind the values
oci_bind_by_name($stmt, ':SID', $SID, 32); oci_bind_by_name($stmt, ':expiration', $expiration); oci_bind_by_name($stmt, ':value', $value);

oci_execute($stmt);

// The session didn't exist since no rows were updated if (oci_num_rows($stmt) == 0) {
// Insert the session data into the database
$query = "INSERT INTO sessions (sessionID, expiration, value) VALUES(:SID, :expiration, :value)";

// Prepare statement
$stmt = oci_parse($conn, $query);

// Bind the values
oci_bind_by_name($stmt, ':SID', $SID, 32); oci_bind_by_name($stmt, ':expiration', $expiration); oci_bind_by_name($stmt, ':value', $value);

oci_execute($stmt);

}

} // end oracle_session_write()

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

function oracle_session_destroy($SID) { GLOBAL $conn;
// Delete all session information having a particular SID
$query = "DELETE FROM sessions WHERE sessionID = :SID";

$stmt = oci_parse($conn, $query); oci_bind_by_name($stmt, ':SID', $SID); oci_execute($stmt);
} // end oracle_session_destroy()

/*
* oracle_session_garbage_collect()
* Deletes all sessions that have expired.
*/

function oracle_session_garbage_collect($lifetime) { GLOBAL $conn;
$time = time() - $lifetime;

// Delete all sessions older than a specific age
$query = "DELETE FROM sessions
WHERE expiration < :lifetime";

$stmt = oci_parse($conn, $query); oci_bind_by_name($stmt, ':lifetime', $time); oci_execute($stmt);
return oci_num_rows($stmt);

} // end oracle_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-1:

session_set_save_handler("oracle_session_open", "oracle_session_close", "oracle_session_select", "oracle_session_write", "oracle_session_destroy", "oracle_session_garbage_collect");

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

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

After executing this script, take a look at the sessioninfo table’s contents, and you’ll see some-
thing like this:

+---------------------------------------+-------------------+-------------------+
| sessionID | 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 Oracle. 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 an Oracle 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 prac- tically 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!

0 comments: