[u][b]session.class.php[/b][/u]
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
 * a session class without use of any session functions of the php language
 * 
 *
 * @license      GNU/GPL http://www.gnu.org/licenses/gpl.html
 * @author       stijn1989 <stijnleenknegt@gmail.com>
 * @version      Versie 1.0
 * @package      Session
 */


/**
 * The session class
 * 
 * @package Session
 * @author stijn1989
 */
class Session
{
	
	
	/**
	 * constantes of the session class
	 */
	const ERROR_SESSION_WRITE_FILE 		= 'Could not write the session data to the session file!';
	const ERROR_SESSION_OPEN_FILE  		= 'Could not open the session file!';
	const ERROR_SESSION_CLOSE_FILE 		= 'Could not close the session file!';
	const ERROR_SESSION_OPEN_DIR 		= 'Could not open the dir!';
	const ERROR_SESSION_READ_DIR		= 'Could not read the dir!';
	const ERROR_SESSION_CLOSE_DIR		= 'Could not close the dir!';
	const ERROR_SESSION_READ_FILE		= 'Could not read the contents of the session file!';
	const ERROR_SESSION_DELETE_FILE 	= 'Could not delete the session file.';
	const ERROR_SESSION_TOUCH_FILE  	= 'Could not toch the file.';
	
	
	
	/**
	 * the session
	 * 
	 * @access private
	 * @name $_session
	 */
	static private $_session = array( );
	
	
	/**
	 * length of the session ID
	 * 
	 * @access private
	 * @name $_sidl
	 */
	static private $_sidl = 17;
	
	
	/**
	 * path to store the session files
	 * 
	 * @access public
	 * @name $session_path
	 */
	static public $session_path = './session/';
	
	
	/**
	 * strong error handling?
	 * 
	 * @access public
	 * @name $session_error_force
	 */
	static public $session_error_force = true;
	
	
	
	/**
	 * construct - start the session
	 * $timeoutforce = true -- it will check all session files on the server
	 * servers with lot's of traffice/visitors should put this to false
	 * if you don't won't long loadtimes and site-errors.
	 * 
	 * @access public
	 * @param integer $timeout
	 * @param boolean $timeout_force
	 * @return boolean
	 */
	public function __construct( $timeout = 0 , $timeoutforce = false )
	{
		
		//check if the session isn't already running - update the history of the session
		if( $this->session_check_alive() === true ) {
			//check the timeout
			if( $timeout != 0 && $timeoutforce === false ) {
				$this->session_check_timeout();
			} elseif( $timeout != 0 && $timeoutforce === true ) {
				$this->session_check_timeout_force();
			}
			
			//update when it isn't timeout
			if( $this->session_check_alive() === true ) {
				$this->session_update_data_history( time() , $_SERVER['REQUEST_URI'] );
			} else {
				return false;
			}
			
			return true;
		}
		
		//session setup
		self::$_session = array( 'id' => $this->create_session_id() , 'ip' => $_SERVER['REMOTE_ADDR'] , 'start' => time() );
		
		
		$session = array( 'session_ip' => self::$_session['ip'] ,
						  'session_id' => self::$_session['id'] , 
						  'session_start' => self::$_session['start'] ,
						  'session_timeout' => $timeout ,
						  'session_host' => $_SERVER['HTTP_HOST'] ,
						  'session_data' => array( 'session_history' => array( time() => $_SERVER['REQUEST_URI'] ) ,
						   							'session_vars' => array( ) 
						   							) 
						   	);
		
		//create the new session
		try {
			
			if( touch( $this->session_file_name() , time() , time() ) === false ) {
				throw new Exception( self::ERROR_SESSION_TOUCH_FILE );
			}
			
			if( ($fp = fopen( $this->session_file_name() , 'w' )) === false ) {
				throw new Exception( self::ERROR_SESSION_OPEN_FILE );
			}
			
			if( fwrite( $fp , $this->session_data( $session ) ) === false ) {
				throw new Exception( self::ERROR_SESSION_WRITE_FILE );
			}
			
			if( fclose( $fp ) === false ) {
				throw new Exception( self::ERROR_SESSION_CLOSE_FILE );
			} else {
				return true;
			}
			
		}
		catch ( Exception $e )
		{
			die( $this->session_error() );
		}
		
	}
	
	
	/**
	 * create the name for the session file
	 * 
	 * @access private
	 * @return string
	 */
	private function session_file_name( )
	{
		return self::$session_path . str_replace('.' , '_' , $_SERVER['REMOTE_ADDR']) . '.session';
	}
	
	
	/**
	 * create a session id
	 * 
	 * @access private
	 * @return integer
	 */
	private function create_session_id( )
	{
		
		for( $i = 0 ; $i < self::$_sidl ; $i++ ) {
			$id .= rand(0,9);
		}
		
		return (strlen($id) != self::$_sidl) ? 0 : $id;
		
	}
	
	
	/**
	 * make the session data
	 * $mode = 0 (serialize) or 1 (unserialize)
	 * 
	 * @param $data
	 * @param $mode 
	 * @access private
	 * @return string
	 */
	private function session_data( $data , $mode = 0 )
	{
		
		switch( $mode ) {
			default: 
					return serialize( $data);
			case 0: 
					return serialize( $data );
			case 1: 
					return unserialize( $data );
		}
		
	}
	
	
	/**
	 * returns an error of the Exception class
	 * 
	 * @access private
	 * @param object $obj
	 * @return string
	 */
	private function session_error( Exception $obj )
	{
		$str = 'Error <b>' . $obj->getCode() . '</b>: ';
		$str .= $obj->getMessage();
		$str .= ' In <b>' . $obj->getFile() . '</b> ';
		$str .= 'at line <b>' . $obj->getLine() . '</b>';
		
		return $str;
	}
	
	
	/**
	 * check if the session is running
	 * 
	 * @access private
	 * @return true
	 */
	private function session_check_alive( )
	{
		
		return ( file_exists( $this->session_file_name() ) === true ) ? true : false;
		
	}
	
	
	/**
	 * update the history data. only when call to new session start
	 * 
	 * @param integer $time
	 * @param string $page
	 * @access private
	 * @return boolean
	 */
	private function session_update_data_history( $time , $page )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//let's get the data of the file
		$data = $this->session_get_data( $this->session_file_name() );
		
