Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【PHP8.x】session_set_save_handler()関数の使い方

session_set_save_handler関数の使い方について、初心者にもわかりやすく解説します。

作成日: 更新日:

基本的な使い方

session_set_save_handler関数は、PHPのセッションの保存と管理方法をカスタマイズするために使用される関数です。通常、PHPのセッションデータはWebサーバーのファイルシステムにファイルとして保存されますが、この関数を利用することで、セッションデータをデータベースやMemcached、Redisなどの外部ストレージに保存するよう、その処理を切り替えることができます。

具体的には、セッションの開始、終了、データの読み込み、データの書き込み、セッションの破棄、そして古いセッションデータの削除(ガベージコレクション)といった、セッションに関連する主要な操作を処理するための独自の関数群(またはクラスのメソッド)を登録します。これらのカスタムハンドラを登録することで、アプリケーションの特定の要件や、複数のWebサーバー間でセッション情報を共有する必要がある分散環境において、より柔軟で効率的なセッション管理を実現できます。

この関数は、session_start()関数が呼び出される前に設定する必要があります。引数には、オープン、クローズ、リード、ライト、デストロイ、ガベージコレクションの各操作に対応するコールバック関数を順番に指定します。成功した場合は真(true)、失敗した場合は偽(false)を返します。この機能は、大規模なシステムや高可用性が求められるシステムで、セッション管理の自由度を高めるために広く活用されています。

構文(syntax)

1<?php
2
3// SessionHandlerInterface を実装したオブジェクトを引数に指定します。
4// ここでは匿名クラスを使用してその場で実装していますが、通常のクラスとして定義することも可能です。
5$customSessionHandler = new class implements SessionHandlerInterface {
6    public function open(string $path, string $name): bool {
7        // セッションを開くための処理を記述します。
8        return true;
9    }
10
11    public function close(): bool {
12        // セッションを閉じるための処理を記述します。
13        return true;
14    }
15
16    public function read(string $id): string|false {
17        // 指定されたIDのセッションデータを読み込む処理を記述します。
18        // データが存在しない場合は空文字列を、エラーの場合は false を返します。
19        return '';
20    }
21
22    public function write(string $id, string $data): bool {
23        // 指定されたIDにセッションデータを書き込む処理を記述します。
24        return true;
25    }
26
27    public function destroy(string $id): bool {
28        // 指定されたIDのセッションデータを破棄する処理を記述します。
29        return true;
30    }
31
32    public function gc(int $max_lifetime): int|false {
33        // 有効期限切れのセッションデータを削除するガベージコレクション処理を記述します。
34        // 削除されたセッション数を整数で返します。
35        return 0;
36    }
37
38    // 必要に応じて、以下のPHP 7.0以降で追加されたオプションメソッドも実装できます。
39    // public function create_sid(): string { return ''; }
40    // public function validate_sid(string $id): bool { return true; }
41    // public function update_timestamp(string $id, string $data): bool { return true; }
42};
43
44// カスタムセッションハンドラを設定します。
45// 第二引数は、シャットダウン時にセッションハンドラを登録するかどうか (デフォルト true)。
46session_set_save_handler($customSessionHandler, true);

引数(parameters)

callable $open, callable $close, callable $read, callable $write, callable $destroy, callable $gc, ?callable $create_sid = null, ?callable $validate_sid = null, ?callable $update_timestamp = null

  • callable $open: セッションIDの読み込みまたは新規作成を行うハンドラー関数
  • callable $close: セッションのクローズ処理を行うハンドラー関数
  • callable $read: 指定されたセッションIDのデータを読み込むハンドラー関数
  • callable $write: 指定されたセッションIDにデータを書き込むハンドラー関数
  • callable $destroy: 指定されたセッションIDのセッションデータを破棄するハンドラー関数
  • callable $gc: セッションIDのガベージコレクション(不要なセッションの削除)を行うハンドラー関数
  • ?callable $create_sid: 新しいセッションIDを生成するハンドラー関数 (オプション)
  • ?callable $validate_sid: セッションIDの有効性を検証するハンドラー関数 (オプション)
  • ?callable $update_timestamp: セッションのタイムスタンプを更新するハンドラー関数 (オプション)

戻り値(return)

bool

セッション保存ハンドラーとして、指定されたコールバック関数が正常に設定されたかどうかを真偽値で返します。成功した場合は true、失敗した場合は false が返されます。

サンプルコード

PHP 8.4 session_set_save_handlerでカスタムセッションを実装する

