v13.39.0 の公式ソースを確認しています。基本操作はセッション、プロバイダーの自動読み込みはパッケージの自動検出を参照してください。
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_start() や session_set_save_handler() を呼ぶ必要はありません。
保存契約を分けて考える
独自バックエンドを実装する場合は、次の役割を満たします。Store がシリアライズと復元を担当するため、ハンドラーでセッション配列を再度 serialize() や json_encode() しません。暗号化を有効にした場合も、EncryptedStore が処理した文字列をそのまま保存します。
bootで生成処理だけを登録する
次は、組み込みのCacheBasedSessionHandler を再利用する最小例です。新しいストレージプロトコルを実装するものではなく、名前付きキャッシュストアを使う独自ドライバーの登録方法を示します。Acme\SessionBridge\ がComposerのPSR-4で src/ に対応する構成です。
src/SessionBridgeServiceProvider.php にプロバイダーを用意します。
Session::extend() はSessionManagerを取得して生成処理を登録しますが、この時点でコールバック内のキャッシュストアは解決しません。保存先の生成は、そのドライバーが初めて選ばれた時点です。プロバイダーの boot() で接続確認やデータ保存を実行しない構成にします。
独自の SessionHandlerInterface 実装を使う場合は、コールバックが返すハンドラーを差し替えます。new Store(...) まで行うと、SessionManagerが期待する戻り値と合わなくなります。
- プロバイダーは通常の非遅延プロバイダーとして読み込みます。
- セッションが初めて使われるより前に登録を完了させます。別プロバイダーが先に独自ドライバーを解決する場合は読み込み順序も確認します。
- ドライバー名にはパッケージ固有の名前を使います。同名の
extend()は生成処理を上書きし、組み込みドライバー名を登録すれば組み込み実装より優先されます。
利用アプリケーションが明示的に選択する
利用者は既存のconfig/session.php の対応キーを変更します。ほかの設定は残します。
config/cache.php の既存の stores 配列へ名前付きストアを追加します。次はRedis接続 default を使う例です。Redisの接続設定と必要なクライアントはアプリケーション側で準備します。
createRedisDriver() を通らないため、session.connection や session.prefix を追加しても、このコールバックが自動で反映するわけではありません。
このコールバックはRepositoryや保存先の設定を変更せず、名前付きストアをそのまま利用します。テスト用の array ストアも、同じアプリケーション内では同じ保存領域を共有します。
パッケージから session.driver・Cookie名・アプリケーション全体のキャッシュプレフィックスを無条件に書き換えないようにします。ほかの用途と保存領域を共有しない設計や、複数サーバーから同じ保存先を使える構成も利用者と確認します。
TTLの単位と保存形式を維持する
CacheBasedSessionHandler のコンストラクターは、保存期間を分で受け取ります。write() は毎回 $minutes * 60 をキャッシュRepositoryの put() へ渡すため、上の例では書き込みのたびにTTLを更新します。gc() は 0 を返し、失効はキャッシュ側へ委ねます。
一方、StartSession がハンドラーの gc($lifetime) へ渡す期間は秒です。session.lifetime の値をそのまま秒として保存する実装にしないようにします。
また、次の変更は設定名の変更だけでは済みません。
パッケージ更新でこれらを変える場合は、再認証の案内、移行処理の有無、旧版と新版が混在するデプロイを検討します。パッケージのバージョン互換性管理と同じく、保存形式と設定キーも保守する公開仕様に含めます。
生成済みStoreは設定変更だけでは置き換わらない
SessionManagerの親クラスであるManager は、生成したドライバーを名前ごとに保持します。さらに、SessionServiceProvider は session.store をsingletonとして登録します。
forgetDrivers() は、セッションデータを削除するAPIでもありません。稼働中リクエストでStoreを作り直すと、すでに読み込んだデータや別の参照との不整合を招きます。
この例のCacheManagerも名前付きストアを保持します。その接続設定を変更した場合は、SessionManagerだけの再解決では不十分です。
設定キャッシュを使うデプロイではキャッシュを再構築し、長寿命ワーカーも新しいコードと設定を読み込むように再起動します。アプリケーションの再起動と保存済みデータの移行は別の作業です。
セッション保存とリクエストのロックは別
カスタムハンドラーを実装しただけでは、同一セッションへの並列リクエストは直列化されません。StartSession のブロッキングは、session.block_store で選ぶキャッシュストアのロックを利用します。セッション本体の保存先と同じとは限りません。
Route::post(...)->block() などで制御する場合は、ロック対応のキャッシュストア、複数サーバーでの共有、待機時間と保持時間を別途検証します。パッケージがアプリケーション全体のブロッキング設定を勝手に有効化しないようにします。
保存・再読み込みまでテストする
Orchestra Testbenchを使うパッケージでは、プロバイダーを読み込み、独自ドライバーを実際に選択したテストを用意します。次はtests/SessionDriverTest.php の例です。テスト用の array キャッシュは外部接続を使いません。
array を本番の共有保存先にしないようにします。
独自ハンドラーを公開する前には、次も追加します。
- 未保存・期限切れのIDが空文字列を返すこと
- 保存した文字列を変更せず復元でき、削除後には再取得できないこと
- TTLの単位と更新、実バックエンドでの期限切れ
- 読み込み・書き込み・削除の失敗時に、定めた戻り値や例外になること
session.encryptを有効にした往復と、サポートするシリアライズ形式- プロバイダーの読み込みだけでは保存先へアクセスせず、組み込みドライバーの選択も奪わないこと
- 必要なら、実際の
webミドルウェアとCookieを通すHTTPテスト、および並列リクエストのロック
関連ページ
セッション
セッションの読み書き、フラッシュデータ、IDの再生成を確認します。
Managerの内部実装
カスタムドライバーの生成と保持、デフォルトドライバーへの委譲を確認します。
パッケージのカスタムキャッシュストア
保存先となるキャッシュStoreとRepositoryの分担、TTLの契約を確認します。
パッケージサービスのライフサイクル
長寿命ワーカーで保持される依存関係と状態の分離を確認します。
一次情報
- 公式HTTP Sessionドキュメント: カスタムドライバーとブロッキング
- 公式パッケージ開発ドキュメント
- SessionManager: カスタム生成処理とStoreの構築
- Store: 読み込み・シリアライズ・保存・IDの再生成
- CacheBasedSessionHandler: TTLの変換と保存契約
- CacheManager: 名前付きストアとRedis設定
- Manager: extend・driver・forgetDrivers
- SessionServiceProvider: Managerとsession.storeのsingleton登録
- StartSession: 保存処理・GC・キャッシュによるロック
- 公式CacheBasedSessionHandlerTest: 未保存・TTL・削除の検証