イントロダクション
HTTPで駆動するアプリケーションはステートレスであるため、セッションで複数のリクエストにわたりユーザーに関する情報を保存する手段を提供しています。そうしたユーザー情報は、後続のリクエストからアクセスできる永続的な保存/バックエンドに通常配置されます。
Laravelには、表現力豊かで統一されたAPIを介してアクセスできるさまざまなセッションバックエンドを用意しています。Memcached、Redis、データベースなどの一般的なバックエンドをサポートしています。
設定
アプリケーションのセッション設定ファイルはconfig/session.php
に保存されています。このファイルで利用可能なオプションを必ず確認してください。デフォルトでは、Laravelはfile
セッションドライバを使用するように設定しています。これは多くのアプリケーションでうまく機能します。アプリケーションが複数のWebサーバ間で負荷分散される場合は、Redisやデータベースなど、すべてのサーバがアクセスできる集中型保存領域を選択する必要があります。
セッションのdriver
設定オプションは、各リクエストのセッションデータが保存される場所を定義します。Laravelは、すぐに使える優れたドライバを最初からいくつか用意しています。
file
- セッションをstorage/framework/sessions
に保存しますcookie
- セッションを暗号化され安全なクッキーに保存しますdatabase
- セッションをリレーショナルデータベースへ保存しますmemcached
/redis
- セッションをこれらの高速なキャッシュベースの保存域へ保存しますdynamodb
- セッションをAWS DynamoDBへ保存しますarray
- セッションをPHP配列に格納し、永続化しません
Note: 配列(array)ドライバは主にテスト中に使用し、セッションに保存したデータが永続化されるのを防ぎます。
ドライバの動作要件
データベース
database
セッションドライバを使用する場合、セッションレコードを含むテーブルを作成する必要があります。テーブルのSchema
宣言の例を以下に示します。
Schema::create('sessions', function ($table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity')->index();
});
session:table
Artisanコマンドを使用してこのマイグレーションを生成できます。データベースのマイグレーションの詳細は、完全なマイグレーションドキュメントを参照してください。
php artisan session:table
php artisan migrate
Redis
LaravelでRedisセッションを使用する前に、PECLを介してPhpRedis
PHP拡張機能をインストールするか、Composerを介してpredis/predis
パッケージ(〜1.0)をインストールする必要があります。Redisの設定の詳細は、LaravelのRedisドキュメントを参照してください。
Note:
session
設定ファイルで、connection
オプションを使用し、セッションで使用するRedis接続を指定できます。
セッションの操作
データの取得
Laravelでセッションデータを操作する主な方法は、グローバルなsession
ヘルパとRequest
インスタンスの2つあります。最初に、Request
インスタンスを介してセッションにアクセスする方法を見てみましょう。これはルートクロージャまたはコントローラメソッドでタイプヒントを使い取得できます。コントローラメソッドの依存関係は、Laravelサービスコンテナを介して自動的に依存注入されることに注意してください。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* 特定のユーザーのプロファイルを表示
*
* @param Request $request
* @param int $id
* @return Response
*/
public function show(Request $request, $id)
{
$value = $request->session()->get('key');
//
}
}
セッションからアイテムを取得するときに、get
メソッドの2番目の引数としてデフォルト値を渡すこともできます。指定したキーがセッションに存在しない場合、このデフォルト値を返します。クロージャをデフォルト値としてget
メソッドに渡すと、リクエストされたキーが存在しない場合にそのクロージャを実行し、その実行結果を返します。
$value = $request->session()->get('key', 'default');
$value = $request->session()->get('key', function () {
return 'default';
});
グローバルセッションヘルパ
グローバルなsession
PHP関数を使用して、セッション内のデータを取得/保存することもできます。session
ヘルパを文字列引数一つで呼び出すと、そのセッションキーの値を返します。キー/値ペアの配列を使用してヘルパを呼び出すと、それらの値をセッションへ保存します。
Route::get('/home', function () {
// セッションからデータを取得
$value = session('key');
// デフォルト値の指定
$value = session('key', 'default');
// セッションにデータを保存
session(['key' => 'value']);
});
Note: HTTPリクエストインスタンスを介してセッションを使用する場合と、グローバルな
session
ヘルパを使用する場合の、実践的な違いはほとんどありません。どちらのメソッドも、すべてのテストケースで使用できるassertSessionHas
メソッドを使用し、テスト可能です。
全セッションデータの取得
セッション内のすべてのデータを取得する場合は、all
メソッドを使用します。
$data = $request->session()->all();
アイテムのセッション存在判定
アイテムがセッションに存在するかを判定するには、has
メソッドを使用します。アイテムが存在し、null
でない場合、has
メソッドはtrue
を返します。
if ($request->session()->has('users')) {
//
}
値がnull
でも、そのアイテムがセッションに存在するかを判定する場合には、exists
メソッドを使用します。
if ($request->session()->exists('users')) {
//
}
アイテムがセッションに存在しないことを判定するには、missing
メソッドを使用します。そのアイテムが存在しない場合、missing
メソッドはtrue
を返します。
if ($request->session()->missing('users')) {
//
}
データの保存
セッションにデータを保存するには、通常、リクエストインスタンスのput
メソッドまたはsession
グローバルヘルパを使用します。
// リクエストインスタンス経由
$request->session()->put('key', 'value');
// グローバルな"session"ヘルパ経由
session(['key' => 'value']);
配列セッション値への追加
push
メソッドを使用して、配列のセッション値へ新しい値を追加できます。たとえば、user.teams
キーにチーム名の配列が含まれている場合、次のように新しい値を配列に追加できます。
$request->session()->push('user.teams', 'developers');
アイテムの取得と削除
pull
メソッドは、単一のステートメントでセッションからアイテムを取得および削除します。
$value = $request->session()->pull('key', 'default');
セッション値の増分と減分
セッションデータが増分や減分をしたい整数の場合は、increment
メソッドとdecrement
メソッドを使えます。
$request->session()->('count');
$request->session()->increment('count', $incrementBy = 2);
$request->session()->decrement('count');
$request->session()->decrement('count', $decrementBy = 2);
データの一時保存
後続のリクエストで使用するために、セッションにアイテムを一時保存したい場合があります。flash
メソッドを使い実現できます。このメソッドを使用してセッションに保存されたデータは、即時および後続のHTTPリクエスト中に利用可能です。後続のHTTPリクエストの後、一時保存したデータを削除します。一時保存データは、主に持続保存の必要がないステータスメッセージに役立ちます。
$request->session()->flash('status', 'Task was successful!');
複数のリクエストの間、一時保存データを保持する必要がある場合は、reflash
メソッドを使用します。これにより、後続のリクエストのためすべての一時保存データを保持します。特定の一時保存データのみを保持する必要がある場合は、keep
メソッドを使用します。
$request->session()->reflash();
$request->session()->keep(['username', 'email']);
一時保存データを現在のリクエストに対してのみ持続するには、now
メソッドを使用します。
$request->session()->now('status', 'Task was successful!');
データの削除
forget
メソッドは、セッションからデータの一部を削除します。セッションからすべてのデータを削除したい場合は、flush
メソッドを使用できます。
// 一つのキーを削除
$request->session()->forget('name');
// 複数のキーを削除
$request->session()->forget(['name', 'status']);
$request->session()->flush();
セッションIDの再生成
多くの場合、セッションIDの再生成は、悪意のあるユーザーがアプリケーションに対するセッション固定攻撃を防ぐため行います。
LaravelアプリケーションスターターキットまたはLaravel Fortifyのどちらかを使用している場合、Laravelは認証中にセッションIDを自動的に再生成します。しかし、セッションIDを手作業で再生成する必要がある場合は、regenerate
メソッドを使用できます。
$request->session()->regenerate();
セッションIDを再生成してセッションからすべてのデータを一文で削除する必要がある場合は、invalidate
メソッドを使用します。
$request->session()->invalidate();
セッションブロッキング
Warning!! セッションブロッキングを利用するには、アプリケーションでアトミックロックをサポートするキャッシュドライバを使用している必要があります。現在、これらのキャッシュドライバには、
memcached
、dynamodb
、redis
、およびdatabase
ドライバをサポートしています。また、cookie
セッションドライバを使用することはできません。
デフォルトでは、Laravelは同じセッションを使用するリクエストを同時に実行することを許可します。したがって、たとえば、JavaScript HTTPライブラリを使用してアプリケーションへ2つのHTTPリクエストを作成すると、両方が同時に実行されます。多くのアプリケーションでは、これは問題ではありません。ただし、セッションデータの損失が、両方がセッションへデータを書き込む2つの異なるアプリケーションエンドポイントに同時にリクエストを行うアプリケーションの小さなサブセットで発生する可能性があります。
これを軽減するために、Laravelは特定のセッションの同時リクエストを制限できる機能を提供します。これを使用するには、block
メソッドをルート定義にチェーンするだけです。この例では、/profile
エンドポイントへの受信リクエストがセッションロックを取得します。このロックが保持されている間、同じセッションIDを共有する/profile
または/order
エンドポイントへの受信リクエストは、実行を続行する前に最初のリクエストの実行が終了するのを待ちます。
Route::post('/profile', function () {
//
})->block($lockSeconds = 10, $waitSeconds = 10)
Route::post('/order', function () {
//
})->block($lockSeconds = 10, $waitSeconds = 10)
block
メソッドは2つのオプションの引数を取ります。block
メソッドの最初の引数は、セッションロックを解放するまでに保持する必要がある最大秒数です。もちろん、この時間より前にリクエストの実行が終了すれば、ロックはより早く解放されます。
block
メソッドの2番目の引数は、セッションロックを取得しようとしているときにリクエストが待機する秒数です。リクエストが指定された秒数以内にセッションロックを取得できない場合、Illuminate\Contracts\Cache\LockTimeoutException
を投げます。
これらの引数のいずれも渡されない場合、ロックは最大10秒間取得され、リクエストはロックの取得を試行する間、最大10秒間待機します。
Route::post('/profile', function () {
//
})->block()
カスタムセッションドライバの追加
ドライバの実装
既存のセッションドライバがアプリケーションのニーズに合わない場合、Laravelでは独自のセッションハンドラが作成できます。カスタムセッションドライバは、PHPの組み込みのSessionHandlerInterface
を実装する必要があります。このインターフェイスには、いくつかの簡単なメソッドが含まれています。スタブ化されたMongoDBの実装は次のようになります。
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
Note: Laravelには、拡張機能を格納するためのディレクトリはありません。好きな場所に自由に配置できます。この例では、
MongoSessionHandler
を格納するためにExtensions
ディレクトリを作成しました。
これらのメソッドの目的は簡単には理解しずらいため、各メソッドの機能について簡単に説明します。
open
メソッドは通常、ファイルベースのセッションストアシステムで使用します。Laravelにはfile
セッションドライバが付属しているため、このメソッドに何も入れる必要があることはまれです。このメソッドは空のままにしておくことができます。open
メソッドと同様に、close
メソッドも通常は無視できます。ほとんどのドライバにとって、それは必要ありません。read
メソッドは、指定された$sessionId
に関連付いたセッションデータの文字列バージョンを返す必要があります。Laravelがシリアル化を実行するため、ドライバでセッションデータを取得または保存するときに、シリアル化やその他のエンコードを行う必要はありません。write
メソッドは、$sessionId
に関連付いた、指定$data
文字列を、MongoDBや選択した別のストレージシステムなどの永続ストレージシステムに書き込む必要があります。繰り返しになりますが、シリアル化を実行しないでください。Laravelがすでにそれを処理しています。destroy
メソッドは、永続ストレージから$sessionId
に関連付いたデータを削除する必要があります。gc
メソッドは、指定$lifetime
(UNIXタイムスタンプ)よりも古いすべてのセッションデータを破棄する必要があります。MemcachedやRedisなどの自己期限切れシステムの場合、このメソッドは空のままにしておくことができます。
ドライバの登録
ドライバを実装したら、Laravelへ登録する準備が済みました。Laravelのセッションバックエンドへドライバを追加するには、Session
ファサードが提供するextend
メソッドを使用します。サービスプロバイダのboot
メソッドからextend
メソッドを呼び出す必要があります。これは、既存のApp\Providers\AppServiceProvider
から行うか、もしくはまったく新しいプロバイダを作成することもできます。
<?php
namespace App\Providers;
use App\Extensions\MongoSessionHandler;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\ServiceProvider;
class SessionServiceProvider extends ServiceProvider
{
/**
* 全アプリケーションサービスの登録
*
* @return void
*/
public function register()
{
//
}
/**
* アプリケーションの全サービスの初期起動処理
*
* @return void
*/
public function boot()
{
Session::extend('mongo', function ($app) {
// SessionHandlerInterfaceの実装を返す
return new MongoSessionHandler;
});
}
}
セッションドライバを登録したら、config/session.php
設定ファイルでmongo
ドライバを使用できます。