1<?php
2declare(strict_types=1);
3
4/**
5 * PHPのカスタムセッションハンドラの基本例
6 *
7 * このコードは、`session_set_save_handler` を使用して、
8 * PHPのセッションデータをメモリ上に一時的に保存する方法を示します。
9 * システムエンジニアを目指す初心者の方へ:
10 * 通常、PHPはセッションデータをファイルに保存しますが、この機能を使うと、
11 * データベースやキャッシュシステム(例:Redis、Memcached)など、
12 * 好きな場所にセッションを保存できるようになります。
13 * 各メソッドは、セッションのライフサイクル(開始、読み込み、書き込み、終了など)
14 * の特定の段階でPHPによって呼び出されます。
15 */
16class MyCustomSessionHandler
17{
18    /**
19     * @var array<string, array{data: string, timestamp: int}>
20     * セッションデータをメモリ上に保持する配列。
21     * キーはセッションID、値はセッションデータと最終更新タイムスタンプの配列です。
22     */
23    private static array $sessionStorage = [];
24
25    /**
26     * セッションが開かれる際に呼び出されます。
27     * ここでデータベース接続の確立など、セッション保存に必要な初期化を行います。
28     */
29    public function open(string $path, string $name): bool
30    {
31        // メモリ上に保存するため、特別な初期化は不要
32        return true;
33    }
34
35    /**
36     * セッションが閉じられる際に呼び出されます。
37     * ここでデータベース接続のクローズなど、終了処理を行います。
38     */
39    public function close(): bool
40    {
41        // メモリ上に保存するため、特別なクリーンアップは不要
42        return true;
43    }
44
45    /**
46     * 指定されたセッションIDのデータを読み込みます。
47     */
48    public function read(string $id): string
49    {
50        if (isset(self::$sessionStorage[$id])) {
51            $session = self::$sessionStorage[$id];
52            // PHPのデフォルト設定 (session.gc_maxlifetime) を使用して有効期限をチェック
53            if ($session['timestamp'] + (int)ini_get('session.gc_maxlifetime') < time()) {
54                unset(self::$sessionStorage[$id]); // 期限切れセッションを削除
55                return '';
56            }
57            return $session['data'];
58        }
59        return '';
60    }
61
62    /**
63     * 指定されたセッションIDにデータを書き込みます。
64     */
65    public function write(string $id, string $data): bool
66    {
67        self::$sessionStorage[$id] = [
68            'data' => $data,
69            'timestamp' => time(), // 最終更新日時を記録
70        ];
71        return true;
72    }
73
74    /**
75     * 指定されたセッションIDのセッションを破棄します。
76     * `session_destroy()` が呼び出された際に使用されます。
77     */
78    public function destroy(string $id): bool
79    {
80        unset(self::$sessionStorage[$id]);
81        return true;
82    }
83
84    /**
85     * ガベージコレクション (GC) 処理。
86     * 古い(有効期限切れの)セッションデータを削除します。
87     * PHPの設定に基づき、不定期に呼び出されます。
88     */
89    public function gc(int $max_lifetime): bool
90    {
91        $currentTime = time();
92        foreach (self::$sessionStorage as $id => $session) {
93            if ($session['timestamp'] + $max_lifetime < $currentTime) {
94                unset(self::$sessionStorage[$id]);
95            }
96        }
97        return true;
98    }
99
100    /**
101     * (デバッグ用) 現在のセッションストレージの内容を取得します。
102     * このメソッドは、MyCustomSessionHandler の内部動作を確認するために使用します。
103     */
104    public static function getSessionStorageForDebug(): array
105    {
106        return self::$sessionStorage;
107    }
108}
109
110// -----------------------------------------------------------------------------
111// カスタムセッションハンドラの設定と使用例
112// -----------------------------------------------------------------------------
113
114// カスタムハンドラのインスタンスを作成
115$handler = new MyCustomSessionHandler();
116
117// `session_set_save_handler` を使用してカスタムハンドラを登録します。
118// PHP 8.4の引数リストに従い、必須の6つのcallableメソッドを指定します。
119session_set_save_handler(
120    [$handler, 'open'],    // セッション開始時に呼び出される
121    [$handler, 'close'],   // セッション終了時に呼び出される
122    [$handler, 'read'],    // セッションデータ読み込み時に呼び出される
123    [$handler, 'write'],   // セッションデータ書き込み時に呼び出される
124    [$handler, 'destroy'], // セッション破棄時に呼び出される
125    [$handler, 'gc']       // ガベージコレクション時に呼び出される
126    // (create_sid, validate_sid, update_timestamp は省略)
127);
128
129// PHPのセッション管理が、スクリプト終了時に正しくカスタムハンドラを呼び出すようにします。
130session_register_shutdown();
131
132// セッションを開始します。
133// これにより、`open()` や `read()` メソッドが呼び出され、`$_SESSION` が利用可能になります。
134session_start();
135
136echo "<h2>カスタムセッションハンドラの動作確認</h2>";
137
138// セッション変数の設定と取得の例
139if (!isset($_SESSION['visits'])) {
140    $_SESSION['visits'] = 1;
141    echo "<li>初めての訪問です!</li>";
142} else {
143    $_SESSION['visits']++;
144    echo "<li>あなたの訪問回数: " . $_SESSION['visits'] . "回目</li>";
145}
146
147$_SESSION['user_name'] = 'ゲストユーザー';
148echo "<li>セッションユーザー名: " . $_SESSION['user_name'] . "</li>";
149echo "<li>現在のセッションID: " . session_id() . "</li>";
150
151// 現在のセッションストレージの内容を表示 (デバッグ用)
152echo "<h3>セッションストレージの現在の状態 (デバッグ用):</h3>";
153echo "<pre>";
154print_r(MyCustomSessionHandler::getSessionStorageForDebug());
155echo "</pre>";
156
157// セッションを破棄するためのリンク
158echo "<h3>セッション操作</h3>";
159if (isset($_GET['destroy'])) {
160    session_destroy(); // セッションを破棄 (`destroy()` メソッドが呼び出されます)
161    echo "<li>セッションが破棄されました。ページをリロードすると新しいセッションが開始されます。</li>";
162    echo "<pre>セッション破棄後のストレージの状態:\n";
163    print_r(MyCustomSessionHandler::getSessionStorageForDebug());
164    echo "</pre>";
165} else {
166    $currentUrl = strtok($_SERVER['REQUEST_URI'], '?');
167    echo '<li><a href="?destroy=1">セッションを破棄する</a></li>';
168    echo '<li><a href="' . $currentUrl . '">ページをリロードする</a> (訪問回数が増えます)</li>';
169}
170
171// スクリプト終了時に `write()` および `close()` メソッドが自動的に呼び出されます。

