Using Session Handling Objects to Maintain the State of Applications with PHP Sessions

Here you have it. The tutorial that you were waiting for! Welcome to the concluding part of the series “Maintaining the state of applications with PHP sessions.” In several tutorials, this series goes through the key points of managing sessions in PHP, and explores some of their most advanced features, such as developing user-defined session storage modules and using session handling objects.

Introduction

If you’ve already read my two previous tutorials in this series, then probably you’ve expanded your knowledge of the basics of PHP session management, including the proper understanding of some of the core operations required for creating, using and destroying sessions.

In addition, handling and propagating session IDs should be pretty familiar concepts to you, since I covered this topic in detail in the first tutorial. However, in order to get the most out of the PHP session management module, during the second article you learned how to build a custom session mechanism by utilizing the helpful “session_save_path()” and “session_set_save_handler()” functions. Specifically, the last function allows you to build an entirely customized session module. This module can potentially use MySQL (or other RDBMS) for saving session-related data, in this way increasing the security of the overall session handling mechanism.

Well, if sessions were a foreign concept to you before reading these tutorials, now you have enough source code to experiment with yourself and start including advanced session scripts within your PHP applications. Nevertheless, the subject is certainly huge and deserves an in-depth look, particularly in those topics related to advanced session handling.

Keeping this idea in mind, in this last part of the series, I’ll explain how to use the PHP built-in “session_set_save_handler()” function inside of a PHP class, in order to create highly-centralized session handling objects. Are you ready to learn how this will be done? Right, let’s go!

{mospagebreak title=A procedural implementation of the “session_set_save_handler()” function}

Before I proceed further in the creation of a session handling class, let’s remind ourselves how the “session_set_save_handler()” can be fed with the proper callback functions, in order to develop a session storage module that uses a MySQL database table for saving and reading session data. As you’ll recall, the signatures for these functions were the following:

// define ‘openSession()’ function
function openSession($sessionPath,$sessionName){
    return true;
}
// define ‘closeSession()’ function
function closeSession(){
    return true;
}
// define ‘readSession()’ method
function readSession($sessionId){
    global $db;
    // escape session ID
    if(!get_magic_quotes_gpc()){
        $sessionId=mysql_real_escape_string($sessionId);
    }
    $result=$db->query(“SELECT sessiondata FROM sessions WHERE
sessionid=’$sessionId’ AND expiry > NOW()”);
    if($result->countRows()>0){
        $row=$result->fetchRow();
        return $row['sessiondata'];
    }
    // return empty string
    return “”;
}
// define ‘writeSession()’ function
function writeSession($sessionId,$sessionData){
    global $db;
    $expiry=time()+get_cfg_var(‘session.gc_maxlifetime’)-1;
    // escape session ID & session data
    if(!get_magic_quotes_gpc()){
        $sessionId=mysql_real_escape_string($sessionId);
    }
    $result=$db->query(“SELECT sessionid FROM sessions WHERE
sessionid=’$sessionId'”);
    // check if a new session must be stored or an existing one
must be updated 
    ($result->countRows()>0)?$db->query(“UPDATE sessions SET
sessionid=’$sessionId’,expiry=’$expiry’,sessiondata=
‘$sessionData’ WHERE sessionid=’$sessionId'”):$db->query(“INSERT
INTO sessions (sessionid,expiry,sessiondata) VALUES
(‘$sessionId’,’$expiry’,’$sessionData’)”);
    return true;
}
// define ‘destroySession()’ function
function destroySession($sessionId){
    global $db;
    // escape session ID
    if(!get_magic_quotes_gpc()){
        $sessionId=mysql_real_escape_string($sessionId);
    }
    $db->query(“DELETE FROM sessions WHERE sessionid=’$sessionId'”);
    return true;
}
// define ‘gcSession()’ function
function gcSession($maxlifetime){
    global $db;
    $db->query(“DELETE FROM sessions WHERE expiry < NOW()”);
    return true;
}

After listing the corresponding callback functions, which must be registered by the “session_set_save_handler()” function, here is the signature of the MySQL processing classes used within some of these functions:

class MySQL {
    var $conId; // connection identifier
    var $host; // MySQL host
    var $user; // MySQL username
    var $password; // MySQL password
    var $database; // MySQL database
    // constructor
    function MySQL($options=array()){
        // validate incoming parameters
        if(count($options)>0){
            foreach($options as $parameter=>$value){
                if(empty($value)){
                    trigger_error(‘Invalid parameter
‘.$parameter,E_USER_ERROR);
                }
                $this->{$parameter}=$value;
            }
            // connect to MySQL
            $this->connectDB();
        }
        else {
            trigger_error(‘No connection parameters were
provided’,E_USER_ERROR);
        }
    }
    // connect to MYSQL server and select database
    function connectDB(){
        if(!$this->conId=mysql_connect($this->host,$this-
>user,$this->password)){
            trigger_error(‘Error connecting to the
server’,E_USER_ERROR);
        }
        if(!mysql_select_db($this->database,$this->conId)){
            trigger_error(‘Error selecting
database’,E_USER_ERROR);
        }
    }
    // perform query
    function query($query){
        if(!$this->result=mysql_query($query,$this->conId)){
            trigger_error(‘Error performing query
‘.$query,E_USER_ERROR);
        }
        // return new Result object
        return new Result($this,$this->result); 
    }
}

class Result {
    var $mysql; // instance of MySQL object
    var $result; // result set
    function Result(&$mysql,$result){
        $this->mysql=&$mysql;
        $this->result=$result;
    }
    // fetch row
    function fetchRow(){
        return mysql_fetch_array($this->result,MYSQL_ASSOC);
    }
    // count rows
    function countRows(){
        if(!$rows=mysql_num_rows($this->result)){
            return false;
        }
        return $rows;
    }
    // count affected rows
    function countAffectedRows(){
        if(!$rows=mysql_affected_rows($this->mysql->conId)){
            trigger_error(‘Error counting affected
rows’,E_USER_ERROR);
        }
        return $rows;
    }
    // get ID from last inserted row
    function getInsertID(){
        if(!$id=mysql_insert_id($this->mysql->conId)){
            trigger_error(‘Error getting ID’,E_USER_ERROR);
        }
        return $id;
    }
    // seek row
    function seekRow($row=0){
        if(!mysql_data_seek($this->result,$row)){
            trigger_error(‘Error seeking data’,E_USER_ERROR);
        }
    }
    function getQueryResource(){
        return $this->result;
    }
}

Right, at this point the MySQL-driven session storage module is close to completion. All I have to do now is call the “session_set_save_handler()” function, passing as arguments the callback functions that you saw before. This operation is performed by the following code snippet:

// include classes
require_once ‘mysqlclass.php';
require_once ‘resultclass.php';
// connect to MySQL
$db=&new MySQL(array
(‘host’=>’localhost’,’user’=>’user’,’password’=>’password’,
‘database’=>’database’));
// use ‘session_set_save_handler function’
session_set_save_handler
(‘openSession’,’closeSession’,’readSession’,’writeSession’,
‘destroySession’,’gcSession’);
session_start();
// register some session variables
$_SESSION['firstname']=’John';
$_SESSION['lastname']=’Doe';

As you can see, here I wrote a procedural script that utilizes the “session_set_save_handler()” function in conjunction with the respective callback functions defined before, in order to manage PHP sessions through a simple “sessions” MySQL database table. By following this approach, instead of using flat files (the default behavior of the PHP session management module), it’s possible to handle sessions in a more secure way. As you may have guessed, this benefit can be quite remarkable for Web applications that need to rely on an improved session handling mechanism, rather than simple flat files.

So far, the procedural method that I developed earlier may potentially fit the needs of an advanced PHP application that uses sessions to maintain its state between different HTTP requests. However, I’d like to show you how to use the same callback functions, this time transformed to the methods of a useful session handling class. Want to see how this is achieved? Fine, let’s jump to the next section.

{mospagebreak title=Developing an object-oriented session management module}

Actually, creating an object-oriented session handling class is much simpler than you might have thought initially. The nitty-gritty of the whole development process rests on converting the prior callback functions to methods of the session handling class. In accordance with this idea, the basic structure of a session managing class can be defined like this:

// define ‘SessionHandler’ class

class SessionHandler {
    function SessionHandler(){
        // code to register class methods goes here
    }
    function openSession(){
        // no specific implementation is required here
        return true;
    }
    function closeSession(){
        // no specific implementation is required here
        return true;
    }
    function readSession(){
       // code to read session data goes here
    }
    function writeSession(){
        // code to write session data goes here
    }
    function gcSession(){
        // code to delete garbage session data goes here
    }      
}

As shown above, the skeleton of the corresponding session handling class is basically made up of the previous six callback functions, which of course have been defined as class methods. At first glance, you can see that encapsulating all the session handling methods behind the structure of a class comes in very useful for constructing a centralized mechanism to manage sessions within a specific PHP application.

I don’t mean that an object-based approach is better that a procedural approach; that would be a misconception. I just want you to realize the convenience of having all the methods integrated into one class, which can be very handy in case you’re developing a Web application purely comprised of PHP objects.

At this point, you saw how the structure of the session handling class looks. Now, it’s time to leap forward and provide a specific implementation for each of its methods. Keep reading to learn more.

{mospagebreak title=Coding the session handling class}

Since I already defined the structure of the pertinent session handling class, specifically writing each of its methods is just a matter of translating the callback functions defined for the procedural session handling approach into class methods. Even when I’m pretty sure that you know how the complete class will look, I’ll do most of the hard work for you. Please, take a look at the definition of this class:

class SessionHandler {
    function SessionHandler(){

        session_set_save_handler(array
(&$this,’openSession’),array(&$this,’closeSession’),array
(&$this,’readSession’),array(&$this,’writeSession’),array
(&$this,’destroySession’),array(&$this,’gcSession’));
    }
    function openSession($sessionPath,$sessionName){
        return true;
    }
    function closeSession(){
        return true;
    }
    function readSession($sessionId){
        global $db;
        // escape session ID
        if(!get_magic_quotes_gpc()){
            $sessionId=mysql_real_escape_string($sessionId);
        }
        $result=$db->query(“SELECT sessiondata FROM sessions
WHERE sessionid=’$sessionId’ AND expiry > NOW()”);
        if($result->countRows()>0){
            $row=$result->fetchRow();
            return $row['sessiondata'];
        }
        // return empty string
        return “”;
    }
    function writeSession($sessionId,$sessionData){
        global $db;
        $expiry=time()+get_cfg_var(‘session.gc_maxlifetime’)-1;
        // escape session ID & session data
        if(!get_magic_quotes_gpc()){
            $sessionId=mysql_real_escape_string($sessionId);
        }
        $result=$db->query(“SELECT sessionid FROM sessions WHERE
sessionid=’$sessionId'”);
        // check if a new session must be stored or an existing
one must be updated 
        ($result->countRows()>0)?$db->query(“UPDATE sessions SET
sessionid=’$sessionId’,expiry=’$expiry’,sessiondata=
‘$sessionData’WHERE sessionid=’$sessionId'”):$db->query(“INSERT
INTO sessions(sessionid,expiry,sessiondata) VALUES
(‘$sessionId’,’$expiry’,’$sessionData’)”);
        return true;
    }
    function destroySession($sessionId){
        global $db;
        // escape session ID
        if(!get_magic_quotes_gpc()){
            $sessionId=mysql_real_escape_string($sessionId);
        }
        $db->query(“DELETE FROM sessions WHERE
sessionid=’$sessionId'”);
        return true;
    }
    function gcSession($maxlifetime){
        global $db;
        $db->query(“DELETE FROM sessions WHERE expiry < NOW()”);
        return true;
    }      
}

As you can see, the above class now includes each of the previous callback functions directly as methods, which maintain the same original definition. Of course, the most important thing to note here is the inclusion of the “session_set_save_handler()” function inside the class constructor, and the corresponding registration of each method to be appropriately called.

In this case, the “session_set_save_handler()” function accepts an input array, containing both a self reference to the “SessionHandler” object (represented by the &$this pointer) and the name of the callback method to be used. The following expression illustrates this condition:

session_set_save_handler(array(&$this,’openSession’),array
(&$this,’closeSession’),array(&$this,’readSession’),array
(&$this,’writeSession’),array(&$this,’destroySession’),array
(&$this,’gcSession’));

Now that you know how the “SessionHandler” class looks, here’s a simple implementation of it, using the pair of additional MySQL processing classes shown before:

// include classes
require_once ‘mysqlclass.php';
require_once ‘resultclass.php';
// connect to MySQL
$db=&new MySQL(array(‘host’=>’localhost’,’user’=>’user’,
‘password’=>’password’,’database’=>’database’));
// instantiate new ‘SessionHandler’ object
$sessionHandler=&new SessionHandler;
session_start();
// register some session variables
$_SESSION['firstname']=’Alejandro';
$_SESSION['lastname']=’Gervasio';

That’s it. The above script demonstrates how to integrate a “SessionHandler” object with the other MySQL processing classes, in order to implement a MySQL-driven session management module. Notice that after instantiating a new session handler object, the session is started normally, by the “session_start()” function, as one would expect using flat files. Definitely, the above source code now looks very compact and readable, since all the hard work for managing sessions is done behind the scenes, by the methods of the useful “SessionHandler” class.

At this point, you hopefully learned how to create a session handling class, which encapsulates all the required methods for implementing a MySQL-based session management module. However, due to their versatility, the methods can be easily rewritten, in order to use another RDBMS. As with most things in Web programming, this will depend on your own development requirements.

To wrap up

Unfortunately, this series has now concluded. Over its different parts, I covered the most relevant aspects of session management in PHP, starting out with the basics of how to create, use and destroy sessions, and additionally explaining the correct implementation of advanced functions, such as “session_set_save_handler()”, which can be used to develop custom session modules, similar to the one I built before.

I hope you find the material I provided you in this series to be useful, so you can use it for extending the boundaries of your grounding in PHP sessions. Meet you in the next PHP series!

[gp-comments width="770" linklove="off" ]
antalya escort bayan antalya escort bayan