跳轉到主要內容
想從套件提供自訂的 Session 儲存位置時,請將儲存處理的註冊與應用程式的選擇分開。設計上應避免僅安裝套件就切換登入狀態的儲存位置,讓使用者在確認對既有 Session 的影響後再啟用。 本頁確認了 Laravel Framework v13.39.0 的官方原始碼。基本操作請參閱 Session,提供者的自動載入請參閱套件自動探索。

extend 接收的是處理器而非 Store

Session::extend() 的回呼會回傳實作 PHP SessionHandlerInterface 的儲存用處理器。它並不是直接回傳實作 Illuminate\Contracts\Session\Session 的 Store 或快取 Repository 的 API。 SessionManager::callCustomCreator() 會將回呼的回傳值傳給 buildSession(),並依 session.encrypt 以 Store 或 EncryptedStore 包裝。此時也會套用 Cookie 名稱與 session.serialization。 在一般的 HTTP 處理中,StartSession 中介層負責管理 Session 的啟動與儲存。為了提供處理器,套件端不需要呼叫 session_start() 或 session_set_save_handler()。

分開思考儲存契約

實作自訂後端時,需要滿足以下職責。 由於 Store 負責序列化與還原,處理器中不要再對 Session 陣列執行 serialize() 或 json_encode()。啟用加密時,也請直接儲存經 EncryptedStore 處理後的字串。
請區分資料未儲存與後端故障。經確認,Store::readFromHandler() 在讀取結果為空或 false 時,會將其視為空的 Session。此外,Store::save() 不會檢查處理器 write() 的回傳值。若套件希望一律將故障視為例外,請不要吞掉儲存位置的例外;對於回傳 false 的後端,也應在處理器端偵測並轉換為例外,明確訂出方針並加以測試。失敗時 Laravel 不一定會自動重試或通知儲存失敗。

在 boot 中只註冊建立處理

以下是重複使用內建 CacheBasedSessionHandler 的最小範例。它並非實作新的儲存協定,而是示範如何註冊使用具名快取儲存的自訂驅動。假設 Acme\SessionBridge\ 透過 Composer 的 PSR-4 對應到 src/。 在 src/SessionBridgeServiceProvider.php 準備提供者。
Session::extend() 會取得 SessionManager 並註冊建立處理,但此時不會解析回呼內的快取儲存。儲存位置要到該驅動首次被選用時才會建立。請不要在提供者的 boot() 中執行連線確認或資料儲存。 使用自訂的 SessionHandlerInterface 實作時,請替換回呼所回傳的處理器。如果連 new Store(...) 都在回呼中完成,就會與 SessionManager 預期的回傳值不符。
  • 提供者以一般的非延遲提供者載入。
  • 在 Session 首次被使用前完成註冊。若其他提供者會先解析自訂驅動,也要確認載入順序。
  • 驅動名稱請使用套件專屬的名稱。同名的 extend() 會覆寫建立處理;若註冊內建驅動的名稱,則會比內建實作更優先。

由使用端應用程式明確選擇

使用者修改既有 config/session.php 中對應的鍵,其他設定保持不變。
儲存位置方面,請在 config/cache.php 既有的 stores 陣列中新增具名儲存。以下是使用 Redis 連線 default 的範例。Redis 的連線設定與所需的用戶端由應用程式端準備。
此範例的連線與前綴由快取儲存的設定決定。由於不會經過內建 Redis Session 驅動的 createRedisDriver(),即使新增 session.connection 或 session.prefix,此回呼也不會自動套用。 此回呼不會變更 Repository 或儲存位置的設定,而是直接使用具名儲存。測試用的 array 儲存在同一個應用程式內也會共用相同的儲存區域。 套件不應無條件改寫 session.driver、Cookie 名稱或應用程式整體的快取前綴。是否採用不與其他用途共用儲存區域的設計,以及能否讓多台伺服器使用同一個儲存位置,也請與使用者確認。
若目的只是一般的 Redis 或資料庫儲存,請先使用內建的 Session 驅動。自訂驅動是需要新的儲存位置或額外儲存規範時的擴充點。

維持 TTL 的單位與儲存格式

CacheBasedSessionHandler 的建構子以分鐘為單位接收保存期間。write() 每次都會將 $minutes * 60 傳給快取 Repository 的 put(),因此在上面的範例中,每次寫入都會更新 TTL。gc() 回傳 0,將失效處理交給快取端。 另一方面,StartSession 傳給處理器 gc($lifetime) 的期間以秒為單位。請避免將 session.lifetime 的值直接當成秒來儲存。 此外,以下變更不只是修改設定名稱就能解決。 若在套件更新中變更這些項目,請考量重新驗證的提示、是否需要遷移處理,以及新舊版本並存的部署。與套件的版本相容性管理相同,儲存格式與設定鍵也應納入需要維護的公開規格。

已建立的 Store 不會只因設定變更而被替換

SessionManager 的父類別 Manager 會依名稱保留已建立的驅動。此外,SessionServiceProvider 會將 session.store 註冊為 singleton。 forgetDrivers() 也不是刪除 Session 資料的 API。在執行中的請求裡重新建立 Store,會造成與已讀取的資料或其他參照之間不一致。 此範例的 CacheManager 也會保留具名儲存。若變更了其連線設定,只重新解析 SessionManager 並不足夠。 在使用設定快取的部署中,請重建快取,並重新啟動長時間執行的 worker,讓它載入新的程式碼與設定。應用程式的重新啟動與已儲存資料的遷移是不同的工作。

Session 儲存與請求鎖是兩回事

僅實作自訂處理器,並不會讓針對同一 Session 的並行請求序列化。StartSession 的阻擋功能使用以 session.block_store 選擇的快取儲存的鎖,它不一定與 Session 本體的儲存位置相同。 以 Route::post(...)->block() 等方式控制時,請另外驗證支援鎖的快取儲存、多台伺服器之間的共用,以及等待時間與持有時間。套件不應擅自啟用應用程式整體的阻擋設定。

測試到儲存與重新載入

在使用 Orchestra Testbench 的套件中,請準備載入提供者並實際選用自訂驅動的測試。以下是 tests/SessionDriverTest.php 的範例。測試用的 array 快取不使用外部連線。
此測試是在同一個應用程式內重新建立 Store 的隔離測試。它不會驗證 HTTP 中介層、Cookie 往返、跨行程的持久化,也不會驗證真實 Redis 的失效或鎖。請不要將 array 當作正式環境的共用儲存位置。 在公開自訂處理器之前,也請加入以下項目。
  • 未儲存或已過期的 ID 會回傳空字串
  • 儲存的字串能原樣還原,刪除後無法再取得
  • TTL 的單位與更新,以及真實後端中的失效
  • 讀取、寫入、刪除失敗時,會得到既定的回傳值或例外
  • 啟用 session.encrypt 時的往返,以及支援的序列化格式
  • 僅載入提供者不會存取儲存位置,也不會搶走內建驅動的選擇
  • 視需要加入經過真實 web 中介層與 Cookie 的 HTTP 測試,以及並行請求的鎖

相關頁面

Session

確認 Session 的讀寫、快閃資料與 ID 的重新產生。

Manager 的內部實作

確認自訂驅動的建立與保留,以及對預設驅動的委派。

套件的自訂快取儲存

確認作為儲存位置的快取 Store 與 Repository 的分工,以及 TTL 契約。

套件服務的生命週期

確認長時間執行的 worker 中所保留的相依性與狀態隔離。

第一手資料

最後修改於 2026年11月2日