session_set_save_handler関数は、PHPのセッションデータを保存・管理する方法をカスタマイズするための機能です。通常、PHPはセッションデータをサーバー上のファイルに保存しますが、この関数を使うと、データベースやキャッシュシステム(例:Redis)など、任意の場所にセッションを保存できるようになります。

このサンプルコードでは、MyCustomSessionHandlerというクラスを作成し、PHPのセッション管理に関する様々な処理をメモリ上で行う方法を示しています。session_set_save_handlerは、openclosereadwritedestroygcという6つのコールバック関数(またはメソッド)を引数として受け取ります。

  • open: セッションが開始される際に呼び出され、初期化処理を行います。
  • close: セッションが終了する際に呼び出され、後処理を行います。
  • read: セッションIDに対応するデータを読み込む際に呼び出されます。
  • write: セッションIDにデータを書き込む際に呼び出されます。
  • destroy: 指定されたセッションを破棄する際に呼び出されます。
  • gc (ガベージコレクション): 古くなったセッションデータを定期的に削除する際に呼び出されます。

これらの関数は、セッションのライフサイクル(開始、読み込み、書き込み、終了など)の特定の段階でPHPによって自動的に呼び出されます。関数はすべて処理の成功時にtrue、失敗時にfalseを返すbool型の戻り値を持ちます。サンプルコードの実行結果では、カスタムハンドラによってメモリ上に保存されたセッションデータが実際にどのように変化し、利用されるかを確認できます。これにより、PHPのセッション管理の仕組みをより深く理解し、柔軟なシステム構築に役立てることができます。

session_set_save_handlerは、PHPのセッションデータをファイル以外に保存するための機能です。引数には、セッションの開始から終了、読み書き、破棄、ガベージコレクションに対応する最低6つのメソッドを渡す必要があります。サンプルコードのメモリ保存は一時的であり、PHPプロセスが終了するとデータが失われるため、実運用ではデータベースやキャッシュシステムなど永続的なストレージの利用が必須です。また、session_register_shutdown()を呼び出すことで、スクリプト終了時にセッションデータが確実に保存されます。複数のリクエストによる同時アクセスを考慮し、セッションデータの排他制御(ロック)を適切に実装することがデータ整合性維持のために非常に重要です。セキュリティ面では、セッションIDの生成・検証やデータの暗号化も検討してください。

PHPカスタムセッションハンドラを実装する

