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

作成日: 更新日:

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

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

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

基本的な使い方

構文(syntax)

<?php

// SessionHandlerInterface を実装したオブジェクトを引数に指定します。
// ここでは匿名クラスを使用してその場で実装していますが、通常のクラスとして定義することも可能です。
$customSessionHandler = new class implements SessionHandlerInterface {
    public function open(string $path, string $name): bool {
        // セッションを開くための処理を記述します。
        return true;
    }

    public function close(): bool {
        // セッションを閉じるための処理を記述します。
        return true;
    }

    public function read(string $id): string|false {
        // 指定されたIDのセッションデータを読み込む処理を記述します。
        // データが存在しない場合は空文字列を、エラーの場合は false を返します。
        return '';
    }

    public function write(string $id, string $data): bool {
        // 指定されたIDにセッションデータを書き込む処理を記述します。
        return true;
    }

    public function destroy(string $id): bool {
        // 指定されたIDのセッションデータを破棄する処理を記述します。
        return true;
    }

    public function gc(int $max_lifetime): int|false {
        // 有効期限切れのセッションデータを削除するガベージコレクション処理を記述します。
        // 削除されたセッション数を整数で返します。
        return 0;
    }

    // 必要に応じて、以下のPHP 7.0以降で追加されたオプションメソッドも実装できます。
    // public function create_sid(): string { return ''; }
    // public function validate_sid(string $id): bool { return true; }
    // public function update_timestamp(string $id, string $data): bool { return true; }
};

// カスタムセッションハンドラを設定します。
// 第二引数は、シャットダウン時にセッションハンドラを登録するかどうか (デフォルト true)。
session_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でカスタムセッションを実装する

<?php
declare(strict_types=1);

/**
 * PHPのカスタムセッションハンドラの基本例
 *
 * このコードは、`session_set_save_handler` を使用して、
 * PHPのセッションデータをメモリ上に一時的に保存する方法を示します。
 * システムエンジニアを目指す初心者の方へ:
 * 通常、PHPはセッションデータをファイルに保存しますが、この機能を使うと、
 * データベースやキャッシュシステム(例:Redis、Memcached)など、
 * 好きな場所にセッションを保存できるようになります。
 * 各メソッドは、セッションのライフサイクル(開始、読み込み、書き込み、終了など)
 * の特定の段階でPHPによって呼び出されます。
 */
class MyCustomSessionHandler
{
    /**
     * @var array<string, array{data: string, timestamp: int}>
     * セッションデータをメモリ上に保持する配列。
     * キーはセッションID、値はセッションデータと最終更新タイムスタンプの配列です。
     */
    private static array $sessionStorage = [];

    /**
     * セッションが開かれる際に呼び出されます。
     * ここでデータベース接続の確立など、セッション保存に必要な初期化を行います。
     */
    public function open(string $path, string $name): bool
    {
        // メモリ上に保存するため、特別な初期化は不要
        return true;
    }

    /**
     * セッションが閉じられる際に呼び出されます。
     * ここでデータベース接続のクローズなど、終了処理を行います。
     */
    public function close(): bool
    {
        // メモリ上に保存するため、特別なクリーンアップは不要
        return true;
    }

    /**
     * 指定されたセッションIDのデータを読み込みます。
     */
    public function read(string $id): string
    {
        if (isset(self::$sessionStorage[$id])) {
            $session = self::$sessionStorage[$id];
            // PHPのデフォルト設定 (session.gc_maxlifetime) を使用して有効期限をチェック
            if ($session['timestamp'] + (int)ini_get('session.gc_maxlifetime') < time()) {
                unset(self::$sessionStorage[$id]); // 期限切れセッションを削除
                return '';
            }
            return $session['data'];
        }
        return '';
    }

    /**
     * 指定されたセッションIDにデータを書き込みます。
     */
    public function write(string $id, string $data): bool
    {
        self::$sessionStorage[$id] = [
            'data' => $data,
            'timestamp' => time(), // 最終更新日時を記録
        ];
        return true;
    }

    /**
     * 指定されたセッションIDのセッションを破棄します。
     * `session_destroy()` が呼び出された際に使用されます。
     */
    public function destroy(string $id): bool
    {
        unset(self::$sessionStorage[$id]);
        return true;
    }

    /**
     * ガベージコレクション (GC) 処理。
     * 古い(有効期限切れの)セッションデータを削除します。
     * PHPの設定に基づき、不定期に呼び出されます。
     */
    public function gc(int $max_lifetime): bool
    {
        $currentTime = time();
        foreach (self::$sessionStorage as $id => $session) {
            if ($session['timestamp'] + $max_lifetime < $currentTime) {
                unset(self::$sessionStorage[$id]);
            }
        }
        return true;
    }