		//$data is an array now (3D) we need to update the session_data (key) his value
		$data['session_data']['session_history'][$time] = $page;
		
		//update the session file		
		return ( $this->session_update_data( $this->session_file_name() , $data , true ) === false ) ? false : true;
		
	}
	
	
	/**
	 * get the data of the session file
	 * 
	 * @access private
	 * @param string $file
	 * @param boolean $unserialize
	 * @return string
	 */
	private function session_get_data( $file , $unserialize = true )
	{
		
		try {
			
			if( touch( $file , time() , time() ) === false ) {
				throw new Exception( self::ERROR_SESSION_TOUCH_FILE );
			}
			
			if( ($fp = fopen( $file , 'r' )) === false ) {
				throw new Exception( self::ERROR_SESSION_OPEN_FILE );
			}
			
			while( !feof($fp) ) {
				$data = fgets($fp , 4096);
			}
			
			if( fclose( $fp ) === false ) {
				throw new Exception( self::ERROR_SESSION_CLOSE_FILE );
			}
			
		}
		catch ( Exception $e )
		{
			die( $this->session_error($e) );
		}
		
		return ( $unserialize === true ) ? $this->session_data($data , 1) : $data;
		
	}
	
	
	/**
	 * update the new session data by inserting it in the session file
	 * 
	 * @access private
	 * @param string file
	 * @param string $data
	 * @param boolean $serialize
	 * @return boolean
	 */
	private function session_update_data( $file , $data , $serialze = false )
	{
		
		if( $serialze === true ) {
			$data = $this->session_data( $data , 0 );
		}
		
		try {
			
			if( touch( $file , time() , time() ) === false ) {
				throw new Exception( self::ERROR_SESSION_TOUCH_FILE );
			}
			
			if( unlink($file) === false ) {
				throw new Exception( self::ERROR_SESSION_DELETE_FILE );
			}
			
			if( touch( $file , time() , time() ) === false ) {
				throw new Exception( self::ERROR_SESSION_TOUCH_FILE );
			}
			
			if( ($fp = fopen( $file , 'w' )) === false ) {
				throw new Exception( self::ERROR_SESSION_OPEN_FILE );
			}
			
			if( fwrite( $fp , $data ) === false ) {
				throw new Exception( self::ERROR_SESSION_WRITE_FILE  );
			}
			
			if( fclose( $fp ) === false ) {
				throw new Exception( self::ERROR_SESSION_CLOSE_FILE );
			}
			
		}
		catch( Exception $e )
		{
			die( $this->session_error($e) );
		}
		
		return true;
		
	}
	
	
	/**
	 * check if the session var has the correct pattern. no starting with a numbre and special chars or spaces
	 * 
	 * @access private
	 * @param string $var
	 * @return boolean
	 */
	private function session_check_var_name( $var )
	{
		
		$pattern = '#^[a-zA-Z][a-zA-Z0-9\_]+#si';
		
		return ( preg_match($pattern , $var) ) ? true : false;
		
	}
	
	
	
	/**
	 * register a session var like $_SESSION['varName'] = varValue; (check clone OOP @php.net)
	 * 
	 * @access public
	 * @param string $name
	 * @param  mixed $value;
	 * @return boolean
	 */
	public function register_var( $name , $value )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//check the var
		if( $this->session_check_var_name($name) === false ) {
			self::$session_return_last[] = false;
		}
		
		//register the var by getting and updating the session file.
		//almost an clone of the session_update_data_history function
		$data = $this->session_get_data( $this->session_file_name() );
		
		$data['session_data']['session_vars'][$name] = $value;
				
		return ( $this->session_update_data( $this->session_file_name() , $data , true ) === false ) ? false : true;
		
	}
	
	
	/**
	 * check if the $name session var exists
	 *
	 * @param string $name
	 * @return boolean
	 */
	public function exists( $name ) 
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		if( is_array($name) === true ) {
			
			//recursie
			foreach ($name as $value) {
				$this->delete_var($value);
			}
			
		} else {
			
			//check if the var exists
			$data = $this->session_get_data( $this->session_file_name() );
						
			return ( array_key_exists( $name , $data['session_data']['session_vars'] ) === true ) ? true : false;
		
		}
		
	}
	
	
	/**
	 * change the value of an session var that exists
	 * $force is when the key($name) doesn't exists it creates the value
	 * $replace: 1 = vervangt de waarde met $value
	 * 			 2 = voegt de waarde toe aan de rechterkant 
	 * 	         3 = voor floats/integers bijtellen met opgeven $value
	 * 			 4 = voor floats/integers aftrekken met opgeven $value
	 * 			 5 = voor floats/integers vermenigvuldigen met opgeven $value
	 * 			 6 = voor floats/integers delen met opgeven $value
	 *  		 7 = voor floats/integers modules met opgeven $value
	 * 
	 * @access public
	 * @param string $name
	 * @param mixed $value;
	 * @param integer $replace
	 * @param boolean $force
	 * @return boolean
	 */
	public function update_var( $name , $value , $replace = 1 , $force = false )
	{
		
		if( $this->session_check_alive() === false ) {
			self::$session_return_last[] = false;
		}
			
		//check the var
		if( $this->session_check_var_name($name) === false ) {
			return false;
		}
		
		//register the var by getting and updating the session file.
		$data = $this->session_get_data( $this->session_file_name() );
		
		if( $force === true && $this->exists( $name ) === false ) {
			
			$data['session_data']['session_vars'][$name] = $value;
			
		} elseif( $this->exists( $name ) === true ) {
			
			switch($replace) {
				
				default: $data['session_data']['session_vars'][$name] = $value;
						 break;
						 
				case 1: $data['session_data']['session_vars'][$name] = $value;
						 break;
						 
				case 2: $data['session_data']['session_vars'][$name] .= $value;
						 break;
						 
				case 3: $data['session_data']['session_vars'][$name] += $value;
						 break;
						 
				case 4: $data['session_data']['session_vars'][$name] -= $value;
						 break;
						 
				case 5: $data['session_data']['session_vars'][$name] *= $value;
						 break;
						 
				case 6: $data['session_data']['session_vars'][$name] /= $value;
						 break;
						 
				case 7: $data['session_data']['session_vars'][$name] %= $value;
						 break;
				
			}
			
		} else {
			return false;
		}
		
		return ( $this->session_update_data( $this->session_file_name() , $data , true ) === false ) ? false : true;
		
	}
	
	
	/**
	 * delete a session var
	 * 
	 * @access public
	 * @param string $name
	 * @return boolean
	 */
	public function delete_var( $name )
	{
		
		if( $this->session_check_alive() === false ) {
			self::$session_return_last[] = false;
		}
		
		//check the var
		if( $this->session_check_var_name($name) === false ) {
			return false;
		}
		
		//register the var by getting and updating the session file.
		$data = $this->session_get_data( $this->session_file_name() );
		
		unset( $data['session_data']['session_vars'][$name] );
		
		return ( $this->session_update_data( $this->session_file_name() , $data , true ) === false ) ? false : true;
		
	}
	
	
	/**
	 * get the value of a session var
	 * 
	 * @access public
	 * @param string $name
	 * @return mixed
	 */
	public function get_var( $name )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
			
		//check the var
		if( $this->session_check_var_name($name) === false ) {
			return false;
		}
		
		//register the var by getting and updating the session file.
		$data = $this->session_get_data( $this->session_file_name() );
		
		return $data['session_data']['session_vars'][$name];
		
		
	}
	
	
	/**
	 * get the value of a session var
	 * the return is an array (if check_alive is true) -> [array] [key : time] _-_ [value : url]
	 * 
	 * @access public
	 * @return mixed
	 */
	public function get_history( )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//get the history
		$data = $this->session_get_data( $this->session_file_name() );
		
		return $data['session_data']['session_history'];
		
		
	}
	
	
	/**
	 * set the values of the session vars to empty - reset
	 * 
	 * @access public
	 * @param boolean $reset_history
	 * @return boolean
	 */
	public function reset( $reset_history = false ) 
	{
	
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//reset all variables
		$data = $this->session_get_data( $this->session_file_name() );
		
		foreach( $data['session_data']['session_vars'] as $key => $value ) {
			$data['session_data']['session_vars'][$key] = '';
		}
		
		//history?
		if( $reset_history === true ) {
		
			foreach( $data['session_data']['session_history'] as $key => $value ) {
				unset( $data['session_data']['session_history'] );
			}
		
		}
		
		return ( $this->session_update_data( $this->session_file_name() , $data , true ) === false ) ? false : true;
	
	}
	
	
	/**
	 * checks the session timeout. if it's timeout it destroy the session and creates new one
	 * 
	 * @access private
	 * @return mixed
	 */
	private function session_check_timeout( )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//get the latest key of the session_history 
		$data = $this->session_get_data( $this->session_file_name() );
		$keys = array_keys( $data['session_data']['session_history'] );
		
		$dif = time() - $keys[count($keys)-1];
		
		if( $dif > $data['session_timeout'] ) {
			$this->destroy();
		}
		
	}
	
	
	/**
	 * checks the session timeout with great force. means he looks to every session file
	 * 
	 * @access private
	 * @return mixed
	 */
	private function session_check_timeout_force( )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//force that session
		$dir = opendir( self::$session_path );
		
		while( ($file = readdir( $dir )) !== false) {
			if( $file != '.' && $file != '..' ) {
				$data = $this->session_get_data( self::$session_path . $file );
				$keys = array_keys( $data['session_data']['session_history'] );
				
				$dif = time() - $keys[count($keys)-1];
				
				if( $dif > $data['session_timeout'] && $data['session_timeout'] != 0 ) {
					$this->destroy();
				}
			}
		}
		
		closedir($dir);
		
	}
	
	
	/**
	 * destroy the session
	 * 
	 * @access public
	 * @return boolean
	 */
	public function destroy( )
	{
		
		if( $this->session_check_alive() === false ) {
			return false;
		}
		
		//destroy
		try {
			
			if( touch( $this->session_file_name() , time() , time() ) === false ) {
				throw new Exception( self::ERROR_SESSION_TOUCH_FILE );
			}
			
			if( unlink( $this->session_file_name() ) === false ) {
				throw new Exception( self::ERROR_SESSION_DELETE_FILE );
			}
			
		}
		catch ( Exception $e )
		{
			die( $this->session_error($e) );
		}
		
		return true;
		
	}
	
}
?>

