明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

定制php4的session技巧

[摘要]原文作者:Ying Yang,翻譯:馬勇這篇文章描述了怎樣定制php4的session處理。我們提供一個怎樣寫一個全功能的基于mysql數(shù)據(jù)庫或dbm文件的session處理程序例子。 一、序言 新的php4有一套自己的session處理函數(shù)。缺省情況下,每個session存貯在系統(tǒng)臨時目錄的一個...
原文作者:Ying Yang,翻譯:馬勇

這篇文章描述了怎樣定制php4的session處理。我們提供一個怎樣寫一個全功能的基于mysql數(shù)據(jù)庫或dbm文件的session處理程序例子。

一、序言

新的php4有一套自己的session處理函數(shù)。缺省情況下,每個session存貯在系統(tǒng)臨時目錄的一個個獨立文件中(例如在unix系統(tǒng)中為/tmp)。
這適合或不適合,依你的需求而言。例如:如果你的支持php的web服務(wù)器分布在不同的機(jī)器上,你不能很容易地共享它們之間的session(當(dāng)然,你也可以將sessions保存在NFS共享中)。另一個潛在的問題是你機(jī)器上的數(shù)千或數(shù)百萬個session文件使你的文件系統(tǒng)變得散亂 。
對我們來說幸運的是,php4的開發(fā)者非常有遠(yuǎn)見(感謝他們),他們?yōu)槟阄疫@樣的用戶提供了擴(kuò)展session處理的接口。

這個文檔解釋一點session的處理并且提供兩個能夠工作的怎樣擴(kuò)展session處理的例子。我們的第一個例子將使session處理程序保存 session數(shù)據(jù)到DBM文件中。我們的第二個例子將保存session數(shù)據(jù)到MYSQL數(shù)據(jù)庫中。
在你開始之前,請下載ying20000602.zip 并且將它解開放到web文檔目錄中。(我已經(jīng)將它帶在本文的結(jié)尾處了)
任何一個我們寫的session處理程序會提供6個基本的函數(shù),它們將被php4的session處理程序調(diào)用,所以你不用擔(dān)心怎樣調(diào)用它們。
好在這些定制處理session的函數(shù)對你來說是完全透明的。所以你可以改動它們而不會影響你自己的PHP腳本。

這幾個函數(shù)是:
sess_open($sess_path, $session_name);

這個函數(shù)被session處理程序調(diào)用來作初始化工作。需要傳給它的兩個參數(shù)是$sess_path,它對應(yīng)你的php.ini文件中的session.save_path選項;$session_name,它對應(yīng)php.ini中的session.name選項。它們具體怎樣工作,請看下面的例子。

sess_close();

這個函數(shù)在頁面結(jié)束執(zhí)行并且session處理程序需要關(guān)閉時被調(diào)用。(注意,不要和sess_destory混淆了,它是用來結(jié)束session的)

sess_read($key);

這個函數(shù)在session處理程序讀取指定session鍵值($key)時。
這個函數(shù)檢索并返回標(biāo)識為$key的session數(shù)據(jù).(注意:你不用擔(dān)心怎樣序列化和反序列化數(shù)據(jù),如果你不知道這是什么意思,不要擔(dān)心它)

譯者注:序列化是將變量或?qū)ο笤诔绦蚪Y(jié)束或需要時保存在文件中,在下次程序運行或需要時再調(diào)入內(nèi)存的技術(shù),有別于只保存數(shù)據(jù)的方法。

sess_write($key, $val);

這個函數(shù)據(jù)在session處理程序需要將數(shù)據(jù)保存時調(diào)用,這種情況經(jīng)常在你的程序結(jié)束時發(fā)生。
它負(fù)責(zé)將數(shù)據(jù)保存在下次能用sess_read($key)函數(shù)檢索的地方。

sess_destroy($key);

這個函數(shù)在需要消毀session時。它負(fù)責(zé)刪除session并且清除環(huán)境。

sess_gc($maxlifetime);
這個函數(shù)負(fù)責(zé)清理碎片。在這種情況下,它負(fù)責(zé)刪除過時的session數(shù)據(jù)。session處理程序會偶爾調(diào)用它們。

