初識PHP會話
前言
最近在學習Laravel 這套框架,發現對PHP的會話不是很瞭解,於是乎看了PHP官網手冊,也看了網的SESSION視訊教程,看完後總覺得缺點什麼,咦~,缺點在掘金上寫篇讀書筆記,嘮叨嘮叨,也可以檢驗一下自己學習的怎麼樣,廢話不多說,直接切入正題
什麼是會話?
在瞭解PHP會話技術前,我們先討論一件事情,那就是什麼是會話。從字面意義上來說,應該是一次會談,一次交流,沒錯就是一次談話,如果是談話的話就需要有兩個談話物件,對計算機世界而言,談話物件就是客戶端和伺服器端,客戶端說一句話,伺服器回答一句。
上面的話說得太直白了,如果用計算機專業術語來解釋會話,就是這樣子的:
會話是瀏覽器和伺服器之間的多次請求和響應
好像似懂非懂的樣子,解釋一下的話,就是客戶端訪問伺服器,到伺服器做出響應,最後客戶端關閉退出,在這段時間裡產生的請求和響應,稱為客戶端和伺服器的一次會話
會話解決了什麼問題呢?
會話的出現,對程式設計師來說又有一個知識點要考了(哈哈),可不可以不用會話呢?答案是不可以的,除非你能證明你是自己。這就和現實世界是一樣的,你能證明你自己嘛,不能,你要拿出你的身份證才可以證明你自己。
在計算時間裡是怎麼解決你是你自己的這個世界難題的呢?使用HTTP協議,好像不行,HTTP協議是無狀態的,它是沒得指望的了。怎麼辦呢,計算機大佬們設計了一套會話技術,專門來處理“記住我”的問題
PHP會話工作原理
如果要理解起來還是很麻煩的,用一張圖來解釋是最合適不過的
我模擬一下圖中操作,就可以大致的瞭解PHP會話的工作原理了
我們第一次訪問PHP官網,瀏覽器發出一個HTTP請求到伺服器,伺服器接收到客戶端的請求。伺服器發現還沒有和客戶端建立會話,進行session初始化操作,建立session_id,session檔案等操作
執行程式碼,如果在執行的過程中有session資料產生,則將資料存放在超全域性陣列$_SESSION
中,程式碼執行完畢,把$_SESSION
中的資料序列化後存放在session檔案中,並設定一個會話cookie,cookie名為session_name()
,cookie值為session_id()
,把cookie資訊攜帶在對客戶端的響應中,一併返回給客戶端
第二次你訪問了PHP官網,還是會執行session初始化,發現你發出的HTTP請求攜帶有session_id
,同時去讀取這個session_id
對應的session檔案,並把檔案中的資料讀取到$_SESSION
陣列中,繼續執行程式碼,如果有設定session資料,把資料暫存在$_SESSION
陣列中,最後程式碼執行完畢,把陣列中的資料序列化後存放在session檔案裡,同樣的把sesion_id通過cookie方式存放在客戶端
,下次在訪問網站的時候,傳送給伺服器端就是了
網站訪問完了退出登入,這個時候伺服器會銷燬session,把客戶端的cookie設定過期,並刪除伺服器端的session檔案
如果下次還訪問PHP官網,重複以上操作
幾個問題?
通過上面的介紹,我發現了這樣一個問題,為什麼伺服器在第一次已經給客戶端傳送了cookie後,在第二次、第三次...都傳送cookie呢,而且內容還是一樣的,這會導致網路開銷的啊。
確實如此,每次傳送也不好,為什麼這麼做呢?
我大致猜一下這其中的原因,首先會話是有時間限制的,有的瀏覽器關閉退出後結束會話,有的會話是兩個小時內的活躍使用者
每次重置cookie,使客戶端的cookie一直處於活躍狀態,這樣瀏覽器就不會清除cookie了
如果不更新的話,cookie一旦過期,使用者就不能使用這個cookie,這樣將導致使用者必須重新登入網站後才可以繼續使用。
通過每次對使用者的響應攜帶著cookie資訊,重置cookie的過期時間,讓客戶端的cookie處於活躍狀態。
第二種情況就是,通過每次傳送cookie到客戶端,重置客戶端的cookie,防止客戶端的cookie被篡改,這種做法是很有限的,並不能完全的杜絕
以上就是我想到的為什麼要每次都給客戶端傳送cookie的原因,我也沒有去驗證這兩種說法,後面我會去找找看是什麼原因
為什麼你一直在說cookie?
這篇文章不是在說session的嗎,怎麼上面一直在提cookie,cookie又是什麼東西。cookie簡單的來說就是實現session的一種方式,session的實現也可以使用URL來實現,只不過cookie要好一些,這裡暫時不展開去說cookie,我會用另一篇文章去做介紹
使用PHP實現會話
php中已經為我們實現了很多處理session的方法,通過這些方法就可以去實現session的初始化、設定session資料、session銷燬等等操作
這裡我列出了常用的session方法,不知道如何使用的,可以隨時在php官網進行查詢,還有其他方法,你可以通過連結進行訪問
建立會話
在PHP中使用session_start()
就可以開始一個會話了,當然如果請求中的cookie攜帶了會話ID,會重用這個會話,不會建立新的會話
在PHP具體的工作會做什麼工作呢,通過PHP手冊我們可以得到這些:
當會話開始的時候,PHP內部會使用會話管理器的open和read回撥函式,這個會話管理器可以是PHP自帶的,也可以是你自己定義的(後面我會實現一個自己的會話管理器),通過session_set_save_handler
來設定自定義的會話管理器,使用read回撥函式返回的會話資料,通過反序列化資料並把資料填充到$_SESSION
全域性變數中
在開啟會話之前我們可以呼叫session_name()
對會話命名,使用session_id()
獲得sessionID
會話管理器
我們在開啟一個會話的時候,PHP會使用會話管理器來對會話資料進行處理,預設的PHP會話管理器是files,這將導致一個問題,它會鎖定會話檔案,對併發請求很大的網站來講,這將是致命的。一個比較但簡單的做法是,每次修改完會話資料後,使用session_write_close()
來儲存會話並釋放檔案鎖,這種做法是非常不建議的。
推薦使用可以併發操作的會話管理器來代替檔案儲存管理器,首選當然是推薦Redis作為session的會話管理操作,為什麼不使用memcached儲存session呢?網上的說法是memcaced是快取資料而不是儲存資料的,memcached的LAR演演算法針對每個slab類執行,而不是針對整體,這樣導致session最老的使用者會掉線,具體的原因這裡就不在深究,我也沒有用過memcached作為session管理
傳送會話ID
在第二次訪問網站的時候,會把sessionID傳送給伺服器,傳送sessionID方式有兩種:
- URL引數
- Cookies
極力推薦使用Cookie的方式來傳遞會話ID,如果使用者禁用Cookie,友情提示使用者開啟,Cookie相關的設定選項我會在會話安全中列出來
銷燬會話
使用者登出的時候要清除會話,銷燬會話操作比起建立來說是比較麻煩的,具體分為三部分,
第一步重置會話變數
php中的會話資料存放在全域性變數$_SESSION
中,需要先把全域性變數重置,重置的方法如下:
$_SESSION = array();
複製程式碼
不要使用unset($_SESSION)
來複位超級變數,因為這樣導致無法繼續使用$_SESSION
來註冊會話變數
第二步刪除會話cookie
為了徹底的銷燬會話,需要把客戶端的會話cookie刪除掉
$params= session_get_cookie_params();
setcookie(session_name(),'',time()-1,$params['path'],$params['domain'],$params['secure'],$params['httponly']);
複製程式碼
讓cookie過期就可以,這樣瀏覽器會自動刪除過期的cookie
如果開啟了session.use_strict_mode配置項後,可以不用手動刪除cookie,因為會話模組不會接受已經過期會話ID的cookie了,它會生產一個新的會話ID cookie,建議開啟這個配置項
第三步銷燬會話資料
最後使用函式session_destroy
來刪除會話資料
session_destroy();
複製程式碼
如果你使用的是file作為會話管理器,那麼還會刪除session檔案
什麼時候刪除會話資料?
php預設會使用gc(garbage collection 垃圾回收)管理會話資料,什麼時候啟動gc程式呢?
首先通過session.gc_maxlifetime 指定了過多少秒後的資料視為垃圾會話並進行清除
垃圾收集器會在會話開啟的時候,根據session.gc_probability與 session.gc_divisor 的設定來啟動垃圾收集器gc。 比如,session.gc_probability 為 1,session.gc_divisor為 100,那麼就有1/100的概率來啟動垃圾收集器gc
如果gc_probability/gc_divisor設定的過大,會頻繁的啟動gc來管理會話資料;設定的太小會有很多的過期會話資料沒有即時清除,浪費儲存空間
gc程式處理過期會話資料也是比較粗糙的,它會遍歷所有的session檔案,檢視檔案修改時間和當前系統時間,來判斷檔案是否過期而刪除檔案
所以php自帶的file會話管理器工作效率是很低的,同時也不支援併發訪問會話檔案,而 Redis 或 Memcached 天生就支援 key/value 過期機制的,用於作為會話處理器非常合適
結束
至此php中的會話基本操作就是這些了,但對會話如何使用,如何保證會話是安全的,這也是非常重要的,防人之心不可無啊。
後面我會用兩個章節來學習會話安全,以及Laravel中的session