    /**
     * (デバッグ用) 現在のセッションストレージの内容を取得します。
     * このメソッドは、MyCustomSessionHandler の内部動作を確認するために使用します。
     */
    public static function getSessionStorageForDebug(): array
    {
        return self::$sessionStorage;
    }
}

// -----------------------------------------------------------------------------
// カスタムセッションハンドラの設定と使用例
// -----------------------------------------------------------------------------

// カスタムハンドラのインスタンスを作成
$handler = new MyCustomSessionHandler();

// `session_set_save_handler` を使用してカスタムハンドラを登録します。
// PHP 8.4の引数リストに従い、必須の6つのcallableメソッドを指定します。
session_set_save_handler(
    [$handler, 'open'],    // セッション開始時に呼び出される
    [$handler, 'close'],   // セッション終了時に呼び出される
    [$handler, 'read'],    // セッションデータ読み込み時に呼び出される
    [$handler, 'write'],   // セッションデータ書き込み時に呼び出される
    [$handler, 'destroy'], // セッション破棄時に呼び出される
    [$handler, 'gc']       // ガベージコレクション時に呼び出される
    // (create_sid, validate_sid, update_timestamp は省略)
);

// PHPのセッション管理が、スクリプト終了時に正しくカスタムハンドラを呼び出すようにします。
session_register_shutdown();

// セッションを開始します。
// これにより、`open()` や `read()` メソッドが呼び出され、`$_SESSION` が利用可能になります。
session_start();

echo "<h2>カスタムセッションハンドラの動作確認</h2>";

// セッション変数の設定と取得の例
if (!isset($_SESSION['visits'])) {
    $_SESSION['visits'] = 1;
    echo "<li>初めての訪問です!</li>";
} else {
    $_SESSION['visits']++;
    echo "<li>あなたの訪問回数: " . $_SESSION['visits'] . "回目</li>";
}

$_SESSION['user_name'] = 'ゲストユーザー';
echo "<li>セッションユーザー名: " . $_SESSION['user_name'] . "</li>";
echo "<li>現在のセッションID: " . session_id() . "</li>";

// 現在のセッションストレージの内容を表示 (デバッグ用)
echo "<h3>セッションストレージの現在の状態 (デバッグ用):</h3>";
echo "<pre>";
print_r(MyCustomSessionHandler::getSessionStorageForDebug());
echo "</pre>";

// セッションを破棄するためのリンク
echo "<h3>セッション操作</h3>";
if (isset($_GET['destroy'])) {
    session_destroy(); // セッションを破棄 (`destroy()` メソッドが呼び出されます)
    echo "<li>セッションが破棄されました。ページをリロードすると新しいセッションが開始されます。</li>";
    echo "<pre>セッション破棄後のストレージの状態:\n";
    print_r(MyCustomSessionHandler::getSessionStorageForDebug());
    echo "</pre>";
} else {
    $currentUrl = strtok($_SERVER['REQUEST_URI'], '?');
    echo '<li><a href="?destroy=1">セッションを破棄する</a></li>';
    echo '<li><a href="' . $currentUrl . '">ページをリロードする</a> (訪問回数が増えます)</li>';
}

// スクリプト終了時に `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セッションをファイルに保存するサンプル

<?php

/**
 * CustomFileSessionHandlerクラス
 * PHPのセッションをファイルシステムに保存するためのカスタムハンドラを提供します。
 * session_set_save_handler関数で使用するメソッドを実装しています。
 */
class CustomFileSessionHandler
{
    private string $savePath;

    /**
     * コンストラクタ
     * セッションファイルを保存するディレクトリを設定し、存在しない場合は作成します。
     *
     * @param string $savePath セッションファイルを保存するディレクトリのパス
     */
    public function __construct(string $savePath)
    {
        $this->savePath = $savePath;
        if (!is_dir($this->savePath)) {
            mkdir($this->savePath, 0777, true);
        }
    }

    /**
     * セッションを開く際に呼び出されます。
     * このメソッドは、セッションストレージの初期化や接続確立などを行います。
     * ファイルベースのハンドラでは通常、特に何もする必要はありません。
     *
     * @param string $path php.iniのsession.save_pathで設定されたパス
     * @param string $name セッション名
     * @return bool 成功した場合true、失敗した場合false
     */
    public function open(string $path, string $name): bool
    {
        // 実際には、この$pathは使用せず、クラスで設定された$this->savePathを使用します。
        return true;
    }

    /**
     * セッションを閉じる際に呼び出されます。
     * このメソッドは、セッションストレージの終了処理や接続解除などを行います。
     * ファイルベースのハンドラでは通常、特に何もする必要はありません。
     *
     * @return bool 成功した場合true、失敗した場合false
     */
    public function close(): bool
    {
        return true;
    }

