摘要:會在腳本執(zhí)行完畢或調(diào)用函數(shù)之后調(diào)用此回調(diào)函數(shù)。此回調(diào)函數(shù)操作成功返回,反之返回。在回調(diào)函數(shù)中,以傳入的作為,以的值作為中的值存入,并設(shè)置過期時間為秒方法以傳入的為從取出相應(yīng)的的值。
1 Session的基本概念和設(shè)置
Session存儲在服務(wù)端,本質(zhì)上和Cookie沒有區(qū)別,都是針對http協(xié)議的局限性而提出的一種保持客戶端和服務(wù)端間會話狀態(tài)的機制。Session經(jīng)常用來網(wǎng)站的上下文間實現(xiàn)頁面變量的傳遞,用戶身份認(rèn)證,程序狀態(tài)記錄等。常見的有配合cookie使用,實現(xiàn)保存用戶的登陸狀態(tài),或者記錄用戶的購物下單信息等。
在使用session之前必須先開啟session,可使用session_start()開啟session,同cookie一樣,在開始之前不能有任何輸出內(nèi)容,否則會出現(xiàn)如下警告:
Warning: session_start(): Cannot send session cookie - headers already sent
也可以修改php.ini中的session.auto_start = 0 為 session.auto_start = 1,設(shè)置自動開啟session支持,這樣就不必每次在使用session的時候都要加上session_start()了。
Session的設(shè)置非常簡單,可以直接使用$_SESSION[key]=value 的形式進行設(shè)置,其中key表示session的鍵,所有設(shè)置的session都存儲在全局?jǐn)?shù)組$_SESSION中。當(dāng)在代碼中設(shè)置了session時,在http請求的消息頭中會攜帶一個名為PHPSESSID的cookie,其值是一個32位16進制的字符串。每個客戶端向服務(wù)器請求時都會產(chǎn)生一個不同的值,如果清除掉瀏覽器的cookie,再次刷新頁面將會重新設(shè)置一個PHPSESSID的值。服務(wù)端接收到這個cookie,根據(jù)其值在服務(wù)器中找到對應(yīng)的session文件,從而實現(xiàn)保持與客戶端鏈接狀態(tài)的信息,其中session中存儲著序列化的session鍵值等信息。設(shè)置了session的http請求消息頭如下:
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Accept-Encoding:gzip, deflate, sdch, br
Accept-Language:zh-CN,zh;q=0.8
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來和服務(wù)器取得聯(lián)系的,session通過sessionID(即PHPSESSID的值)來找到對應(yīng)服務(wù)器中session的文件名的。sessionID時在客戶端和服務(wù)端是通過 HTTP Requset 和 HTTP Response 傳來傳去的。sessionID按照一定的算法生成,保證其值的唯一性和隨機性。Cookie里存儲著session的sessionID和session的生存期,如果沒有設(shè)置session的生存期,則sessionID存儲在內(nèi)存中,關(guān)閉瀏覽器時session失效,重新請求頁面時回重新注冊一個sessionID。
默認(rèn)情況下,Session是存儲在服務(wù)器硬盤上的,在php.ini可通過session.save_path設(shè)置session文件的存儲路徑,默認(rèn)為服務(wù)器上/tmp目錄。此配置指令還有一個可選的 N 參數(shù)來決定會話文件分布的目錄深度。例如,設(shè)定為 "5;/tmp" 將使創(chuàng)建的會話文件和路徑類似于 /tmp/4/b/1/e/3/sess_4b1e384ad74619bd212e236e52a5a174If。要使用 N 參數(shù),必須在使用前先創(chuàng)建好這些目錄。在 ext/session 目錄下有個小的 shell 腳本名叫 mod_files.sh,windows 版本是 mod_files.bat 可以用來做這件事。此外注意如果使用了 N 參數(shù)并且大于 0,那么將不會執(zhí)行自動垃圾回收。文件儲存模塊默認(rèn)使用 mode 600 創(chuàng)建文件。通過 修改可選參數(shù) MODE 來改變這種默認(rèn)行為: N;MODE;/path ,其中 MODE 是 mode 的八進制表示。使用以上描述的可選目錄層級參數(shù) N 時請注意,對于絕大多數(shù)站點,大于1或者2的值會不太合適——因為這需要創(chuàng)建大量的目錄:例如,值設(shè)置為 3 需要在文件系統(tǒng)上創(chuàng)建 64^3 個目錄,將浪費很多空間和 inode。僅僅在絕對肯定站點足夠大時,才可以設(shè)置 N 大于2。一個session文件的內(nèi)容如下:
siteadmin_username|s:7:"special";siteadmin_truename|s:6:"特殊";siteadmin_usertype|i:1;
內(nèi)容的格式為:session名|值類型:長度:值; 。
3 使用Redis存儲Session
對于大訪問量的網(wǎng)站來說,會有許多的客戶端和服務(wù)端建立鏈接,那么將會生成許多的session文件,由于session文件是存儲在硬盤上的,每次服務(wù)器去讀取這些session文件都要經(jīng)過許多的I/O操作。PHP中可使用session_set_save_handle()函數(shù)自定義session保存函數(shù)(如打開,關(guān)閉,寫入,讀取等)。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 內(nèi)置的會話存儲機制之外的方式, 可以使用本函數(shù)。 例如,可以自定義會話存儲函數(shù)來將會話數(shù)據(jù)存儲到數(shù)據(jù)庫。該函數(shù)的參數(shù)解析如下。
open(string $savePath, string $sessionName):open 回調(diào)函數(shù)類似于類的構(gòu)造函數(shù), 在會話打開的時候會被調(diào)用。 這是自動開始會話或者通過調(diào)用 session_start() 手動開始會話 之后第一個被調(diào)用的回調(diào)函數(shù)。 此回調(diào)函數(shù)操作成功返回 TRUE,反之返回 FALSE。
close():close 回調(diào)函數(shù)類似于類的析構(gòu)函數(shù)。 在 write 回調(diào)函數(shù)調(diào)用之后調(diào)用。 當(dāng)調(diào)用 session_write_close() 函數(shù)之后,也會調(diào)用 close 回調(diào)函數(shù)。 此回調(diào)函數(shù)操作成功返回 TRUE,反之返回 FALSE。
read(string $sessionId):如果會話中有數(shù)據(jù),read 回調(diào)函數(shù)必須返回將會話數(shù)據(jù)編碼(序列化)后的字符串。 如果會話中沒有數(shù)據(jù),read 回調(diào)函數(shù)返回空字符串。在自動開始會話或者通過調(diào)用 session_start() 函數(shù)手動開始會話之后,PHP 內(nèi)部調(diào)用 read 回調(diào)函數(shù)來獲取會話數(shù)據(jù)。 在調(diào)用 read 之前,PHP 會調(diào)用 open 回調(diào)函數(shù)。read 回調(diào)返回的序列化之后的字符串格式必須與 write 回調(diào)函數(shù)保存數(shù)據(jù)時的格式完全一致。 PHP 會自動反序列化返回的字符串并填充 $_SESSION 超級全局變量。 雖然數(shù)據(jù)看起來和 serialize() 函數(shù)很相似, 但是需要提醒的是,它們是不同的。
write(string $sessionId, string $data):在會話保存數(shù)據(jù)時會調(diào)用 write 回調(diào)函數(shù)。 此回調(diào)函數(shù)接收當(dāng)前會話 ID 以及 $_SESSION 中數(shù)據(jù)序列化之后的字符串作為參數(shù)。 序列化會話數(shù)據(jù)的過程由 PHP 根據(jù) session.serialize_handler 設(shè)定值來完成。序列化后的數(shù)據(jù)將和會話 ID 關(guān)聯(lián)在一起進行保存。 當(dāng)調(diào)用 read 回調(diào)函數(shù)獲取數(shù)據(jù)時,所返回的數(shù)據(jù)必須要和 傳入 write 回調(diào)函數(shù)的數(shù)據(jù)完全保持一致。PHP 會在腳本執(zhí)行完畢或調(diào)用 session_write_close() 函數(shù)之后調(diào)用此回調(diào)函數(shù)。 注意,在調(diào)用完此回調(diào)函數(shù)之后,PHP 內(nèi)部會調(diào)用 close 回調(diào)函數(shù)。
PHP 會在輸出流寫入完畢并且關(guān)閉之后 才調(diào)用 write 回調(diào)函數(shù), 所以在 write 回調(diào)函數(shù)中的調(diào)試信息不會輸出到瀏覽器中。 如果需要在 write 回調(diào)函數(shù)中使用調(diào)試輸出, 建議將調(diào)試輸出寫入到文件。
destroy($sessionId):當(dāng)調(diào)用 session_destroy() 函數(shù), 或者調(diào)用 session_regenerate_id() 函數(shù)并且設(shè)置 destroy 參數(shù)為 TRUE 時, 會調(diào)用此回調(diào)函數(shù)。此回調(diào)函數(shù)操作成功返回 TRUE,反之返回 FALSE。
gc($lifetime):為了清理會話中的舊數(shù)據(jù),PHP 會不時的調(diào)用垃圾收集回調(diào)函數(shù)。 調(diào)用周期由 session.gc_probability 和 session.gc_divisor 參數(shù)控制。 傳入到此回調(diào)函數(shù)的 lifetime 參數(shù)由 session.gc_maxlifetime 設(shè)置。 此回調(diào)函數(shù)操作成功返回 TRUE,反之返回 FALSE。
create_sid():當(dāng)需要新的會話 ID 時被調(diào)用的回調(diào)函數(shù)。 回調(diào)函數(shù)被調(diào)用時無傳入?yún)?shù), 其返回值應(yīng)該是一個字符串格式的、有效的會話 ID。
一個關(guān)于使用Redis代替文件存儲session的例子如下:
首先編寫一個管理session的類sessionmanager,代碼如下:
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. } } ?>
在該類的構(gòu)造函數(shù)中,使用session_set_save_handler()設(shè)置session的處理函數(shù),實例化該類時便完成了用指定函數(shù)接管系統(tǒng)處理session的工作。將以上代碼保存為sessionmanager.php文件。在write回調(diào)函數(shù)中,以傳入的sessionID作為key,以session的值作為redis中key的值存入redis,并設(shè)置過期時間為60秒;read方法以傳入的sessionID為key從redis取出相應(yīng)的session的值。destroy可根據(jù)傳入的sessionID刪除redis中的session。
我們編寫另外一個設(shè)置session的腳本,并引入sessionmanager.php文件,示例化sessionmanager類。代碼如下:
1,2,3,4,4); ?>
保存以上代碼為set.php,另外編寫一個可訪問session的腳本,代碼如下:
保存以上代碼為get.php文件。測試時,先訪問set.php,然后再訪問get.php,會在瀏覽器輸出以下結(jié)果:
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) } }
可見已經(jīng)成功的設(shè)置并獲得了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的數(shù)據(jù)類型存儲session的,其key遍是sessionID,也是HTTP Request中的cookie名為PHPSESSID的值。session在redis和在文件中的存儲形式都是一樣的,只不過在redis對雙引號做了轉(zhuǎn)義。
本文節(jié)選自 《php7實踐指南》 陳小龍著
微信掃一掃,發(fā)現(xiàn)更多內(nèi)容
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/22538.html
摘要:協(xié)議是無狀態(tài)的,一旦數(shù)據(jù)交換完畢,客戶端與服務(wù)器端的連接就會關(guān)閉,再次交換建立新的連接,也就是說,服務(wù)器無法跟蹤會話。而和就是用與解決這種問題。值得一提的是是建立在的基礎(chǔ)上創(chuàng)建的。注意在的文件中,設(shè)置了的生命周期最長為分鐘。 在將cookie 和 session 之前需要先理解什么是會話會話: 用戶打開一個瀏覽器,點擊多個超鏈接,訪問多個web資源,然后關(guān)閉瀏覽器,整個過程稱為一個...
摘要:例如要想在多個二級域名中共享,需要設(shè)置為頂級域名,這樣就可以在所有二級域名里面或者到這個的值了。頂級域名只能獲取到設(shè)置為頂級域名的,設(shè)置為其他子級域名的無法獲取。 Cookie和Session詳解 Cookie Cookie只存儲在客服端 Cookie是什么:Cookies是web服務(wù)器存放在用戶硬盤的一段文本,Cookies允許一個wen站點在用戶的機器存放一些文本的信息,并可以在以...
摘要:目前大多數(shù)的應(yīng)用都是用實現(xiàn)跟蹤的。的安全性一般,他人可通過分析存放在本地的并進行欺騙。在安全性第一的前提下,選擇更優(yōu)??紤]到減輕服務(wù)器性能方面,應(yīng)當(dāng)適時使用。因此,維持一個會話的核心就是客戶端的唯一標(biāo)識,即。 showImg(https://segmentfault.com/img/bV8riL?w=800&h=444); 在技術(shù)面試中,經(jīng)常被問到說說Cookie和Session的區(qū)別...
摘要:依賴注入并不限于構(gòu)造函數(shù)作為經(jīng)驗,注入最適合必須的依賴關(guān)系,比如示例中的情況注入最適合可選依賴關(guān)系,比如緩存一個對象實例。 本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Dependency Injection Container in P...
摘要:什么是用來存儲客戶端的一小段文本是一門客戶端的技術(shù)因為是存儲在客戶端瀏覽器中的是為了實現(xiàn)客戶端與服務(wù)器端之間的狀態(tài)的保持技術(shù),不安全,不要使用存儲敏感信息比如登錄狀態(tài)和登錄信息一些敏感的數(shù)據(jù)應(yīng)該存儲在服務(wù)器端的值從哪里來的當(dāng)你訪問一個網(wǎng)站這 什么是cookie, 用來存儲客戶端的一小段文本是一門客戶端的技術(shù) 因為cookie是存儲在客戶端瀏覽器中的是為了實現(xiàn) 客戶端與服務(wù)器端之間的狀態(tài)...
閱讀 3085·2021-11-16 11:45
閱讀 3633·2021-09-29 09:34
閱讀 730·2021-08-16 10:50
閱讀 1597·2019-08-30 15:52
閱讀 1996·2019-08-30 15:45
閱讀 887·2019-08-29 15:23
閱讀 1950·2019-08-26 13:51
閱讀 3324·2019-08-26 12:23