Zend - The PHP Company




HTTP

Add Code


PHP4 User Defined Session Handler for PostgreSQL  

Type: code fragment
Added by: yohgaki
Entered: 10/02/2001
Last modified: 04/12/2000
Rating: - (fewer than 3 votes)
Views: 11819
This is an example user defined session handlers for PostgreSQL. It includes some functions to access session data as an example


<?php
/*
 * session.php - User defined PHP4 session handler for PostgreSQL
 *
 * How to use:
 *
 * 1. Create session database and table below.
 * 2. Save file some where.
 * 3. Change constant SESS_CONN, SESS_TABLE, SESS_DATA_MAX for your environment.
 *    - Do not set SESS_DATA_MAX = PostgreSQL's PageSize.
 *    - It must be equal or less than (PageSize - OtherFieldSize - PageOverHead)
 *                            Default PostgreSQL(7.0.3 or less) page size is 8KB.
 *    - PostgreSQL 7.1 can store any size of data, but you need to compile PHP with libpq comes with 7.1.
 * 4. "track_vars" should be enabled.
 * 6. Get browser_check.html and save it to "/lib/browser_check.html", unless you delete/comment out codes checks session ID.
 *
 * Note:
 * Error handler, etc needed for production use.
 * Errors are triggered. It may be logged/printed according to php.ini
 *      (Note: Not all errors in session handlers are displayed)
 * If session data is very large, it could consume a lot of CPU and other resources.
 * Transaction is used to support larger session data. (With larger session data, race condition is more apparent)
 * It's good idea disabling fsync() for session database. (Who needs session data in case of crush?)
 * Pros and Cons using transaction/lock.
 *    - Pros: Ensure data consistency when user opens multiple browsers
 *    - Cons: DoS attack.
 *
 * GLOBAL Variable:
 *
 * $session_db     - PostgreSQL database connection resource
 * $session_property  - Session property such as access count, active time, created time, etc
 *
 *
 * PostgreSQL Table


-- === BEGIN ===

CREATE TABLE sys_session (
    session_id            varchar(32)     NOT NULL,
    i_created            integer         NOT NULL,
    i_active             integer         NOT NULL,
    i_counter            integer         NOT NULL DEFAULT 1,
    i_error                integer            NOT NULL DEFAULT 0,
    i_warn                integer            NOT NULL DEFAULT 0,
    t_message            text,
    t_remote_addr         varchar(32)     DEFAULT '',
    t_session_data        text,

    PRIMARY KEY (session_id)
);

-- === END ===

 *
 * If you have comment/suggestion, e-mail to yohgaki@hotmail.com
 *
 */


/////////////////////////////////// Constants ///////////////////////////////////

define('SESS_DB',        'host=dev dbname=db_session user=yohgaki');        // Session database connect string
define('SESS_TABLE',    'sys_session');                                 // Session table name
define('SESS_DATA_MAX',    102400);                                        // Session data max size. (100KB)
define('SESS_BROWSER_CHECK_HTML','/lib/browser_check.html');            // Session handling helper HTML
// browser_check.html is available at http://www.zend.com/codex.php?id=458&single=1
// This file is used to ensure browser supports Cookie and JavaScript.

//////////////////////////////// Setup Session /////////////////////////////////


// Register session halnders
session_set_save_handler (
    
'pg_session_open',
    
'pg_session_close',
    
'pg_session_read',
    
'pg_session_write',
    
'pg_session_destroy',
    
'pg_session_gc'
);

// Check if session cache_limiter should be changed.
if (isset($session_cache_limiter)) {
    
session_cache_limiter($session_cache_limiter);
}

// Start session by default
session_start();