現(xiàn)在我們已經(jīng)清楚了我們提供的函數(shù)。它們不是非要這樣命名,但必須接受這些參數(shù)。(不管你需不需要它們)

DBM session 處理程序

我們的第一個范例是寫一個保存session數(shù)據(jù)到DBM文件中的定制session處理程序。(這是ying20000602.zip中的session_dbm.php文件)

有很多充足的理由讓你要這樣做,例如,如果你在isp那兒有一臺共享的服務(wù)器(譯注:相當(dāng)于我們說的虛擬主機(jī)吧)并且你不想讓你的session數(shù)據(jù)

和別人的混在一起。

重要注釋:
在你試驗這些程序時你的php4必須有DBM支持。如果不是這樣的(譯注:如果沒有DBM支持)會很難看,真的很難看!
我們要做的這些工作將會得到一個所有session數(shù)據(jù)的DBM文件。(萬一你不知道,DBM文件象一個僅保存"鍵/值"對的非常簡單的數(shù)據(jù)庫.
由下面的6個函數(shù)據(jù)實現(xiàn):


sess_open($sess_path, $session_name);
我們將調(diào)用dbmopen()打開一個處于讀寫模式的DBM文件。我們的DBM文件將被命名為/tmp/PHPSESSID,除非你修改了php.ini中的session路
徑和名字設(shè)置。


sess_close();
在這個函數(shù)中,我們將簡單地調(diào)用dbmclose()函數(shù)關(guān)閉DBM文件。


sess_read($key);
這兒我們僅僅調(diào)用dbmfetch()載入和參數(shù)$key相關(guān)連的session數(shù)據(jù)。
在載入一個session時,我們需要保證讀入的不是一個過期數(shù)據(jù),所以我們必須給session配上一個時間標(biāo)記。

為什么?因為在它們失效,不管什么原因而沒有被刪掉時,我們不會意外地讀入過期數(shù)據(jù)。這會是一個很大的禁忌。


我們知道DBM文件只保存 鍵/值 對,因此不得不在寫session數(shù)據(jù)時將時間標(biāo)記同" 值"一起寫入,在讀session數(shù)據(jù)時去掉。

任何已經(jīng)過期的session將被忽略?纯催@個源程序,它會讓你更清楚。



sess_write($key, $val);
寫入一個session,我們會使用dbmreplace()函數(shù)。注意,從上所述我們要保存過期時間標(biāo)記在session中,所以我們要將時間標(biāo)記綁到值上。

sess_destroy($key);
消毀一個session很容易,我們只需要調(diào)用dbmdelete()函數(shù)將它從session文件中刪除。

sess_gc($maxlifetime);
過期數(shù)據(jù)收集在這兒有點令人討厭但卻是必需的,為了達(dá)到目的我們在循環(huán)掃描所有保存在DBM文件中的session并且刪掉過期的。這會很慢因
為我們循環(huán)通過所有保存在這個文件中的所有session數(shù)據(jù)。

現(xiàn)在我們已經(jīng)有了一個DBM session處理程序,太酷了!

現(xiàn)在,我們讓這些session保存到mysql數(shù)據(jù)庫中。

Mysql session處理程序
(This
我們的下一個范例是寫一個將session數(shù)據(jù)存到mysql數(shù)據(jù)庫的定制session處理程序。(這個在session_mysql.php文件中,見文章尾部)
在你有許多支持PHP的服務(wù)器并且你需要共享它們之間的session時你會想將session保存在數(shù)據(jù)庫中的。(比如你服務(wù)于很多用戶并且需要
負(fù)載平衡時)
You have a bunch of machines doing web/PHP stuff, a machine serving
你有一批機(jī)器作支持php的服務(wù)器,需要一臺機(jī)器作你的普通數(shù)據(jù)庫服務(wù)器,另外一臺運行mysql數(shù)據(jù)庫處理session。僅這樣對大多數(shù)人來
說就具有很大的殺傷力的。:)(譯注:可能意思是太酷了吧)

重要提示:
在你試驗之前你的php必須支持mysql。(譯注:這好象已經(jīng)不成問題了,php現(xiàn)在已經(jīng)內(nèi)建mysql支持了)如果不是這樣的話,事情會很難看,真的
很難看。
首先我們在mysql中創(chuàng)建一個session數(shù)據(jù)庫,并且創(chuàng)建一個session表。先運行你的mysql客戶端并且運行下面的命令:
mysql> CREATE DATABASE sessions;

mysql> GRANT select, insert, update, delete ON sessions.* TO phpsession@localhost
-> IDENTIFIED BY 'phpsession';

mysql> CREATE TABLE sessions (
-> sesskey char(32) not null,
-> expiry int(11) unsigned not null,
-> value text not null,
-> PRIMARY KEY (sesskey)
-> );

下一步,修改session_mysql.php文件的$SESS_DB* 變量使其匹配你機(jī)器上的數(shù)據(jù)庫設(shè)置。在你繼續(xù)之前確信一切看起來良好。

我們的6個函數(shù)會依靠mysql數(shù)據(jù)庫工作:


sess_open($sess_path, $session_name);
我們需要調(diào)用mysql_Pconnect(),然后用mysql_selsect_db()選擇session數(shù)據(jù)庫 。$sess_path 和$session_name 參數(shù)
是無關(guān)的但我們不得不保留它們。(譯注:原文如此,可能是為了兼容吧)

sess_close();
我們要打開一個mysql永久連接因此我們在這個函數(shù)中不做任何事。(一個空函數(shù))


sess_read($key);
這個竅門就是一個簡單的select語句,我們想要讀取所給的$key的session數(shù)據(jù),需要指定過期時間信息。

sess_write($key, $val);
寫session數(shù)據(jù)用了一個小把戲。我們首先試圖用insert語句保存session數(shù)據(jù)到數(shù)據(jù)庫中。如果失敗(主鍵約束)則意味著這個key已經(jīng)寫入,然后我們
不得不用一update語句代替。

sess_destroy($key);
刪除一個session很容易,我們只需要從數(shù)據(jù)庫中刪除這個鍵值。

sess_gc($maxlifetime);
處理過期session也很容易,我們只需要從數(shù)據(jù)庫中刪除過期session().

作為結(jié)束這個小教程,希望你在擴(kuò)展php4的session處理時有個好感覺。
這個范例只是簡單的示范了你怎樣做才能擴(kuò)展它們使其適應(yīng)你的需要,如果你找到什么bug的話,請讓我知道:)

