分析 Session(1)
今天要講的是與預設 middleware 相關的另一個元件--Session。這個元件應該是到目前為止,最多類別的元件。
在看類別圖之前,我們先從 SessionServiceProvider
了解裡面有哪些相關的類別需要初始化:
原本是分不同的方法各別呼叫 singleton,這裡刻意使用依序呼叫的方法來呈現。
$this->app->singleton('session', function ($app) { |
從這裡可以知道,SessionManager
是最主要的核心類別,StartSession
則是 middleware。
類別圖
為了版面簡潔,Illuminate\Session
開頭的類別,都有忽略。除非是跟其他元件有關聯。另外為了表示這元件的內聚關係,有忽略一些靜態呼叫的類別,如 Illuminate\Support\Arr
類別。
@startuml |
從這張圖可以了解類別間關係,比方說:
SessionManager
是管理 driver 的實作,它繼承了Illuminate\Support\Manager
Illuminate\Contracts\Session\Session
是管資料存放在記憶體的介面SessionHandlerInterface
是管資料存放在持久化空間的介面CacheBasedSessionHandler
可以接多種實作,只要符合Illuminate\Contracts\Cache\Repository
SessionManager
雖然有很多關係連結,不過就先從 SessionManager 類別開始吧!SessionManager 繼承了 Illuminate\Support\Manager
。
Manager 的用途很特別,通常我們在不同的場景會使用不同實作時,如 DB 在測試會使用 SQLite,上線會使用 MySQL,這時為符合 open-closed principle,通常我們會選擇 strategy pattern。Manager 就是一個管理 strategy 實例的抽象類。
一開始初始化,當然會需要 Container,因為後面要產生實例的時候會需要它。而今天一開始在講 service provider 時,有看到這段程式碼:
$this->app->singleton('session.store', function ($app) { |
這是取得實例的實作,來看看 Manager::driver()
:
public function driver($driver = null) |
從這段程式碼和 session.store
的建構方法可以知道,SessionManager 只會使用 default driver 而已(也就是 $driver
恆為 null)。Manager 定義 getDefaultDriver()
是抽象方法,我們先來看 SessionManager 是如何實作的:
public function getDefaultDriver() |
這就是 Laravel config/session.php
的 driver 設定。從這個設定檔的註解也可以知道,driver 有非常多種,如 file
、database
、memcached
等,這也剛好對應類別圖的 SessionHandlerInterface
實作。
如果設定是 file 的話,driver 又是怎麼被建構出來的呢,接著繼續看 createDriver()
:
protected function createDriver($driver) |
這裡的 createFileDriver
或是其他 driver 會是由子類別實作了,來繼續看 file driver 怎麼做的。
protected function createFileDriver() |
這裡的 $this->app['files']
是 Illuminate\Filesystem\Filesystem
實例,而 session.files
與 session.lifetime
則是設定。
其他 handler 的分析方法也是依此類推,就先略過了。
FileSessionHandler
實作了 SessionHandlerInterface
,這也是 PHP 提供的介面,搭配 session_set_save_handler()
可以用在自定義內建 session 的實作,也就是 $_SESSION
實際背後會處理細節,是可以自定義的。
換句話說,Laravel 所實作的五個 handler 不僅可以用在 SessionManager 上,也可以用在 PHP 內建的 $_SESSION
上,會這樣設計是有原因的,後面會再提到。
這五個實作都有一個共同特色,就是下面這兩個方法都是回傳 true
:
public function open($savePath, $sessionName) |
其他實作則依不同的 handler 有不同的存取方法,如 CacheBasedSessionHandler
就很好理解:
public function read($sessionId) |
既然一開始有提到 SessionManager 不處理資料,實際處理資料的是 Store,或者說是 Illuminate\Contracts\Session\Session
,Laravel 又是如何使用它的呢?從類別圖與上面的程式碼分析可以猜得出來,Laravel 自幹了 Session 處理機制,但它又是如何知道進來的 request 是來自同一個 client 呢?
這些秘密就在 StartSession
這個 middleware 裡,明天再繼續分析。