詳解Session

1 Session的基本概念和設置php

Session存儲在服務端,本質上和Cookie沒有區別,都是針對http協議的侷限性而提出的一種保持客戶端和服務端間會話狀態的機制。Session常常用來網站的上下文間實現頁面變量的傳遞,用戶身份認證,程序狀態記錄等。常見的有配合cookie使用,實現保存用戶的登錄狀態,或者記錄用戶的購物下單信息等。html

在使用session以前必須先開啓session,可以使用session_start()開啓session,同cookie同樣,在開始以前不能有任何輸出內容,不然會出現以下警告:node

Warning: session_start(): Cannot send session cookie - headers already sentweb

也能夠修改php.ini中的session.auto_start = 0 爲 session.auto_start = 1,設置自動開啓session支持,這樣就沒必要每次在使用session的時候都要加上session_start()了。redis

Session的設置很是簡單,能夠直接使用$_SESSION[key]=value 的形式進行設置,其中key表示session的鍵,全部設置的session都存儲在全局數組$_SESSION中。當在代碼中設置了session時,在http請求的消息頭中會攜帶一個名爲PHPSESSID的cookie,其值是一個32位16進制的字符串。每一個客戶端向服務器請求時都會產生一個不一樣的值,若是清除掉瀏覽器的cookie,再次刷新頁面將會從新設置一個PHPSESSID的值。服務端接收到這個cookie,根據其值在服務器中找到對應的session文件,從而實現保持與客戶端連接狀態的信息,其中session中存儲着序列化的session鍵值等信息。設置了session的http請求消息頭以下:算法

Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8shell

Accept-Encoding:gzip, deflate, sdch, br數據庫

Accept-Language:zh-CN,zh;q=0.8windows

Cache-Control:max-age=0數組

Connection:keep-alive

Cookie:PHPSESSID=4680c9df2ce9ac4d1aa7f366bd92d83a

Host:localhost

Upgrade-Insecure-Requests:1

User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36

2 Session的工做原理和存儲機制

前文說到,session是經過一個名爲PHPSESSID的cookie來和服務器取得聯繫的,session經過sessionID(即PHPSESSID的值)來找到對應服務器中session的文件名的。sessionID時在客戶端和服務端是經過 HTTP Requset 和 HTTP Response 傳來傳去的。sessionID按照必定的算法生成,保證其值的惟一性和隨機性。Cookie裏存儲着session的sessionID和session的生存期,若是沒有設置session的生存期,則sessionID存儲在內存中,關閉瀏覽器時session失效,從新請求頁面時回從新註冊一個sessionID。

默認狀況下,Session是存儲在服務器硬盤上的,在php.ini可經過session.save_path設置session文件的存儲路徑,默認爲服務器上/tmp目錄。此配置指令還有一個可選的 N 參數來決定會話文件分佈的目錄深度。例如,設定爲 '5;/tmp' 將使建立的會話文件和路徑相似於 /tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If。要使用 N 參數,必須在使用前先建立好這些目錄。在 ext/session 目錄下有個小的 shell 腳本名叫 mod_files.sh,windows 版本是 mod_files.bat 能夠用來作這件事。此外注意若是使用了 N 參數而且大於 0,那麼將不會執行自動垃圾回收。文件儲存模塊默認使用 mode 600 建立文件。經過 修改可選參數 MODE 來改變這種默認行爲: N;MODE;/path ,其中 MODE 是 mode 的八進制表示。使用以上描述的可選目錄層級參數 N 時請注意,對於絕大多數站點,大於1或者2的值會不太合適——由於這須要建立大量的目錄:例如,值設置爲 3 須要在文件系統上建立 64^3 個目錄,將浪費不少空間和 inode。僅僅在絕對確定站點足夠大時,才能夠設置 N 大於2。一個session文件的內容以下:

siteadmin_username|s:7:"special";siteadmin_truename|s:6:"特殊";siteadmin_usertype|i:1;

內容的格式爲:session名|值類型:長度:值; 。

3 使用Redis存儲Session

對於大訪問量的網站來講,會有許多的客戶端和服務端創建連接,那麼將會生成許多的session文件,因爲session文件是存儲在硬盤上的,每次服務器去讀取這些session文件都要通過許多的I/O操做。PHP中可以使用session_set_save_handle()函數自定義session保存函數(如打開,關閉,寫入,讀取等)。session_set_save_handle()語法以下:

bool session_set_save_handler ( callable $open , callable $close , callable $read , callable $write , callable $destroy , callable $gc [, callable $create_sid ] )

若是想使用 PHP 內置的會話存儲機制以外的方式, 可使用本函數。 例如,能夠自定義會話存儲函數來將會話數據存儲到數據庫。該函數的參數解析以下。

open(string $savePath, string $sessionName):open 回調函數相似於類的構造函數, 在會話打開的時候會被調用。 這是自動開始會話或者經過調用 session_start() 手動開始會話 以後第一個被調用的回調函數。 此回調函數操做成功返回 TRUE,反之返回 FALSE。

close():close 回調函數相似於類的析構函數。 在 write 回調函數調用以後調用。 當調用 session_write_close() 函數以後,也會調用 close 回調函數。 此回調函數操做成功返回 TRUE,反之返回 FALSE。

read(string $sessionId):若是會話中有數據,read 回調函數必須返回將會話數據編碼(序列化)後的字符串。 若是會話中沒有數據,read 回調函數返回空字符串。在自動開始會話或者經過調用 session_start() 函數手動開始會話以後,PHP 內部調用 read 回調函數來獲取會話數據。 在調用 read 以前,PHP 會調用 open 回調函數。read 回調返回的序列化以後的字符串格式必須與 write 回調函數保存數據時的格式徹底一致。 PHP 會自動反序列化返回的字符串並填充 $_SESSION 超級全局變量。 雖然數據看起來和 serialize() 函數很類似, 可是須要提醒的是,它們是不一樣的。

