Zend Framework - Session plugin

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
fastfingertips
Forum Contributor
Posts: 242
Joined: Sun Dec 28, 2003 1:40 am
Contact:

Zend Framework - Session plugin

Post by fastfingertips »

Hello

I'm playing with ZF for a while and i have create a plugin that allows you to implement a custom session management system (in this classes you will find sessions stored in database). The benefit of this solution are:

- use PHP native functions like: session_start() and session_destroy();
- use the global $_SESSION

The plugin is using an SQL table (the example is for a MySQL table)

Code: Select all

CREATE TABLE `app_session` (
  `session_id` varchar(32) collate latin1_general_ci NOT NULL default '',
  `session_time` int(11) NOT NULL default '0',
  `session_start` int(11) NOT NULL default '0',
  `session_data` text collate latin1_general_ci NOT NULL,
  `session_key` varchar(50) collate latin1_general_ci default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;
I will wait your comments regarding this and i will gladly implement any good idea :)

The Session class where factory is located.

Code: Select all

/**
 * Application_Plugins_Session_Exception
 */
require_once 'Application/Plugins/Session/Exception.php';

/**
 * Factory for Zend_Db_Handler classes.
 * 
 * @param string $adapterName   Name of the adapter to return:
 *                              'database' -> Application_Plugins_Session_Handler_Database
 *
 * @param array  $config        An array of adapter configuration keys.
 *
 * @return Application_Plugins_Session_Handler_Abstract
 * 
 */
class Application_Plugins_Session {
	static public function factory($adapterName, $configuration = null)
    {    	
        if (!is_string($adapterName) or !strlen($adapterName)) {        	
            throw new Application_Plugins_Session_Exception('Adapter name must be specified in a string!');
        }        
		$adapterName = 'Application_Plugins_Session_Handler_'.str_replace(' ','_',ucwords(str_replace('_', ' ', $adapterName)));        
				
        # Load adapter
        Zend::loadClass($adapterName);

        return new $adapterName();
    }
}
The abstract class from which all other implementations will be extended (sorry for my english):

Code: Select all

/**
 * Abstract class for Zend_Session to help enforce private constructs.
 *
 * @category   Zend
 * @package    Zend_Session
 */
abstract class Application_Plugins_Session_Handler_Abstract 
{
	/**
	 * This is the first function called by PHP when a session is started
	 *
	 * @param string $strSessionPath
	 * @param string $strSessionName
	 */
	public function open($strSessionPath, $strSessionName) 
	{
		
	}
	
	/**
	 * The close function is the last function called by PHP related to the session. 
	 * It does not accept any parameters, and returns either TRUE or FALSE. 
	 */
	public function close()
	{
		
	}
	
	/**
	 * Retrieve any data that is stored for the session, and return it. It is extremely 
	 * important always to return a string from this function, even if it’s empty.
	 */
	public function read() 
	{
		
	}
	
	/**
	 * Write session data
	 *
	 * @param string $strSessionId
	 * @param string $SessionData
	 */
	public function write($strSessionId,$SessionData)
	{
		
	}
	
	/**
	 * Destroy session, delete all data related to the session id
	 *
	 * @param string $strSessionId
	 */
	public function destroy($strSessionId)
	{
		
	}
	
	public function gc($strSessionLifeTime)
	{
		
	}
}
The Database implementation

Code: Select all

/**
 * Application_Plugins_Session_Handler_Absstract
 */
require_once 'Application/Plugins/Session/Handler/Abstract.php';

/**
 * Application_Plugins_Session_Handler_Exception
 */
require_once 'Application/Plugins/Session/Handler/Exception.php';

class Application_Plugins_Session_Handler_Database extends Application_Plugins_Session_Handler_Abstract
{
	public function open($strSessionPath, $strSessionName)
	{
		return true;		
	}	
	
	public function close() 
	{
		return true;
	}
	
	protected function _generateId()
	{	
		do {
			# Generate session ID
			$strSessionId = md5(uniqid(mt_srand((double) microtime() * 1000000)));	
			
		} while ($this->_validateId($strSessionId) === false);
	}
	