1<?php
2
3/**
4 * カスタムセッションハンドラのサンプルクラス
5 *
6 * SessionHandlerInterface を実装することで、PHP の推奨されるオブジェクト指向の方法で
7 * セッションの保存、読み込み、削除などをカスタマイズします。
8 *
9 * session_set_save_handler() にコールバック関数を直接渡す方法は利用可能ですが、
10 * この SessionHandlerInterface を実装するアプローチが、現代のPHP開発では推奨されています。
11 * これは、より構造化されたコードと将来的な互換性のためにも良いプラクティスです。
12 */
13class MyCustomSessionHandler implements SessionHandlerInterface
14{
15    /**
16     * @var array 簡単なデモ用メモリ内ストレージ。実際のアプリケーションではデータベースやファイルを使用します。
17     */
18    private array $sessionData = [];
19
20    /**
21     * セッションを開く処理。
22     * セッションが開始される際に一度だけ呼び出されます。
23     * 通常はデータベース接続の確立やファイルハンドルのオープンなどを行います。
24     *
25     * @param string $path セッションの保存パス (通常は無視されます)
26     * @param string $name セッション名
27     * @return bool 成功した場合に true
28     */
29    public function open(string $path, string $name): bool
30    {
31        // echo "Session Opened: Path='$path', Name='$name'\n"; // デバッグ用
32        return true; // このデモでは常に成功とします
33    }
34
35    /**
36     * セッションを閉じる処理。
37     * セッションが終了する際に呼び出されます (例: スクリプト終了時、session_write_close() 呼び出し時)。
38     * 通常はデータベース接続のクローズやファイルハンドルの解放などを行います。
39     *
40     * @return bool 成功した場合に true
41     */
42    public function close(): bool
43    {
44        // echo "Session Closed\n"; // デバッグ用
45        return true; // このデモでは常に成功とします
46    }
47
48    /**
49     * セッションIDに基づいてデータを読み込む処理。
50     * セッションデータが必要なときに呼び出されます。
51     *
52     * @param string $id セッションID
53     * @return string 読み込んだセッションデータ (PHP が内部でシリアライズされた文字列)
54     */
55    public function read(string $id): string
56    {
57        // echo "Session Read: ID='$id'\n"; // デバッグ用
58        return $this->sessionData[$id] ?? ''; // データがなければ空文字列を返す
59    }
60
61    /**
62     * セッションIDとデータを保存する処理。
63     * セッションデータが変更されたり、セッションが終了する際に呼び出されます。
64     *
65     * @param string $id セッションID
66     * @param string $data 保存するセッションデータ (PHP が内部でシリアライズされた文字列)
67     * @return bool 成功した場合に true
68     */
69    public function write(string $id, string $data): bool
70    {
71        // echo "Session Write: ID='$id', Data='$data'\n"; // デバッグ用
72        $this->sessionData[$id] = $data; // デモ用ストレージに保存
73        return true;
74    }
75
76    /**
77     * セッションIDのデータを破棄する処理。
78     * session_destroy() が呼び出された際に呼び出されます。
79     *
80     * @param string $id 破棄するセッションID
81     * @return bool 成功した場合に true
82     */
83    public function destroy(string $id): bool
84    {
85        // echo "Session Destroy: ID='$id'\n"; // デバッグ用
86        unset($this->sessionData[$id]); // デモ用ストレージから削除
87        return true;
88    }
89
90    /**
91     * ガベージコレクション処理。期限切れのセッションデータを削除します。
92     * セッション開始時に一定の確率で呼び出されます。
93     *
94     * @param int $max_lifetime セッションの最大有効期間 (秒)
95     * @return bool 成功した場合に true
96     */
97    public function gc(int $max_lifetime): bool
98    {
99        // echo "Session GC: Max Lifetime='$max_lifetime's\n"; // デバッグ用
100        // このデモでは簡単なメモリ内ストレージなので、明示的なGC処理は省略します。
101        // 実際のアプリケーションでは、ここで期限切れのレコードを削除するロジックを実装します。
102        return true;
103    }
104
105    /**
106     * 新しいセッションIDを生成する処理 (PHP 7.0+)。
107     * このメソッドが実装されていない場合、PHPの標準のID生成メカニズムが使用されます。
108     *
109     * @return string 新しいセッションID
110     */
111    public function create_sid(): string
112    {
113        // echo "Session Create SID\n"; // デバッグ用
114        // PHP標準のセッションID生成機能を使用するか、独自のIDを生成します。
115        return session_create_id();
116    }
117
118    /**
119     * セッションIDが有効かどうかを検証する処理 (PHP 7.0+)。
120     *
121     * @param string $id 検証するセッションID
122     * @return bool 有効な場合に true
123     */
124    public function validate_sid(string $id): bool
125    {
126        // echo "Session Validate SID: ID='$id'\n"; // デバッグ用
127        // このデモでは、ストレージにIDが存在すれば有効とみなします。
128        return isset($this->sessionData[$id]);
129    }
130
131    /**
132     * セッションの最終アクセス時刻を更新する処理 (PHP 7.0+)。
133     * セッションがアクティブな間に、セッションの有効期限を延長するために呼び出されます。
134     *
135     * @param string $id セッションID
136     * @param string $data セッションデータ (PHP が内部でシリアライズされた文字列)
137     * @return bool 成功した場合に true
138     */
139    public function update_timestamp(string $id, string $data): bool
140    {
141        // echo "Session Update Timestamp: ID='$id'\n"; // デバッグ用
142        // このデモでは何もせず true を返します。
143        // 実際のアプリケーションでは、データベースのタイムスタンプカラムを更新するなどの処理を行います。
144        return true;
145    }
146}
147
148// -----------------------------------------------------------------------------
149// セッションハンドラの登録と利用
150// -----------------------------------------------------------------------------
151
152// カスタムハンドラのインスタンスを作成
153$handler = new MyCustomSessionHandler();
154
155// session_set_save_handler() に SessionHandlerInterface を実装したオブジェクトを渡す
156// これはPHP 5.4以降で推奨されるセッションハンドラ設定方法です。
157// 第二引数を true に設定すると、スクリプト終了時に session_write_close() が自動的に呼び出されます。
158session_set_save_handler($handler, true);
159
160// セッションを開始
161session_start();
162
163// セッションIDの表示
164echo "Current Session ID: " . session_id() . "\n";
165
166// セッションデータの設定と更新
167if (!isset($_SESSION['counter'])) {
168    $_SESSION['counter'] = 1;
169    echo "Initial counter: " . $_SESSION['counter'] . "\n";
170} else {
171    $_SESSION['counter']++;
172    echo "Counter incremented to: " . $_SESSION['counter'] . "\n";
173}
174
175// セッションデータを確認 (この例では MyCustomSessionHandler の内部状態にアクセスしています)
176// 実際のアプリケーションでは、通常は $_SESSION 変数を通じて操作します。
177// echo "Stored session data (raw): " . $handler->read(session_id()) . "\n";
178
179// 必要であればセッションを破棄 (この行のコメントを解除するとセッションが破棄されます)
180// session_destroy();
181// echo "Session destroyed.\n";
182// unset($_SESSION); // セッション変数も破棄
183
184// スクリプト終了時に session_write_close() が自動的に呼び出され、
185// write() および close() メソッドが実行されます。
186// もし session_set_save_handler の第2引数を false にした場合、
187// ここで明示的に session_write_close(); を呼び出す必要があります。
188
189?>

PHPのsession_set_save_handler関数は、ウェブアプリケーションでユーザーの状態を維持する「セッション」の保存方法をカスタマイズするために使用されます。通常、PHPはセッションデータをファイルに保存しますが、この関数を使うことで、データベースやキャッシュサーバーなど、独自のストレージにセッションを保存できるようになります。

