HomePHP Page 2 - Using Session Handling Objects to Maintain the State of Applications with PHP Sessions
A procedural implementation of the “session_set_save_handler()” function - PHP
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.
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.