[u][b]voorbeeld #01(login.php)[/b][/u]
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
 * an example (login) of the usage of the session class
 * 
 *
 * @license      GNU/GPL http://www.gnu.org/licenses/gpl.html
 * @author       stijn1989 <stijnleenknegt@gmail.com>
 * @version      Versie 1.0
 * @package      Session
 */

//start a new session
include('session.class.php');
$session = new Session( 300 , false );

//login gegevens
$uname = 'stijn';
$pwd = md5('stijn');

//logout function
function logout( $destroy = false )
{
	
	global $session;
	
	if( $session->delete_var('username') === true && $session->delete_var('logged_in') === true ) {
		echo "uitgelogd!";
	}
	
	if( $destroy === true ) {
		if( $session->destroy() === true ) {
			echo " session destoried!";
		}
		header('location: login.php');
	}
	
}

//check if we are logged in
if( $session->get_var('logged_in') === true && $session->get_var('username') == $uname ) {
	echo "<a href=\"?logout=1\">logout</a>";
	
	if( $_GET['logout'] == 1 ) {
		logout(true);
		
	}
	
} else {
	
	//login
	if( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
		
		if( empty($_POST['username']) === true || isset($_POST['username']) === false ) {
			die('You must enter a username to login!');
		}
		
		if( empty($_POST['password']) === true || isset($_POST['password']) === false ) {
			die('You must enter a password to login!');
		}
		
		if( $_POST['username'] != $uname || md5($_POST['password']) != $pwd ) {
			die('password or username is wrong!');
		}
		
		//passed the checkpoints
		$session->register_var('username' , $_POST['username']);
		$session->register_var('logged_in' , true);
		
		//refresh
		header('location: login.php');
		
	} else {
	?>
	<form action="<?php echo $PHP_SELF; ?>" method="POST">
	<table width="100%" cellpadding="0" cellspacing="0">
	
	<tr>
	<td width="50%" align="right">Username:</td>
	<td width="50%" align="left"><input type="text" name="username" size="30"></td>
	</tr>
	
	<tr>
	<td width="50%" align="right">Password:</td>
	<td width="50%" align="left"><input type="password" name="password" size="30"></td>
	</tr>
	
	<tr>
	<td colspan="2" align="center"><input type="submit" name="submit" value="Log-in"></td>
	</tr>
	
	</table>
	</form>
	<?php 
	}
}
?>

