1. 程式人生 > 程式設計 >初識PHP會話

初識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