//// Check Session ID. If you don't need this, comment out or delete ////
// if there no session ID in COOIKE or GET, redirect.
if (!isset($HTTP_COOKIE_VARS[session_name()]) && !isset($HTTP_GET_VARS[session_name()])) {
//    header('Cache-Control: public, max-age=10800');
//    header('Status: 302 Moved Templorarily');
    
if ($HTTP_SERVER_VARS['SERVER_PORT'] == 443) {
        
$session_check_url 'https://'$HTTP_SERVER_VARS['SERVER_NAME'] . SESS_BROWSER_CHECK_HTML;
    }
    else {
        
$session_check_url 'http://'$HTTP_SERVER_VARS['SERVER_NAME'] . SESS_BROWSER_CHECK_HTML;
    }
    
header('Location: '$session_check_url     .'?sid=' session_id() . '&sname=' session_name() . '&url=' rawurlencode($HTTP_SERVER_VARS['REQUEST_URI']));
    exit;
}
//// END Check Session ID ////


////////////////////////// Session Handler Functions //////////////////////////

function pg_session_open ($save_path$session_name) {
//error_log('session_open(): Called',0);
    
global $session_db;

    
$session_db pg_pconnect(SESS_DB);
    
pg_exec($session_db,'BEGIN TRANSACTION;');
    if (
pg_errormessage($session_db)) {
        
trigger_error('pg_session_open(): Cannot connect to database. 'pg_errormessage($session_db),E_USER_WARNING);
        return 
false;
    }

    return 
true;
}

function 
pg_session_close() {
//error_log('session_close(): Called',0);
    
global $session_db;

    
pg_exec($session_db,'COMMIT;');
    if (
pg_errormessage($session_db)) {
        
trigger_error('pg_session_close(): Error when commit'pg_errormessage($session_db),E_USER_WARNING);
    }
    return 
true;
}

function 
pg_session_read ($session_id) {
//error_log('session_read(): Called',0);
    
global $session_db$session_property;

    if (
strlen($session_id) != 32) {
        
trigger_error('pg_session_read(): Invalid SessionID = '.$session_idE_USER_NOTICE);
        return 
'';
    }

    
$session_id addslashes($session_id);
    
$result pg_exec($session_db'SELECT * FROM 'SESS_TABLE ." WHERE session_id = '$session_id' FOR UPDATE");
    if (
pg_numrows($result) == 1) {
        
$session_propertypg_fetch_array($result,0,PGSQL_ASSOC);
        return 
$session_property['t_session_data'];
    }
    elseif (
pg_errormessage($session_db)) {
        
trigger_error('pg_session_read(): Failed to read sessions.'pg_errormessage($session_db),E_USER_WARNING);
        return 
'';
    }
    else {
        
$session_property null// For session_write()
        
return '';
    }
}

function 
pg_session_write ($session_id$session_data) {
//error_log('session_write(): Called',0);
    
global $session_db$session_property$HTTP_SERVER_VARS;

    if (
strlen($session_id) != 32) {
        
trigger_error('pg_session_write(): Invalid Session ID ='.$session_id,E_USER_NOTICE);
        return 
false;
    }
    if (
strlen($session_data) > intval(SESS_DATA_MAX)) {
        
trigger_error('pg_session_write(): Session data too large. '$session_idE_USER_WARNING);
    }

    if (
$session_property) {
        
$query 'UPDATE 'SESS_TABLE ." SET i_active = "time() .", i_counter = ". ++$session_property['i_counter'] .", t_session_data = '$session_data' WHERE session_id = '$session_id';";
    }
    else {
        
$query 'INSERT INTO 'SESS_TABLE ." (session_id, i_created, i_active, t_remote_addr, t_session_data) VALUES ('$session_id', "time() .", "time() .", '"$HTTP_SERVER_VARS['REMOTE_ADDR'] ."', '$session_data');";
    }

    
pg_exec($session_db,$query);
    if (
pg_errorMessage($session_db)) {
        
trigger_error('pg_session_write(): Failed to INSERT/UPDATE session. '.pg_errormessage($session_db). ' Qeury: '.$query,E_USER_WARNING);
    }
    return 
true;
}

