一、默認的存儲機制——文件
PHP默認使用磁盤文件來保存 SESSION數據,即默認存儲方式爲 files。
php.ini中關於 SESSION的基本配置:
1、session.save_handler = files //SESSION的存儲方式
2、session.save_path = 'xxx' //存放文件的路徑
2、session.name = 'PHPSESSID' //SESSION的名稱
3、session.use_trans_sid = 0 //爲1則表示給每個URL加上 SESSION名 = 會話Id,一般情況下不建議開啓,不安全
4、session.cookie.lifetime = 0 /*即存儲session_id的 COOKIE 的保存時間,只要 COOKIE還在,客戶端就能通過 session_id 來找到之前的 SESSION 數據。默認爲0,COOKIE保存在服務器內存中,因此關閉瀏覽器之後,就找不到之前的SESSION數據了,因爲保存會話id的COOKIE已經失效了,這也是爲什麼 SESSION數據只能在同一次會話中獲取了。設置爲大於0的數,則 SESSION 可以在多次會話中獲取,知道 COOKIE 過期*/
1、session_start
1、session_start() 是 session機制的開始,它有一定概率開啓垃圾回收機制,因爲 session是存放在文件中的,所以垃圾回收機制會去刪除那些已經過期了的磁盤文件。
這個開啓垃圾回收的概率是根據php.ini的配置決定的,但是有的系統是 session.gc_probability =0,這也就是說概率是0,而是通過cron腳本來實現垃圾回收。
session.gc_probability =1
session.gc_divisor =1000
session.gc_maxlifetime =1440
啓動垃圾回收機制的概率是 session.gc_probability / session.gc_divisor,(網站訪問量很大時,建議把這個值設置小一點)。
而 session.gc_maxlifetime表示session默認最大生命週期,若在這個期間秒內訪問了該session,則重新計時。
2、如果在使用 session_start() 之前沒有通過 session_id() 函數手動設置id,session會判斷當前是否有 $_COOKIE[session_name()](session_name()返回session名)。
3、如果第2步中得到了id,則會去 session.save_path 指定的目錄裏尋找名爲 ‘SESS_’. session_id() 的文件,讀取文件的內容並反序列化成數組,然後賦給 $_SESSION數組。
4、如果第2步中沒有得到id,即既沒有手動設置,也沒有相應 COOKIE,或者找不到對應的 SESSION 文件,則生成一個UUID來作爲session_id,並在發送響應時將session_id以COOKIE的方式發送到客戶端。
相當於執行了下面 COOKIE 操作,注意的是,這只是模擬操作,COOKIE實際上是在header頭中發送的,因此這之前是不能有輸出的, 同理還有session_regenerate_id()。
setcookie(session_name(),
session_id(), //生成一個id
session.cookie_lifetime, //默認0,即會話結束後就cookie就消失
session.cookie_path, //默認'/'當前程序根目錄下都有效
session.cookie_domain, //默認爲空
)
2、爲$_SESSION賦值
比如新添加一個值 $_SESSION[‘t’] =’a’; 那麼這個$_SESSION 會先保存在內存中,當腳本執行結束的時候,再把 $_SESSION 數組序列化之後寫入到 ‘SESS_’ . session_id() 文件中,然後關閉相關資源。
這個階段可以執行更改 session_id 的操作,比如銷燬一箇舊的session_id,生成一個新的 session_id。一般用在自定義 SESSION 機制角色的轉換上,比如匿名用戶持有一個 session_id,當它登錄後需要更換新的 session_id:
if (isset($_COOKIE[session_name()])) {
setcookie(session_name(), '', time() - 42000, '/'); //舊session cookie過期
}
session_regenerate_id();//這一步會生成新的session_id
//然後繼續操作 SESSION
3、寫入SESSION文件
在腳本結束的時候會將 $_SESSION 數組經序列化後寫入到’SESS_’. session_id() 文件中。
4、 銷燬SESSION
默認情況下,SESSION發出去的COOKIE屬於即時COOKIE,保存在客戶端內存中,當瀏覽器關閉後,就會消失。
假如有時需要人爲強制過期,而不是關閉瀏覽器時才過期,比如退出登錄等情況,那麼就需要在代碼裏手動銷燬 SESSION 數據,方法有幾種:
- setcookie(session_name(), ”, time() -8000000, ..);//讓客戶端保存的COOKIE過期
- unset($_SESSION); //直接刪除所有的 $_SESSION數據,用戶通過session_id取得的SESSION是空的
- session_destroy(); //直接刪除對應的SESSION文件
當不關閉瀏覽器的情況下,再次刷新,2和3情況下,用戶依然持有 session_id,但是已經得不到任何SESSION數據了。而1情況下,用戶的訪問已經找不到之前設置的SESSION了。
如果 COOKIE 被禁用了怎麼辦?
1.可以在 URL 裏添加一個PHPSESSID=>sesssion_id
在頁面加入:
if(isset($_GET[‘PHPSESSID’]){
//手動設置 sesssion_id
session_id($_GET[‘PHPSESSID’]);
}
//程序會尋找該session_id對應的SESSION文件
session_start();
//獲得數據
2.可以啓用session.use_trans_sid來指定是否啓用透明 SID 支持 ,即在每個URL中的Query部分加上session_name=session_id。(對JS中的URL不起作用。)
php.ini中:
session.use_trans_sid = 1
這樣,即使禁用了 COOKIE,也能在跨頁時傳遞 SESSION變量了。
二、自定義 SESSION 存儲方式
首先將 php.ini 文件中的 session.save_handler 設置爲 user。
然後通過 session_set_save_handler(‘open’,’close’,’read’,’write’,’destroy’,’gc’) 函數來實現。這6個回調函數必須返回 TRUE/FALSE 。
下面是個簡單的例子,與默認的files處理機制類似。可以通過下面的例子來進一步掌握默認機制的原理,也可以方便地擴展,比如使用數據庫保存SESSION。
注意,ssesion_id 的傳遞依然是使用COOKIE,只是保存 SESSION 數據的方式改變了。
// 執行 session_start() 後執行的第一個回調函數
function open($save_path, $session_name)
{
global $sess_save_path; // 聲明爲全局變量是爲了讓之後回調的函數能訪問該變量
if (!is_dir($save_path)) {
mkdir($save_path);
}
$sess_save_path = $save_path; // $save_path 爲 session.save_path
return(true);
}
function close()
{
return(true);
}
function read($id)
{
global $sess_save_path;
$sess_file = "$sess_save_path/yyy_$id";
return (string) file_get_contents($sess_file);
}
function write($id, $sess_data)
{
global $sess_save_path;
$sess_file = "$sess_save_path/yyy_$id";
return file_put_contents($sess_file, $sess_data) === false? false : true;
}
// session_destory()函數的工作原理。
function destroy($id)
{
global $sess_save_path;
$sess_file = "$sess_save_path/yyy_$id";
return(@unlink($sess_file));
}
// 執行session_start()時,一定機率執行該函數,$maxlifetime 爲配置項 session.gc_maxlifetime 的值
function gc($maxlifetime)
{
global $sess_save_path;
foreach (glob("$sess_save_path/yyy_*") as $filename) {
if (filemtime($filename) + $maxlifetime < time()) {
@unlink($filename);
}
}
return true;
}
session_set_save_handler("open", "close", "read", "write", "destroy", "gc");
session_start();
/*
...
*/