HTTP
|
|
|
|
<?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_id, E_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_property= pg_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_id, E_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: '.$query, E_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: '.$query, E_USER_WARNING);
return -1;
}
$record = pg_Fetch_Array($result_id, 0, PGSQL_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: '.$query, E_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: '.$query, E_USER_WARNING);
return -1;
}
$record = pg_Fetch_Array($result_id, 0, PGSQL_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: '.$query, E_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, $row, PGSQL_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
|
|
|
|