function 
pg_session_destroy ($session_id) {
//error_log('session_destory(): Called',0);
    
global $session_db;

    
pg_exec($session_db'DELETE FROM 'SESS_TABLE ." WHERE session_id = '"addslashes($session_id). "'");
    if (
pg_errormessage($session_db)) {
        
trigger_error('pg_session_destory(): Failed to DELETE session. '.pg_errormessage($session_db),E_USER_WARNING);
        return 
false;
    }
    else {
        return 
true;
    }
}

function 
pg_session_gc ($maxlifetime 4000) {
//error_log('session_gc(): Called',0);
    
global $session_db;

    
pg_exec($session_db'DELETE FROM 'SESS_TABLE ." WHERE i_active < ". (time() - $maxlifetime));
    if (
pg_errormessage($session_db)) {
        
trigger_error('pg_session_gc(): Failed to DELETE old sessions.'pg_errormessage($session_db),E_USER_WARNING);
        return 
false;
    }
    else {
        return 
true;
    }
}


//////////////////////// Session Data Access Function //////////////////////////

// Get session info (Basic Auth Info)
// Unless browser_check.html is used, this function does not work first time accessed.
function session_get_info() {
    global 
$session_propety;

    if (!isset(
$session_property['session_id'])) {
        
trigger_error('session_get_info(): Null session id'E_USER_NOTICE);
        return 
null;
    }
    else {
        return 
$session_property;
    }
}

// Set session info (Basic Auth Info)
// Unless browser_check.html is used, this function does not work first time accessed.
// Fields are MUST be defined in session table.
function session_set_info($user$login_type) {
    global 
$session_property;
    global 
$session_db;

    if (!isset(
$session_property['session_id'])) {
        
trigger_error('session_set_info(): Null session id'E_USER_NOTICE);
        return 
false;
    }

    
$session_property['user']     = (empty($user)? 'null' :$user);
    
$session_property['login_type']    = (empty($login_type)? 'null' :$login_type);
    
    
$query ='UPDATE '.SESS_TABLE.' SET user = '.$session_property['user'].
            
", login_type   = '".$session_property['login_type']."'".
            
" WHERE session_id= '".$session_property['session_id']."'";
    
pg_Exec($session_db$query);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_set_info(): '.pg_ErrorMessage($session_db). ' Qeury: '.$queryE_USER_WARNING);
        return 
false;
    }
    return 
true;
}


// This function increment session error count
// RETURN -1 if error
// Unless browser_check.html is used, this function does not work first time accessed.
function session_add_error_count($error_message 'Error unkown') {
    global 
$session_db;

    
$session_id session_id();
    if (!
$session_id) {
        
// no session. Return
        
return -1;
    }
    
// Get current error count
    
$query 'SELECT i_error FROM '.SESS_TABLE." WHERE session_id = '".$session_id."'";
    
$result_id = @pg_Exec($session_db$query);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_add_error_count(): '.pg_ErrorMessage($session_db). ' Qeury: '.$queryE_USER_WARNING);
        return -
1;
    }

    
$record pg_Fetch_Array($result_id0PGSQL_ASSOC);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_add_error_count(): '.pg_ErrorMessage($session_db), E_USER_WARNING);
        return -
1;
    }

    
$query "UPDATE "SESS_TABLE ." SET i_error=".++$record['i_error'].", t_message = '$error_message'";
    
pg_Exec($session_db$query);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_add_error_count(): '.pg_ErrorMessage($session_db). ' Qeury: '.$queryE_USER_WARNING);
        return -
1;
    }

    return 
$record['i_error'];
}