faq:
如果你擔(dān)心session文件在/tmp目錄中和別的虛擬機(jī)混淆,正好將它們存到別處。
這就是為什么有session.save_path選項的原因。
如果你擔(dān)心性能的話你可以考慮將session存在共享內(nèi)存中。你只需要在編譯php時加上MM支持(--with-mm)并指定sessio.save_handler 為 mm
在.htaccess中,php.ini中或httpd.conf中。

我覺得只有多臺機(jī)器要保存session時才會用到數(shù)據(jù)庫。
(最后這句實在不好譯,自已看吧)

session_dbm.php
=========================================================================
<?
/* ------------------------------------------------------------------------
* session_dbm.php
* ------------------------------------------------------------------------
* PHP4 DBM Session Handler
* Version 1.00
* by Ying Zhang (ying@zippydesign.com)
* Last Modified: May 21 2000
*
* ------------------------------------------------------------------------
* TERMS OF USAGE:
* ------------------------------------------------------------------------
* You are free to use this library in any way you want, no warranties are
* expressed or implied. This works for me, but I don't guarantee that it
* works for you, USE AT YOUR OWN RISK.
*
* While not required to do so, I would appreciate it if you would retain
* this header information. If you make any modifications or improvements,
* please send them via email to Ying Zhang <ying@zippydesign.com>.
*
* ------------------------------------------------------------------------
* DESCRIPTION:
* ------------------------------------------------------------------------
* This library tells the PHP4 session handler to write to a DBM file
* instead of creating individual files for each session.
*
* ------------------------------------------------------------------------
* INSTALLATION:
* ------------------------------------------------------------------------
* Make sure you have DBM support compiled into PHP4. Then copy this
* script to a directory that is accessible by the rest of your PHP
* scripts.
*
* ------------------------------------------------------------------------
* USAGE:
* ------------------------------------------------------------------------
* Include this file in your scripts before you call session_start(), you
* don't have to do anything special after that.
*/