この関数は引数として、セッションの開く、閉じる、読み込む、書き込む、破棄する、ガベージコレクションを実行するといった、各操作に対応するcallable(呼び出し可能な関数やメソッド)を受け取ります。登録が成功するとtrueを、失敗するとfalseを返します。

サンプルコードでは、現代のPHP開発で推奨されるSessionHandlerInterfaceを実装したMyCustomSessionHandlerクラスを用いてセッションをカスタマイズしています。このクラスはopenreadwriteclosedestroygcといったセッション管理に必要なメソッド群を定義しており、それぞれのメソッドが特定のセッション操作のタイミングでPHPから呼び出されます。例えば、readメソッドはセッションIDに基づいてデータを読み込み、writeメソッドはセッションIDとデータを保存します。オプションでcreate_sidvalidate_sidupdate_timestampも実装できます。

カスタムハンドラを有効にするには、MyCustomSessionHandlerのインスタンスを作成し、session_set_save_handler($handler, true)のように登録します。第二引数をtrueにすると、スクリプト終了時に自動的にセッションが書き込まれ、閉じられます。これにより、セッションの永続化ロジックをアプリケーションの要件に合わせて柔軟に制御することが可能になります。

PHPのsession_set_save_handlerは、セッションの保存方法をカスタマイズする関数です。将来のPHPバージョンでは、コールバック関数を直接渡す方法が非推奨となる可能性があるため、サンプルコードのようにSessionHandlerInterfaceを実装したオブジェクト指向のアプローチを強く推奨します。

サンプルコードはデモ用としてメモリ内でセッションを扱っていますが、実際のシステムではデータベースやファイルなど永続的なストレージを利用してセッションデータを安全に管理してください。各メソッドには、選定したストレージに応じた具体的な保存・読み込み・削除処理などを実装する必要があります。カスタムハンドラはsession_start()を呼び出す前に必ず登録してください。

PHPセッションをファイルに保存するサンプル

