Session-Handler-Funktionen für PostgreSQL mit PHP4

Dies ist ein Beispiel für benutzerdefinierte Session-Handler-Funktionen mit PHP 4 für PostgreSQL. Im Anschluss an die Session-Handler-Funktionen finden Sie beispielhaft einige Funktionen für den Zugriff auf die Sessiondaten.

Diese Quelltexte wurden freundlicherweise von Yasuo Ohgaki, dem Maintainer der PostgreSQL-Extension von PHP, zur Verfügung gestellt.
Falls Sie Vorschläge oder Kommentare zu diesen Funktionen haben, schicken Sie eine Mail an yohgaki@php.net.

So geht's:
  1. Erstellen Sie eine Session-Datenbank und darin die weiter unten beschriebene Tabelle.
  2. Passen Sie die Konstanten SESS_CONN, SESS_TABLE und SESS_DATA_MAX an Ihre Systemumgebung an
    - Der Wert der Konstanten SESS_DATA_MAX muß kleiner sein als eine PostgreSQL Seite. SESS_DATA_MAX soll höchstens den Wert (Seitengröße - Feldgröße - Seiten-Overhead) haben. Normalerweise ist die Größe einer PostgreSQL-Seite 8 KB.
    - PostgreSQL ab der Version 7.1 kann beliebig große Daten speichern, vorausgesetzt Sie kompilieren es mit einer Version der Bibliothek libpq, die in einer Distribution ab Version 7.1 enthalten ist.
  3. "track_vars" sollte aktiviert sein
  4. Erstellen Sie eine Datei browser_check.html. Diese Datei soll prüfen, ob der Browser des Clients Cookies und JavaScript unterstützt. Speichern Sie die Datei im Verzeichnis "/lib/browser_check.html", es sei denn, sie löschen oder kommentieren die Codezeilen aus, die die Session-ID prüfen.
Beachten Sie:
  • Im praktischen Einsatz sind Funktionen zur Fehlerbearbeitung notwendig.
  • Fehler werden entsprechend Ihren Einstellungen in der php.ini protokolliert oder ausgegeben. Aber nicht alle Fehler, die in Session-Handler-Funktionen auftreten werden angezeigt.
  • Falls die Sessiondaten sehr umfangreich sind, können sie einen großen Teil der Rechnerleistung und anderer Ressourcen in Anspruch nehmen.
  • Für umfangreiche Session-Daten können Transaktionen benutzt werden. (Große Datenmengen führen offenbar eher zu Fehlerbedingungen.)
  • Für die Session-Datenbank ist es sinnvoll, fsync() auf FALSE zu setzen. Dies erhöht die Verarbeitungsgeschwindigkeit und im Falle eines Systemabsturzes werden die Sessiondaten ohnehin nicht mehr gebraucht.
  • Transaktionen sichern die Datenkonsistenz, falls ein Benutzer mehrere Browser öffnet
Die globalen Variablen:
$session_db - Die Verbindungskennung
$session_property - Die Session-Eigenschaften, wie Zugriffszähler, aktive Zeit, Erzeugungszeitpunkt usw.

Die PostgreSQL Tabelle:
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)
);

Die Quelltexte


<?php

// Definition der Konstanten
// Der Verbindungsstring zur Datenbank Session
//
define('SESS_DB', 'host=dev dbname=db_session user=yohgaki');
// Den Namen der Session-Tabelle setzen
define('SESS_TABLE', 'sys_session'));
// Maximale Größe der Sessiondaten (100KB)
define('SESS_DATA_MAX', 102400);
// Der Pfad zu der Datei browser_check.html
define('SESS_BROWSER_CHECK_HTML', '/lib/browser_check.html');

// Setup Session

// Die Session.Handler-Funktionen registrieren
session_set_save_handler 
    
'pg_session_open'
    
'pg_session_close'
    
'pg_session_read'
    
'pg_session_write'
    
'pg_session_destroy'
    
'pg_session_gc' 
); 