write(string $sessionId, string $data):在會話保存數據時會調用 write 回調函數。 此回調函數接收當前會話 ID 以及 $_SESSION 中數據序列化以後的字符串做爲參數。 序列化會話數據的過程由 PHP 根據 session.serialize_handler 設定值來完成。序列化後的數據將和會話 ID 關聯在一塊兒進行保存。 當調用 read 回調函數獲取數據時,所返回的數據必需要和 傳入 write 回調函數的數據徹底保持一致。PHP 會在腳本執行完畢或調用 session_write_close() 函數以後調用此回調函數。 注意,在調用完此回調函數以後,PHP 內部會調用 close 回調函數。

PHP 會在輸出流寫入完畢而且關閉以後 才調用 write 回調函數, 因此在 write 回調函數中的調試信息不會輸出到瀏覽器中。 若是須要在 write 回調函數中使用調試輸出, 建議將調試輸出寫入到文件。

destroy($sessionId):當調用 session_destroy() 函數, 或者調用 session_regenerate_id() 函數而且設置 destroy 參數爲 TRUE 時, 會調用此回調函數。此回調函數操做成功返回 TRUE,反之返回 FALSE。

gc($lifetime):爲了清理會話中的舊數據,PHP 會不時的調用垃圾收集回調函數。 調用週期由 session.gc_probability 和 session.gc_divisor 參數控制。 傳入到此回調函數的 lifetime 參數由 session.gc_maxlifetime 設置。 此回調函數操做成功返回 TRUE,反之返回 FALSE。

create_sid():當須要新的會話 ID 時被調用的回調函數。 回調函數被調用時無傳入參數, 其返回值應該是一個字符串格式的、有效的會話 ID。

一個關於使用Redis代替文件存儲session的例子以下:

首先編寫一個管理session的類sessionmanager,代碼以下:

<?php

class sessionmanager{

private $redis;

private $sessionsavepath;

private $sessionname;

public function __construct()

{

$this->redis = new Redis();

$this->redis->connect('10.116.19.14',6400);

$reval = session_set_save_handler(

array($this,"open"),

array($this,"close"),

array($this,"read"),

array($this,"write"),

array($this,"destroy"),

array($this,"gc")

);

session_start();

}

public function open($patn,$name){

return true;

}

public function close(){

return true;

}

public function read($id){

$value = $this->redis->get($id);

if($value) {

return $value;

} else {

return false;

}

}

public function write($id,$data){

if($this->redis->set($id,$data)) {

$this->redis->expire($id,60);

return true;

} else {

return false;

}

}

public function destroy($id) {

if($this->redis->delete($id)) {

return true;

}

return false;

}

public function gc($maxlifetime){

return true;

}

public function __destruct()

{

session_write_close();

// TODO: Implement __destruct() method.

}

}

?>

在該類的構造函數中,使用session_set_save_handler()設置session的處理函數,實例化該類時便完成了用指定函數接管系統處理session的工做。將以上代碼保存爲sessionmanager.php文件。在write回調函數中,以傳入的sessionID做爲key,以session的值做爲redis中key的值存入redis,並設置過時時間爲60秒;read方法以傳入的sessionID爲key從redis取出相應的session的值。destroy可根據傳入的sessionID刪除redis中的session。

咱們編寫另一個設置session的腳本,並引入sessionmanager.php文件,示例化sessionmanager類。代碼以下:

<?php

include 'sessionmanager.php';

new sessionmanager();

$_SESSION['namehaha'] = 'lixiaolong';

$_SESSION['namehah'] = 'lixiaolong';

$_SESSION['namehaa'] = 'lixiaolong';

$_SESSION['namhaha'] = 'lixiaolong';

$_SESSION['namhaha'] = array(‘a'=>1,2,3,4,4);

?>

保存以上代碼爲set.php,另外編寫一個可訪問session的腳本,代碼以下:

<?php

include 'sessionmanager.php';

new sessionmanager();

var_dump($_SESSION);

?>

保存以上代碼爲get.php文件。測試時,先訪問set.php,而後再訪問get.php,會在瀏覽器輸出如下結果:

array(4) { ["namehaha"]=> string(10) "lixiaolong" ["namehah"]=> string(10) "lixiaolong" ["namehaa"]=> string(10) "lixiaolong" ["namhaha"]=> array(5) { ["a"]=> int(1) [0]=> int(2) [1]=> int(3) [2]=> int(4) [3]=> int(4) } }

可見已經成功的設置並得到了session。查看redis中存儲的session信息,

redis 127.0.0.1:6400> get ruevh62hlm809d1p2lg2o0fbv7

「namehaha|s:10:"lixiaolong";namehah|s:10:"lixiaolong";namehaa|s:10:"lixiaolong";namhaha|a:5:{s:1:"a";i:1;i:0;i:2;i:1;i:3;i:2;i:4;i:3;i:4;}"

Redis中是以string的數據類型存儲session的,其key遍是sessionID,也是HTTP Request中的cookie名爲PHPSESSID的值。session在redis和在文件中的存儲形式都是同樣的,只不過在redis對雙引號作了轉義。

本文節選自 《php7實踐指南》 陳小龍著

微信掃一掃,發現更多內容

761c57ad902ff50c.jpg

相關文章
相關標籤/搜索