// This function increment session error count
// RETURN -1 if error
// Unless browser_check.html is used, this function does not work first time accessed.
function session_add_warn_count($warn_message 'Warning unkown') {
    global 
$session_db;

    
$session_id session_id();
    if (!
$session_id) {
        
// no session. Return
        
return -1;
    }

    
// Get current error count
    
$query 'SELECT i_warn FROM '.SESS_TABLE." WHERE session_id = '".$session_id."'";
    
pg_Exec($session_db$query);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_add_warn_count(): '.pg_ErrorMessage($session_db). ' Qeury: '.$queryE_USER_WARNING);
        return -
1;
    }

    
$record pg_Fetch_Array($result_id0PGSQL_ASSOC);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_add_warn_count(): '.pg_ErrorMessage($session_db), E_USER_WARNING);
        return -
1;
    }

    
$query "UPDATE "SESS_TABLE ." SET i_warn=".++$record['i_warn'].", t_message = '$warn_message'";
    
pg_Exec($session_db$query);
    if (
pg_ErrorMessage($session_db)) {
        
trigger_error('session_add_warn_count(): '.pg_ErrorMessage($session_db). ' Qeury: '.$queryE_USER_WARNING);
        return -
1;
    }
    return 
$record['i_warn'];
}

?>


Usage Example


<?php
/*
 * session_test.php - PostgreSQL session handler test script
 * 
 * 
 * Note: "track_vars" must be enabled. "register_globals" is better to be disabled.
 *
 */

$session_cache_limiter 'nocache';    // Do not allow cache
require_once('session.php');        // Setup and start session

?>
<html>
<head>
<title>PgSQL Session Handler Test</title>
</head>

<body bgcolor="#FFFFFF">
<h1>PgSQL Session Handler Test</h1>
<?php

// Just for an example to show that PHP4 session handles object
class object_counter {
    var 
$cnt 0;

    function 
increment() {
        
$this->cnt++;
    }

    function 
get() {
        return 
$this->cnt;
    }
}

if (!isset(
$HTTP_SESSION_VARS['integer_counter'])) {
    
// Init if no session vars.

    
$HTTP_SESSION_VARS['integer_counter']     = 0;
    
$HTTP_SESSION_VARS['object_counter']    = new object_counter;
    
$HTTP_SESSION_VARS['array_counter']        = array('counter' => 0);
}

// Increment counters
$HTTP_SESSION_VARS['integer_counter']++;
$HTTP_SESSION_VARS['object_counter']->increment();
$HTTP_SESSION_VARS['array_counter']['counter']++;

// Display values.
print("<b>n");
print(
'Integer Counter: '.$HTTP_SESSION_VARS['integer_counter']."<br>n");
print(
'Object Counter: '.$HTTP_SESSION_VARS['object_counter']->get()."<br>n");
print(
'Array Counter: '.$HTTP_SESSION_VARS['array_counter']['counter']."<br>n");
print(
"</b>n");


if (!isset(
$HTTP_GET_VARS['notable'])) {
    
// Display session database contents
    // get session data
    
$query     'SELECT * FROM sys_session ORDER BY i_active DESC';
    
$qid     pg_exec($session_db$query);

    print(
"<br>Note: <br>Session data is saved after connection is closed. Therefore, session access counter is less than 1 to Session Variable counter. ");
    print(
"Displayed session data is before it is updated. If you select database, it has the same value as counters above.<br>n");
    
// Print header
    
print("<table border="2"><tr>n");
    
$row 0;
    
$rec = @pg_fetch_array($qid$rowPGSQL_ASSOC);
    if (
is_array($rec)) {
        foreach(
$rec as $k => $v) {
            print(
"<th>$k</th>n");
        }
    }
    print(
"</tr>n");

    
// Print data
    
while($rec = @pg_fetch_array($qid$row++, PGSQL_ASSOC)) {
        print(
"<tr>n");
        foreach(
$rec as $k => $v) {
            print(
"<td>$v</td>n");
        }
        print(
"</tr>n");
    }
    print(
"</table>n");
}

?>
</body>
</html>


Rate This Script





Search



This Category All Categories