$SESS_DBM = "";
$SESS_LIFE = get_cfg_var("session.gc_maxlifetime");

function sess_open($save_path, $session_name) {
global $SESS_DBM;

$SESS_DBM = dbmopen("$save_path/$session_name", "c");
return ($SESS_DBM);
}

function sess_close() {
global $SESS_DBM;

dbmclose($SESS_DBM);
return true;
}

function sess_read($key) {
global $SESS_DBM, $SESS_LIFE;

$var = "";
if ($tmp = dbmfetch($SESS_DBM, $key)) {
$expires_at = substr($tmp, 0, strpos($tmp, " "));

if ($expires_at > time()) {
$var = substr($tmp, strpos($tmp, " ") + 1);
}
}

return $var;
}

function sess_write($key, $val) {
global $SESS_DBM, $SESS_LIFE;

dbmreplace($SESS_DBM, $key, time() + $SESS_LIFE . " " . $val);
return true;
}

function sess_destroy($key) {
global $SESS_DBM;

dbmdelete($SESS_DBM, $key);
return true;
}

function sess_gc($maxlifetime) {
global $SESS_DBM;

$now = time();
$key = dbmfirstkey($SESS_DBM);
while ($key) {
if ($tmp = dbmfetch($SESS_DBM, $key)) {
$expires_at = substr($tmp, 0, strpos($tmp, " "));
if ($now > $expires_at) {
sess_destroy($key);
}
}

$key = dbmnextkey($SESS_DBM, $key);
}
}

session_set_save_handler(
"sess_open",
"sess_close",
"sess_read",
"sess_write",
"sess_destroy",
"sess_gc");
?>
=====================================================================================
session_mysql.php
=======================================================================================
<?
/* ------------------------------------------------------------------------
* session_mysql.php
* ------------------------------------------------------------------------
* PHP4 MySQL Session Handler
* Version 1.00
* by Ying Zhang (ying@zippydesign.com)
* Last Modified: May 21 2000
*
* ------------------------------------------------------------------------
* TERMS OF USAGE:
* ------------------------------------------------------------------------
* You are free to use this library in any way you want, no warranties are
* expressed or implied. This works for me, but I don't guarantee that it
* works for you, USE AT YOUR OWN RISK.
*
* While not required to do so, I would appreciate it if you would retain
* this header information. If you make any modifications or improvements,
* please send them via email to Ying Zhang <ying@zippydesign.com>.
*
* ------------------------------------------------------------------------
* DESCRIPTION:
* ------------------------------------------------------------------------
* This library tells the PHP4 session handler to write to a MySQL database
* instead of creating individual files for each session.
*
* Create a new database in MySQL called "sessions" like so:
*
* CREATE TABLE sessions (
* sesskey char(32) not null,
* expiry int(11) unsigned not null,
* value text not null,
* PRIMARY KEY (sesskey)
* );
*
* ------------------------------------------------------------------------
* INSTALLATION:
* ------------------------------------------------------------------------
* Make sure you have MySQL support compiled into PHP4. Then copy this
* script to a directory that is accessible by the rest of your PHP
* scripts.
*
* ------------------------------------------------------------------------
* USAGE:
* ------------------------------------------------------------------------
* Include this file in your scripts before you call session_start(), you
* don't have to do anything special after that.
*/

$SESS_DBHOST = "localhost"; /* database server hostname */
$SESS_DBNAME = "sessions"; /* database name */
$SESS_DBUSER = "phpsession"; /* database user */
$SESS_DBPASS = "phpsession"; /* database password */

$SESS_DBH = "";
$SESS_LIFE = get_cfg_var("session.gc_maxlifetime");