// Prüfen, ob der session_cache_limiter geändert werden sollte.
if (isset($session_cache_limiter)) { 
    
session_cache_limiter($session_cache_limiter); 


// Die Session starten
session_start(); 


// Session ID prüfen. Falls Sie das nicht brauchen,
// kommentieren Sie diese Zeilen aus oder löschen Sie sie.
// falls es keine Session ID in dem COOKIE-Array oder GET-Array gibt, 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; 

// ENDE Session ID prüfen

// Die Session-Handler-Funktionen

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(): Keine Verbindung zum Datenbankserver.' . 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(): Fehler beim Beenden der Transaktion' . 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(): Fehlerhafte 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(): Konnte Sessiondaten nicht lesen.' . pg_errormessage($session_db), E_USER_WARNING);
        return 
''
    } 
    else { 
        
$session_property = null; // Für 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(): Fehlerhafte Session ID ='. $session_id, E_USER_NOTICE); 
        return 
false
    } 
    if (
strlen($session_data) > intval(SESS_DATA_MAX)) { 
        
trigger_error('pg_session_write(): Die Sessiondaten sind zu gross. '. $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(): INSERT/UPDATE der Tabelle session fehlgeschlagen. ' . pg_errormessage($session_db ). ' Query: ' . $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(): Konnte die Session nicht zerstören. ' . 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(): Konnte alte Sessions nicht löschen.' . pg_errormessage($session_db), E_USER_WARNING); 
        return 
false
    } 
    else { 
        return 
true; 
    } 


// Auf Sessiondaten zugreifen

// Session-Informationen auslesen (Basic Auth Info)
// Diese Funktion arbeitet beim ersten Aufruf nicht, es sei denn
// Sie benutzen die Datei browser_check.html.
function session_get_info() { 
    global 
$session_propety

    if (!isset(
$session_property['session_id'])) { 
        
trigger_error('session_get_info(): Keine Session ID', E_USER_NOTICE);
        return 
null
    } 
    else { 
        return 
$session_property;
    } 



// Session-Informationen setzen (Basic Auth Info)
// Diese Funktion arbeitet beim ersten Aufruf nicht, es sei denn
// Sie benutzen die Datei browser_check.html.
function session_set_info($user$login_type) { 
    global 
$session_property
    global 
$session_db

    if (!isset(
$session_property['session_id'])) { 
        
trigger_error('session_set_info(): Keine 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) . ' Query: ' . $query, E_USER_WARNING);
        return 
false
    } 
    return 
true



// Diese Funktion inkrementiert den Fehlerzähler für die Sessions
// Sie gibt -1 zurück, falls ein Fehler auftritt
// Diese Funktion arbeitet beim ersten Aufruf nicht, es sei denn
// Sie benutzen die Datei browser_check.html.
function session_add_error_count($error_message 'Unbekannter Fehler') { 
    global 
$session_db

    
$session_id session_id(); 
    if (!
$session_id) { 
        
// Keine Session vorhanden.
        
return -1
    } 



// Den aktuellen Zählerstand holen
    
$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). ' Query: '.$query, E_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']; 



// Diese Funktion inkrementiert den Fehlerzähler für die Sessions
// Sie gibt -1 zurück, falls ein Fehler auftritt
// Diese Funktion arbeitet beim ersten Aufruf nicht, es sei denn
// Sie benutzen die Datei browser_check.html.
function session_add_warn_count($warn_message 'Warnung unbekannt') { 
    global 
$session_db

    
$session_id session_id(); 
    if (!
$session_id) { 
        
// Es gibt keine Session
        
return -1
    } 


// Den Stand des Fehlerzählers auslesen
    
$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']; 
}

?>


Ein Beispiel


<?php
/*
* session_test.php - Ein Testscript für die PostgreSQL Session-Handler
*
*
* Beachten Sie, dass "track_vars" aktiviert sein muss.
* "register_globals" sollte besser deaktiviert sein.
*
*/ 

$session_cache_limiter 'nocache';    // Das Caching deaktivieren
require_once('session.php');        //Die Session einrichten und starten

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

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

// Nur als Beispiel um zu zeigen, dass eine
// PHP4 Session mit Objekten umgehen kann
class object_counter 
    var 
$cnt 0

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

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


if (!isset(
$HTTP_SESSION_VARS['integer_counter'])) { 
    
// Initialisierung, falls es keine Sessionvariablen gibt.

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


// Die Zähler inkrementieren
$HTTP_SESSION_VARS['integer_counter'] ++; 
$HTTP_SESSION_VARS['object_counter'] -> increment(); 
$HTTP_SESSION_VARS['array_counter']['counter'] ++; 

// Die Werte anzeigen.
print("<b>\n"); 
print(
'Integer Zähler: '.$HTTP_SESSION_VARS['integer_counter']."<br>\n"); 
print(
'Objekt Zähler: '.$HTTP_SESSION_VARS['object_counter']->get()."<br>\n"); 
print(
'Array Zähler: '.$HTTP_SESSION_VARS['array_counter']['counter']."<br>\n"); 
print(
"</b>\n"); 


if (!isset(
$HTTP_GET_VARS['notable'])) { 
    
// Den Inhalt der Datenbank session ausgeben
    // die Sessiondaten holen
    
$query     'SELECT * FROM sys_session ORDER BY i_active DESC'
    
$qid     pg_exec($session_db$query); 

    print(
"<br>Beachten Sie: <br>Die Session-Daten werden geschrieben, nachdem die Verbindung zum Datenbankserver geschlossen wurde. Darum ist der Stand des Session-Zugriffszählers um 1 geringer als der Stand des Session-Variablen-Zählers. "); 
    print(
"Die Sessiondaten werden vor dem Update ausgegeben. Wenn Sie die Datenbank abfragen, erhalten Sie dieselben Werte wie oben angezeigt.<br>\n"); 
    
// Header ausgeben
    
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"); 


?> 

 
© 2002 by Cornelia Boenigk
Elefant
© Cornelia Boenigk
Wasenstrasse 8
72124 Pliezhausen
mail: c@pgsql.info
PostgreSQL powered
Valid HTML 4.01
Valid CSS