    /**
     * セッションデータを読み込む際に呼び出されます。
     * 指定されたセッションIDに対応するデータをストレージから読み込みます。
     *
     * @param string $id セッションID
     * @return string セッションデータ (文字列)、またはデータがない場合は空文字列
     */
    public function read(string $id): string
    {
        $file = $this->getSessionFileName($id);
        if (file_exists($file)) {
            // ファイルの最終更新時刻とセッションの最大ライフタイムを比較し、
            // 期限切れの場合はファイルを削除して空文字列を返します。
            if (filemtime($file) + ini_get('session.gc_maxlifetime') < time()) {
                unlink($file);
                return '';
            }
            return (string) file_get_contents($file);
        }
        return '';
    }

    /**
     * セッションデータを書き込む際に呼び出されます。
     * 指定されたセッションIDとデータ文字列をストレージに書き込みます。
     *
     * @param string $id セッションID
     * @param string $data 書き込むセッションデータ
     * @return bool 成功した場合true、失敗した場合false
     */
    public function write(string $id, string $data): bool
    {
        $file = $this->getSessionFileName($id);
        return (bool) file_put_contents($file, $data);
    }

    /**
     * セッションを破棄する際に呼び出されます。
     * 指定されたセッションIDに対応するデータをストレージから削除します。
     *
     * @param string $id 破棄するセッションID
     * @return bool 成功した場合true、失敗した場合false
     */
    public function destroy(string $id): bool
    {
        $file = $this->getSessionFileName($id);
        if (file_exists($file)) {
            unlink($file);
        }
        return true;
    }

    /**
     * ガベージコレクション (GC) を実行する際に呼び出されます。
     * 古いセッションデータ (最大ライフタイムを超えたもの) を削除します。
     *
     * @param int $max_lifetime セッションの最大ライフタイム (秒)
     * @return bool 成功した場合true、失敗した場合false
     */
    public function gc(int $max_lifetime): bool
    {
        foreach (glob($this->savePath . '/sess_*') as $file) {
            if (filemtime($file) + $max_lifetime < time()) {
                unlink($file);
            }
        }
        return true;
    }

    /**
     * ヘルパーメソッド: セッションファイル名を生成します。
     *
     * @param string $id セッションID
     * @return string セッションファイルのフルパス
     */
    private function getSessionFileName(string $id): string
    {
        return $this->savePath . '/sess_' . $id;
    }
}

// --- メインスクリプト ---

// セッションファイルを保存するためのテンポラリディレクトリを設定します。
// 環境に合わせて適切なパスに変更してください。
$sessionSavePath = sys_get_temp_dir() . '/php_custom_sessions_example';

// カスタムセッションハンドラをインスタンス化します。
$handler = new CustomFileSessionHandler($sessionSavePath);

// session_set_save_handler関数を使用して、PHPにカスタムセッションハンドラを登録します。
// 各引数には、CustomFileSessionHandlerクラスの対応するメソッドへのcallableを指定します。
// 最後の引数 'true' は、PHPのシャットダウン時にsession_write_close()が自動的に呼び出されるようにします。
session_set_save_handler(
    [$handler, 'open'],      // セッションを開く関数
    [$handler, 'close'],     // セッションを閉じる関数
    [$handler, 'read'],      // セッションデータを読み込む関数
    [$handler, 'write'],     // セッションデータを書き込む関数
    [$handler, 'destroy'],   // セッションを破棄する関数
    [$handler, 'gc'],        // ガベージコレクションを行う関数
    null,                    // オプション: create_sid callable
    null,                    // オプション: validate_sid callable
    null                     // オプション: update_timestamp callable
);

// カスタムハンドラが登録された状態でセッションを開始します。
session_start();

// --- セッションの動作をデモンストレーション ---

// セッション変数 'counter' が設定されているか確認します。
if (!isset($_SESSION['counter'])) {
    // 初回アクセスの場合
    $_SESSION['counter'] = 1;
    echo "ようこそ!これはあなたの最初の訪問です。\n";
} else {
    // 2回目以降のアクセスの場合
    $_SESSION['counter']++;
    echo "あなたはこれまでこのページを " . $_SESSION['counter'] . " 回訪問しました。\n";
}

echo "セッションID: " . session_id() . "\n";
echo "セッションデータは以下のディレクトリに保存されます: " . $sessionSavePath . "\n";

// セッション破棄の動作を確認したい場合は、以下の行のコメントを解除してください。
// その後、ページを複数回リロードし、再度コメントを解除した状態でリロードすると、
// カウンターがリセットされるのが確認できます。
// session_destroy();

?>

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()を呼び出してセッションを開始してください。実運用では、セッション保存パスのセキュリティやアクセス権限、堅牢なエラーハンドリングに十分配慮してください。

【PHP8.x】session_set_save_handler関数の使い方 | いっしー@Webエンジニア