1<?php
2
3/**
4 * CustomFileSessionHandlerクラス
5 * PHPのセッションをファイルシステムに保存するためのカスタムハンドラを提供します。
6 * session_set_save_handler関数で使用するメソッドを実装しています。
7 */
8class CustomFileSessionHandler
9{
10    private string $savePath;
11
12    /**
13     * コンストラクタ
14     * セッションファイルを保存するディレクトリを設定し、存在しない場合は作成します。
15     *
16     * @param string $savePath セッションファイルを保存するディレクトリのパス
17     */
18    public function __construct(string $savePath)
19    {
20        $this->savePath = $savePath;
21        if (!is_dir($this->savePath)) {
22            mkdir($this->savePath, 0777, true);
23        }
24    }
25
26    /**
27     * セッションを開く際に呼び出されます。
28     * このメソッドは、セッションストレージの初期化や接続確立などを行います。
29     * ファイルベースのハンドラでは通常、特に何もする必要はありません。
30     *
31     * @param string $path php.iniのsession.save_pathで設定されたパス
32     * @param string $name セッション名
33     * @return bool 成功した場合true、失敗した場合false
34     */
35    public function open(string $path, string $name): bool
36    {
37        // 実際には、この$pathは使用せず、クラスで設定された$this->savePathを使用します。
38        return true;
39    }
40
41    /**
42     * セッションを閉じる際に呼び出されます。
43     * このメソッドは、セッションストレージの終了処理や接続解除などを行います。
44     * ファイルベースのハンドラでは通常、特に何もする必要はありません。
45     *
46     * @return bool 成功した場合true、失敗した場合false
47     */
48    public function close(): bool
49    {
50        return true;
51    }
52
53    /**
54     * セッションデータを読み込む際に呼び出されます。
55     * 指定されたセッションIDに対応するデータをストレージから読み込みます。
56     *
57     * @param string $id セッションID
58     * @return string セッションデータ (文字列)、またはデータがない場合は空文字列
59     */
60    public function read(string $id): string
61    {
62        $file = $this->getSessionFileName($id);
63        if (file_exists($file)) {
64            // ファイルの最終更新時刻とセッションの最大ライフタイムを比較し、
65            // 期限切れの場合はファイルを削除して空文字列を返します。
66            if (filemtime($file) + ini_get('session.gc_maxlifetime') < time()) {
67                unlink($file);
68                return '';
69            }
70            return (string) file_get_contents($file);
71        }
72        return '';
73    }
74
75    /**
76     * セッションデータを書き込む際に呼び出されます。
77     * 指定されたセッションIDとデータ文字列をストレージに書き込みます。
78     *
79     * @param string $id セッションID
80     * @param string $data 書き込むセッションデータ
81     * @return bool 成功した場合true、失敗した場合false
82     */
83    public function write(string $id, string $data): bool
84    {
85        $file = $this->getSessionFileName($id);
86        return (bool) file_put_contents($file, $data);
87    }
88
89    /**
90     * セッションを破棄する際に呼び出されます。
91     * 指定されたセッションIDに対応するデータをストレージから削除します。
92     *
93     * @param string $id 破棄するセッションID
94     * @return bool 成功した場合true、失敗した場合false
95     */
96    public function destroy(string $id): bool
97    {
98        $file = $this->getSessionFileName($id);
99        if (file_exists($file)) {
100            unlink($file);
101        }
102        return true;
103    }
104
105    /**
106     * ガベージコレクション (GC) を実行する際に呼び出されます。
107     * 古いセッションデータ (最大ライフタイムを超えたもの) を削除します。
108     *
109     * @param int $max_lifetime セッションの最大ライフタイム (秒)
110     * @return bool 成功した場合true、失敗した場合false
111     */
112    public function gc(int $max_lifetime): bool
113    {
114        foreach (glob($this->savePath . '/sess_*') as $file) {
115            if (filemtime($file) + $max_lifetime < time()) {
116                unlink($file);
117            }
118        }
119        return true;
120    }
121
122    /**
123     * ヘルパーメソッド: セッションファイル名を生成します。
124     *
125     * @param string $id セッションID
126     * @return string セッションファイルのフルパス
127     */
128    private function getSessionFileName(string $id): string
129    {
130        return $this->savePath . '/sess_' . $id;
131    }
132}
133
134// --- メインスクリプト ---
135
136// セッションファイルを保存するためのテンポラリディレクトリを設定します。
137// 環境に合わせて適切なパスに変更してください。
138$sessionSavePath = sys_get_temp_dir() . '/php_custom_sessions_example';
139
140// カスタムセッションハンドラをインスタンス化します。
141$handler = new CustomFileSessionHandler($sessionSavePath);
142
143// session_set_save_handler関数を使用して、PHPにカスタムセッションハンドラを登録します。
144// 各引数には、CustomFileSessionHandlerクラスの対応するメソッドへのcallableを指定します。
145// 最後の引数 'true' は、PHPのシャットダウン時にsession_write_close()が自動的に呼び出されるようにします。
146session_set_save_handler(
147    [$handler, 'open'],      // セッションを開く関数
148    [$handler, 'close'],     // セッションを閉じる関数
149    [$handler, 'read'],      // セッションデータを読み込む関数
150    [$handler, 'write'],     // セッションデータを書き込む関数
151    [$handler, 'destroy'],   // セッションを破棄する関数
152    [$handler, 'gc'],        // ガベージコレクションを行う関数
153    null,                    // オプション: create_sid callable
154    null,                    // オプション: validate_sid callable
155    null                     // オプション: update_timestamp callable
156);
157
158// カスタムハンドラが登録された状態でセッションを開始します。
159session_start();
160
161// --- セッションの動作をデモンストレーション ---
162
163// セッション変数 'counter' が設定されているか確認します。
164if (!isset($_SESSION['counter'])) {
165    // 初回アクセスの場合
166    $_SESSION['counter'] = 1;
167    echo "ようこそ!これはあなたの最初の訪問です。\n";
168} else {
169    // 2回目以降のアクセスの場合
170    $_SESSION['counter']++;
171    echo "あなたはこれまでこのページを " . $_SESSION['counter'] . " 回訪問しました。\n";
172}
173
174echo "セッションID: " . session_id() . "\n";
175echo "セッションデータは以下のディレクトリに保存されます: " . $sessionSavePath . "\n";
176
177// セッション破棄の動作を確認したい場合は、以下の行のコメントを解除してください。
178// その後、ページを複数回リロードし、再度コメントを解除した状態でリロードすると、
179// カウンターがリセットされるのが確認できます。
180// session_destroy();
181
182?>

PHPのsession_set_save_handler関数は、標準のセッション保存メカニズム(通常はファイル)を、開発者が定義した独自の処理に置き換えるための機能です。この関数を利用することで、セッションデータをファイルシステムだけでなく、データベースやキャッシュサーバーなど、様々な場所に保存できるようカスタマイズできます。

このサンプルコードでは、CustomFileSessionHandlerというクラスを定義し、セッションデータをファイルシステムに保存するための具体的なロジックを実装しています。このクラスは、セッションの開始時に呼ばれるopen、終了時に呼ばれるclose、データの読み込みを行うread、書き込みを行うwrite、セッションを削除するdestroy、そして古いセッションを定期的に削除するガベージコレクション(GC)を行うgcという、一連の必須メソッドを含んでいます。

session_set_save_handler関数は、これらCustomFileSessionHandlerクラスのメソッドを引数として受け取り、PHPのセッション処理に紐付けます。引数には、セッションを開く$open、閉じる$close、データを読み込む$read、書き込む$write、破棄する$destroy、古いセッションを削除する$gcという、それぞれセッション操作の特定の役割を担うcallable(呼び出し可能な関数やメソッド)を指定します。関数が正常にハンドラを登録できた場合、戻り値としてtrueが返されます。

カスタムハンドラが登録された後、session_start()関数を呼び出すことでセッションが開始され、それ以降の$_SESSION変数へのアクセスは、登録されたカスタムクラスのメソッドを通じて行われます。サンプルコードでは、訪問回数をカウントするセッション変数を使い、カスタムファイルハンドラが正しく動作し、セッションデータが指定されたディレクトリにsess_から始まるファイルとして保存される様子を示しています。