function sess_open($save_path, $session_name) {
global $SESS_DBHOST, $SESS_DBNAME, $SESS_DBUSER, $SESS_DBPASS, $SESS_DBH;

if (! $SESS_DBH = mysql_pconnect($SESS_DBHOST, $SESS_DBUSER, $SESS_DBPASS)) {
echo "<li>Can't connect to $SESS_DBHOST as $SESS_DBUSER";
echo "<li>MySQL Error: ", mysql_error();
die;
}

if (! mysql_select_db($SESS_DBNAME, $SESS_DBH)) {
echo "<li>Unable to select database $SESS_DBNAME";
die;
}

return true;
}

function sess_close() {
return true;
}

function sess_read($key) {
global $SESS_DBH, $SESS_LIFE;

$qry = "SELECT value FROM sessions WHERE sesskey = '$key' AND expiry > " . time();
$qid = mysql_query($qry, $SESS_DBH);

if (list($value) = mysql_fetch_row($qid)) {
return $value;
}

return false;
}

function sess_write($key, $val) {
global $SESS_DBH, $SESS_LIFE;

$expiry = time() + $SESS_LIFE;
$value = addslashes($val);

$qry = "INSERT INTO sessions VALUES ('$key', $expiry, '$value')";
$qid = mysql_query($qry, $SESS_DBH);

if (! $qid) {
$qry = "UPDATE sessions SET expiry = $expiry, value = '$value' WHERE sesskey = '$key' AND expiry > " . time();
$qid = mysql_query($qry, $SESS_DBH);
}

return $qid;
}

function sess_destroy($key) {
global $SESS_DBH;

$qry = "DELETE FROM sessions WHERE sesskey = '$key'";
$qid = mysql_query($qry, $SESS_DBH);

return $qid;
}

function sess_gc($maxlifetime) {
global $SESS_DBH;

$qry = "DELETE FROM sessions WHERE expiry < " . time();
$qid = mysql_query($qry, $SESS_DBH);

return mysql_affected_rows($SESS_DBH);
}

session_set_save_handler(
"sess_open",
"sess_close",
"sess_read",
"sess_write",
"sess_destroy",
"sess_gc");
?>
=========================================================================
test.php
==========================================================================
<?
/* ------------------------------------------------------------------------
* test.php
* ------------------------------------------------------------------------
* PHP4 Customer Session Handler Test Script
* Version 1.00
* by Ying Zhang (ying@zippydesign.com)
* Last Modified: May 21 2000
*/

/* default to DBM handler */
if (! isset($handler)) {
$handler = "dbm";
}

/* default action is increment */
if (! isset($action)) {
$action = "increment";
}

/* load up the appropriate session handling script, depending on the handler */
if ($handler == "dbm") {
include("session_dbm.php");

} elseif ($handler == "mysql") {
include("session_mysql.php");

} else {
echo "<li>Unrecognized handler ($handler)";
die;
}

/* start the session and register a simple counter */
session_start();
session_register("count");

/* figure out what we should do, depending on the action */
switch ($action) {
case "increment" :
$count = isset($count) ? $count + 1 : 0;
break;

case "destroy" :
session_destroy();
break;

case "gc" :
$maxlife = get_cfg_var("session.gc_maxlifetime");
sess_gc($maxlife);
break;

default:
echo "<li>Unknown action ($action)";
break;
}
?>

<h1>Session Test Script</h1>
<ul>
<li>Handler: <b><?=$handler?></b>
<li>Action: <b><?=$action?></b>
<li>Count: <b><?=$count?></b>
</ul>

<hr size=1>
<form>
<table>
<tr>
<td>Handler:</td>
<td>
<select name="handler">
<option value="dbm">DBM</option>
<option value="mysql">MySQL</option>
</select>
</td>
</tr>
<tr>
<td>Action:</td>
<td>
<select name="action">
<option value="increment">Increment</option>
<option value="destroy">Session Destroy</option>
<option value="gc">Force Garbage Collection</option>
</select>
</td>
</tr>
<tr>
<td></td>
<td><br><input type="submit"></td>
</tr>
</table>
</form> 


標(biāo)簽:定制php4的session技巧