	protected function _formatSessionData($SessionData) 
	{
		$arrSession = array();
		$arrSession = preg_split("/([a-zA-Z0-9\_]+\|)/",$SessionData,-1,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
		return array_chunk($arrSession,2);				
	}
	
	protected function _validateId($strSessionId)
	{
		# Get database connection
		$db = Zend::registry('db');
		
		# Check is the session ID is unique
		$select = $db->select();
		$select->from('app_session','count(session_id) as session_found');
		$select->where('session_id = ?',$strSessionId);
		$intValue = $db->fetchOne($select->__toString()) ;		
		if($intValue) {
			return false;
		}
		else {
			return true;
		}
	}
	
	
	public function write($strSessionId,$SessionData) 
	{
		# Get database connection
		$db = Zend::registry('db');
		$arrSessionData = $this->_formatSessionData($SessionData);
		
		# Check if the session id exists
		if($this->_validateId($strSessionId))
		{
			foreach ($arrSessionData as $arrSessionDataKey => $arrSessionDataValue) {			
				# ID of the session doesn't exist, perform insert		
				$db->insert("app_session",array("session_id"=>$strSessionId,"session_time"=>time(),"session_start"=>time(),"session_data"=>$arrSessionDataValue[1],"session_key"=>$arrSessionDataValue[0]));
			}
		}
		else 
		{
			# ID of the session exists perform update			
			foreach ($arrSessionData as $arrSessionDataKey => $arrSessionDataValue) {				
				# ID of the session doesn't exist, perform insert		
				$intAffected = $db->update("app_session",array("session_time"=>time(),"session_key"=>$arrSessionDataValue[0],"session_data"=>$arrSessionDataValue[1]),"session_id = '".$strSessionId."' and session_key = '".$arrSessionDataValue[0]."'");
				if(!$intAffected) {
					$db->insert("app_session",array("session_id"=>$strSessionId,"session_time"=>time(),"session_start"=>time(),"session_data"=>$arrSessionDataValue[1],"session_key"=>$arrSessionDataValue[0]));					
				}
			}						
		}
		return true;
	}
	
	public function destroy($strSessionId)
	{
		# Get database connection
		$db = Zend::registry('db');
		
		# Check if the session id exists
		if(!$this->_validateId($strSessionId))
		{
			# Delete session and data associated with it from database
			return $db->delete("app_session","session_id= '".$strSessionId."'") > 0 ? true : false;
		}
		return true;
	}
	
	public function gc($strSessionLifeTime = 30)
	{
		# Get database connection
		$db = Zend::registry('db');
		
		# Load application configuration parameters to get session lifetime
		$config_app = Zend::registry('config_app');
		
		$strSessionLifeTime = strtotime("-".$config_app->lifetime." minutes");
		$db->delete("app_session","session_time < ".$strSessionLifeTime);
		return true;
	}
	
	public function read($strSessionId)
	{
		# Get database connection
		$db = Zend::registry('db');
		
		# Get all data associated with the session
		$select = $db->select();
		$select->from('app_session','CONCAT(session_key,session_data) as session_data');
		$select->where('session_id = ?',$strSessionId);
		$SessionData = $db->fetchAll($select->__toString());	
		
		$SessionString = "";
		if(is_array($SessionData)) {
			foreach ($SessionData as $key => $value) {			
				$SessionString .= $value['session_data'];
			}
		}
		
		# If no results the empty string will be returned
		return $SessionString;
	}
}
fastfingertips
Forum Contributor
Posts: 242
Joined: Sun Dec 28, 2003 1:40 am
Contact:

Improvement for read method

Post by fastfingertips »

Using the mysql functions to group and select session data i achieved better execution time (up to 50% :) )

Code: Select all

public function read($strSessionId)
        {
                # Get database connection
                $db = Zend::registry('db');
               
                # Get all data associated with the session
                $select = $db->select();
                $select->from('app_session',"(GROUP_CONCAT(session_key,session_data SEPARATOR '')) as session_data");
		$select->where('session_id = ?',$strSessionId);
                $SessionData = $db->fetchAll($select->__toString());            
                
               
                # If no results the empty string will be returned
                return $SessionData ;
        }
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Looks good, I'll give it a test within a couple days and see if I can come up with any issues.
Post Reply