session_set_save_handlerは、PHPのセッションデータをファイル以外の場所(データベースやキャッシュサーバーなど)に保存したい場合に利用する関数です。サンプルコードはファイルベースのカスタムハンドラの例ですが、他のストレージへの応用も可能です。openclosereadwritedestroygcの6つのメソッドを正しく実装する必要があります。特にreadメソッドでの有効期限チェックとgcメソッドでの古いセッションデータ削除は、セッションの正常な機能と保存領域の管理に不可欠です。これらの実装が不適切だと、セッションが正しく動作しなかったり、ストレージが肥大化する原因となります。カスタムハンドラ登録後は、必ずsession_start()を呼び出してセッションを開始してください。実運用では、セッション保存パスのセキュリティやアクセス権限、堅牢なエラーハンドリングに十分配慮してください。

PHP 8 session_set_save_handler でカスタムセッションを実装する

1<?php
2
3/**
4 * カスタムセッションハンドラクラスの例
5 * ファイルシステムにセッションデータを保存します。
6 * このクラスはSessionHandlerInterfaceを実装しており、PHPの組み込みセッションハンドラを置き換えることができます。
7 */
8class MySessionHandler implements SessionHandlerInterface
9{
10    private string $savePath;
11
12    /**
13     * コンストラクタ
14     *
15     * @param string $savePath セッションファイルを保存するディレクトリパス。
16     *                         指定しない場合は、PHPのシステム一時ディレクトリ内に 'php_sessions' フォルダが作成されます。
17     */
18    public function __construct(string $savePath = '')
19    {
20        // セッション保存パスを設定。末尾にスラッシュを確実に付与します。
21        // デフォルトはシステムの一時ディレクトリ (sys_get_temp_dir()) の下に 'php_sessions' というフォルダを使用します。
22        $this->savePath = rtrim($savePath ?: sys_get_temp_dir(), '/') . '/php_sessions/';
23
24        // 保存パスが存在しない場合は作成を試みます。
25        if (!is_dir($this->savePath)) {
26            // trueを指定することで、必要に応じて中間ディレクトリも作成します。
27            // 0777はディレクトリのパーミッションですが、本番環境ではより制限されたパーミッション(例: 0755)が推奨されます。
28            mkdir($this->savePath, 0777, true);
29        }
30    }
31
32    /**
33     * セッションを開くときに呼び出されます。
34     * このメソッドは、セッションの開始時に一度だけ実行されます。
35     *
36     * @param string $path セッションの保存パス (php.iniのsession.save_pathの値)
37     * @param string $name セッション名 (php.iniのsession.nameの値)
38     * @return bool 成功時にtrueを返します。
39     */
40    public function open(string $path, string $name): bool
41    {
42        // このカスタムハンドラでは、コンストラクタで保存パスが既に設定されているため、
43        // openメソッドで受け取る$pathや$nameを直接使用する必要は通常ありません。
44        // データベース接続など、セッション利用開始時に必要な初期化処理をここで行うことができます。
45        return true;
46    }
47
48    /**
49     * セッションを閉じるときに呼び出されます。
50     * スクリプトの実行終了時、またはsession_write_close()が呼び出されたときに実行されます。
51     *
52     * @return bool 成功時にtrueを返します。
53     */
54    public function close(): bool
55    {
56        // データベース接続の切断など、セッション利用終了時に必要なリソース解放処理をここで行うことができます。
57        return true;
58    }
59
60    /**
61     * セッションデータを読み込みます。
62     *
63     * @param string $id セッションID
64     * @return string|false セッションIDに対応するデータ文字列、またはデータがない場合は空文字列を返します。
65     *                      読み込みに失敗した場合はfalseを返しますが、空文字列を返すのが一般的です。
66     */
67    public function read(string $id): string|false
68    {
69        $file = $this->getSessionFilename($id);
70        // ファイルが存在し、読み取り可能な場合のみ内容を返す
71        if (file_exists($file) && is_readable($file)) {
72            return (string) file_get_contents($file);
73        }
74        return ''; // データがない、または読み取り不可の場合は空文字列を返す
75    }
76
77    /**
78     * セッションデータを書き込みます。
79     *
80     * @param string $id セッションID
81     * @param string $data セッションデータ (シリアライズされた文字列)
82     * @return bool 成功時にtrueを返します。
83     */
84    public function write(string $id, string $data): bool
85    {
86        $file = $this->getSessionFilename($id);
87        // ファイルにデータを書き込みます。LOCK_EXフラグは、他のプロセスからの同時書き込みを防ぎます。
88        return (bool) file_put_contents($file, $data, LOCK_EX);
89    }
90
91    /**
92     * セッションを破棄します。
93     * session_destroy() が呼び出されたときに実行されます。
94     *
95     * @param string $id 破棄するセッションのID
96     * @return bool 成功時にtrueを返します。
97     */
98    public function destroy(string $id): bool
99    {
100        $file = $this->getSessionFilename($id);
101        // ファイルが存在する場合のみ削除を試みる
102        if (file_exists($file)) {
103            return unlink($file);
104        }
105        return true; // ファイルが存在しない場合は、既に破棄されていると見なしてtrueを返す
106    }
107
108    /**
109     * ガベージコレクション (GC) を実行します。
110     * 古いセッションファイルを削除します。
111     *
112     * @param int $max_lifetime セッションの最大有効期限 (秒)。php.iniのsession.gc_maxlifetimeで設定されます。
113     * @return int|false 削除されたセッションファイルの数、または失敗時にfalseを返します。
114     */
115    public function gc(int $max_lifetime): int|false
116    {
117        $count = 0;
118        // セッション保存ディレクトリ内のすべてのセッションファイルを取得
119        foreach (glob($this->savePath . 'sess_*') as $file) {
120            // ファイルの最終更新時刻と$max_lifetimeを比較して、期限切れかどうかを判断
121            // file_exists($file) は、ループ中に別のプロセスによってファイルが削除された場合に備えてチェック
122            if (filemtime($file) + $max_lifetime < time() && file_exists($file)) {
123                @unlink($file); // 期限切れファイルを削除。@でエラーを抑制し、削除失敗しても続行。
124                $count++;
125            }
126        }
127        return $count;
128    }
129
130    /**
131     * セッションファイル名を生成するヘルパーメソッド
132     *
133     * @param string $id セッションID
134     * @return string セッションファイルの完全なパス
135     */
136    private function getSessionFilename(string $id): string
137    {
138        return $this->savePath . 'sess_' . $id;
139    }
140}
141
142// --- サンプルコードの実行部分 ---
143
144// カスタムセッションハンドラのインスタンスを作成します。
145// コンストラクタに保存パスを明示的に渡さない場合、上記MySessionHandlerクラスのデフォルトパスが使用されます。
146$handler = new MySessionHandler();
147
148// session_set_save_handler() を使ってカスタムハンドラを登録します。
149// SessionHandlerInterfaceを実装したオブジェクトを渡す場合、第二引数 ($register_shutdown) は
150// 通常trueを推奨します。これにより、スクリプト終了時に自動的にclose()メソッドが呼び出され、
151// セッションの保存が保証されます。
152session_set_save_handler($handler, true);
153
154// PHPのセッション管理設定の例(必要に応じて、ini_setで設定値を変更できます)
155// ini_set('session.gc_probability', 1);   // ガベージコレクション実行の確率を1%に設定 (1/100)
156// ini_set('session.gc_divisor', 100);     // ガベージコレクション実行の分母を100に設定
157// ini_set('session.gc_maxlifetime', 1440); // セッションの有効期限を1440秒(24分)に設定
158
159// セッションを開始します。
160// これにより、open(), read(), write(), close()などのセッションハンドラメソッドがPHPによって適切なタイミングで呼び出されます。
161session_start();
162
163// セッション変数の操作例
164if (!isset($_SESSION['count'])) {
165    $_SESSION['count'] = 0;
166} else {
167    $_SESSION['count']++;
168}
169
170echo "現在のセッションカウント: " . $_SESSION['count'] . "\n";
171echo "現在のセッションID: " . session_id() . "\n";
172echo "セッションデータは " . $handler->savePath . " ディレクトリに保存されます。\n";
173echo "このページをリロードするとカウントが増加します。\n\n";
174
175// セッションを破棄する例(このスクリプトを複数回実行して動作を確認した後、必要に応じてコメント解除して実行)
176// session_destroy();
177// echo "セッションが破棄されました。リロードしてもカウントはリセットされます。\n";
178
179// スクリプトの終了時にセッションデータが自動的に保存され、close()が呼び出されます。
180?>