[u][b]voorbeeld #02(hits_count.php)[/b][/u]
<?php

include('session.class.php');
include('array_dump.php');

if( isset( $_GET['destroy'] ) === true && $_GET['destroy'] == 1 ) {
	$session->destroy();
}

$session = new Session(300);

if( $session->exists( 'count' ) === false || $session->exists( 'hits' ) === false ) {
	$session->register_var('count' , 0);
	$session->register_var('hits' , 0);
}

if( $session->exists( 'count' ) === true && $session->exists( 'hits' ) === true  ) {
	
	if( $session->get_var('hits') < 10 ) { 
	
		$session->update_var('hits' , 1 , 3 , false);
		
		//update the session var with big for
		for($i=0;$i<100;$i++) {
			$session->update_var('count' , $i , 3 , false );
		}
		
		echo $session->get_var('count') . ' hits: ' . $session->get_var('hits');
	
	} else { //destroy session afther 10 times
	
		echo ( $session->reset( true ) === true ) ? 'sessie gereset' : 'sessie werd niet gereset. klik <a href=\"?destroy=1\">hier</a> om deze te vernietigen';
		
	}

}
?>
<br /><br />
<h2 align="center">Session history</h2>
<?php

//array_dump: http://www.sitemasters.be/?pagina=scripts/scripts&cat=23&id=1104
array_dump( $session->get_history() );

?>