PHPのsession_set_save_handler関数は、セッションの保存や読み込みといった標準的な処理を、開発者が定義した独自の仕組みに置き換えるための機能です。この関数を使うことで、例えばセッションデータをファイルではなくデータベースに保存するなど、セッション管理の方法を自由にカスタマイズできます。

引数には、セッションの「開始」「終了」「読み込み」「書き込み」「破棄」「ガベージコレクション(古いセッションの削除)」といった各操作を処理するコールバック関数、またはSessionHandlerInterfaceを実装したオブジェクトを渡します。サンプルコードでは、MySessionHandlerクラスがこのインターフェースを実装しており、ファイルシステムにセッションデータを保存する具体的な処理を定義しています。

session_set_save_handler($handler, true)と記述することで、作成したカスタムハンドラのインスタンスをPHPに登録します。これにより、その後のsession_start()などのセッション関連の関数が呼び出された際に、MySessionHandlerクラスで定義された各メソッド(open(), read(), write()など)が自動的に実行されるようになります。

サンプルコードの実行部分では、カスタムハンドラを登録した後にsession_start()を呼び出し、$_SESSION変数を使ってセッションデータを操作しています。これにより、セッションデータがMySessionHandlerクラスのロジックに従って、指定されたディレクトリのファイルとして保存・管理されることが確認できます。この関数は、カスタムハンドラの登録が成功した際にtrueを返します。

このサンプルコードは、PHPのセッション保存方法をファイルシステムにカスタマイズする一例です。session_set_save_handler関数は、セッションの開始、読み書き、破棄といった一連の処理を独自に定義する際に利用します。提供されたカスタムハンドラクラスはSessionHandlerInterfaceを実装しており、PHPがセッション操作時に対応するメソッドを自動で呼び出す仕組みになっています。重要な注意点として、mkdirで指定している0777のファイルパーミッションは開発環境向けです。本番環境では、セキュリティ上の理由から、より制限されたパーミッション(例: 0755)を必ず設定してください。また、session_set_save_handlerの呼び出しは、session_start()関数よりも前に行う必要があります。セッションデータには機密性の高い情報が含まれることがあるため、保存先ディレクトリのアクセス権限を厳重に管理し、ガベージコレクションによって期限切れデータを確実に削除することが非常に重要です。

関連コンテンツ