hogehoge foobar Blog Style Beta

Web,Mac,Linux,JavaScript,Perl,PHP,RegExp,Git,Vim,Redmineなど技術的なことのメモや、ちょっと便利そうなものの紹介をしています。

PHPセッションをDBで管理 - PHPセッション管理のコールバック関数を設定編

前回エントリ PHPセッションをDBで管理 - OracleでのAutoIncrement用テーブルを作成編 の続きです。
前回はOracleでセッション管理用のテーブルを作成するところまで行ないました。
今回は、そのテーブルを使って実際にPHPからセッション管理を行うのをやってみます。

※参考にさせてもらったページ

PHPのセッションをDB管理するにあたり、全体的な部分は「Web Artisan Blog」、OracleでAutoIncrementについては「イイ!! プログラミング Blog」、OracleのCLOBの更新方法等については「あるプログラマーのつぶやき」を参考にさせていただきました。

PHPセッション管理のコールバック関数の作成〜設定

PHPのセッションはデフォルトでファイルを使用するようになっているため、まずは設定を変更します。これはphp.iniまたはini_setで「session.save_handler」を「user」に設定すればOKです。

次に実際にセッション管理用の関数を定義します。
定義する関数としては、以下のようなものになります。
(表の関数名は当エントリのサンプルコードの関数名です。実際には任意の名前を付けられます。)

関数名 用途
sess_open セッションのオープン時に実行される関数,コンストラクタのようなもの
sess_close セッションのクローズ時に実行される関数,デストラクタのようなもの
sess_read セッションの読み込み時に実行される関数,セッションIDをキーにしてセッション情報を取得する処理を書く
sess_write セッションの書き込み時に実行される関数,セッションIDをキーにしてセッション情報を更新する処理を書く
sess_destroy セッション破棄時に実行される関数,セッションIDをキーにしてセッション情報を削除する処理を書く
sess_clean ガベージコレクション実行時に実行される関数,maxlifetime以上経過しているセッションを削除する処理を書く

定義した関数については「session_set_save_handler」で、セッション管理用コールバック関数として設定をします。

設定の例
<?php
session_set_save_handler('sess_open','sess_close','sess_read','sess_write','sess_destroy','sess_clean');
?>

ここまで出来たら、あとは普通に「session_start();」でセッション開始したり、「$foo = $_SESSION['foo'];」「$_SESSION['bar'] = $bar;」とかでセッションの読み書きが出来るようになります。
もちろんセッション情報はDBに書き込まれます。

下記に、今回作成したサンプルコードを書いておきます。

今回作成したサンプルコード

※エラー制御とかをぶっちぎっているのはご了承ください。

<?php
ini_set('session.save_handler', 'user');

//セッションのオープン時
function sess_open()
{
    return true;
}

//セッションのクローズ時
function sess_close()
{
    return true;
}

//セッションの読み込み時
function sess_read($id)
{
    // セッションの取得(テーブル:sessions)
    // sessions.dataはCLOB型
    $conn = oci_connect("username", "password", "oracle_sid");
    $sql = "SELECT data FROM sessions WHERE sess_id = '$id'";
    $stmt = oci_parse($conn, $sql);
    oci_execute($stmt, OCI_DEFAULT);
    while ( $row = oci_fetch_array($stmt, OCI_ASSOC+OCI_RETURN_LOBS) ){
        $data = $row['DATA'];
    }
    oci_free_statement($stmt);
    oci_close($conn);
    return $data;
}

//セッションの書き込み時
function sess_write($id, $data)
{
    // セッションを一度削除
    $conn = oci_connect("username", "password", "oracle_sid");
    $sql = "DELETE FROM sessions WHERE sess_id = '" . $id . "'";
    $stmt = oci_parse($conn, $sql);
    oci_execute($stmt, OCI_DEFAULT);
    
    // 新たなシーケンスでセッションを再度書き込み
    // シーケンスはOracleトリガーで自動インクリメントされる
    $sql = "INSERT INTO sessions (sess_id, data, update_time) VALUES ('$id', empty_clob(), sysdate) RETURNING data INTO :lob";
    $stmt = oci_parse($conn, $sql);
    $clob = oci_new_descriptor($conn, OCI_D_LOB);
    oci_bind_by_name($stmt, ":lob", &$clob, -1, OCI_B_CLOB);
    oci_execute($stmt, OCI_DEFAULT);
    $clob->save($data);
        
    oci_commit($conn);
    oci_free_statement($stmt);
    oci_close($conn);
    
    $rtn = true;
    return $rtn;
}
 
//セッション破棄時(session_destroy時)
function sess_destroy($id)
{
    // セッションの削除
    $conn = oci_connect("username", "password", "oracle_sid");
    $sql = "DELETE FROM sessions WHERE sess_id = '" . $id . "'";
    $stmt = oci_parse($conn, $sql);
    oci_execute($stmt, OCI_DEFAULT);
    
    oci_commit($conn);
    oci_free_statement($stmt);
    oci_close($conn);
    
    $rtn = true;
    return $rtn;
}

//ガーベージコレクション実行時
function sess_clean($maxlifetime)
{
    // 最終更新日時からmaxlifetime以上経過しているセッションを削除
    $conn = oci_connect("username", "password", "oracle_sid");
    $sql = "DELETE FROM sessions WHERE update_time < ( sysdate - " . $maxlifetime . " / 86400 )";
    $stmt = oci_parse($conn, $sql);
    oci_execute($stmt, OCI_DEFAULT);
    
    oci_commit($conn);
    oci_free_statement($stmt);
    oci_close($conn);
    
    $rtn = true;
    return $rtn;
}

// セッション管理用のユーザー定義のコールバック関数を設定する。
session_set_save_handler('sess_open','sess_close','sess_read','sess_write','sess_destroy','sess_clean');
?>

そういえば・・・

PHPの資格試験が今年(2010年)の秋くらいから始まるらしいですね。
なんでも教科書がオライリー本だとか。

PHPセッションをDBで管理 - OracleでのAutoIncrement用テーブルを作成編

PHPのセッション管理はデフォルトがファイルでの管理になりますが、WEBサーバーを冗長化した時などは各サーバーにセッション用ファイルが格納されてしまうため、途中でセッションが切れてしまうといった問題が発生します。

そうならないために、PHPのセッションをDBで管理する方法をやってみます。
セッション管理するDBとして、今回はOracleを使用しています。

ちょっと書くことが多いので、エントリを2つに分けて書きます。
今回はセッション情報を格納するOracleでのAutoIncrement用テーブルを作成する方法について書きます。

※参考にさせてもらったページ

PHPのセッションをDB管理するにあたり、全体的な部分は「Web Artisan Blog」、OracleでAutoIncrementについては「イイ!! プログラミング Blog」、OracleのCLOBの更新方法等については「あるプログラマーのつぶやき」を参考にさせていただきました。

テーブルの作成

オートインクリメントする項目についてはNUMBER型+NOT NULL+PRIMARY KEYとします。(下記では"seq_no")
セッションIDをセットする項目にはCHAR型+UNIQUEとします。(下記では"sessid")
実際にセッションの値が格納される項目はCLOB型にします。
ガベージコレクションを実施するために最終更新日時の項目をDATE型で定義します。(下記では"updated_at")

CREATE TABLE sessions (
    seq_no NUMBER NOT NULL,
    sess_id CHAR(32) NOT NULL ,
    data CLOB,
    update_time DATE,
    CONSTRAINT pk_sessions PRIMARY KEY( seq_no ),
    CONSTRAINT uq_sessions UNIQUE( sess_id )
)

シーケンスの作成

次にインクリメントされるためのシーケンスを作成します。
1から始まり1ずつ増える、ごく一般的なシーケンスです。
最大値として「NOMAXVALUE」を指定しておきます。

CREATE SEQUENCE sessions_seq
START WITH 1
INCREMENT BY 1
NOMAXVALUE;

トリガーの作成

先に作成した、テーブルとシーケンスを結びつけるためのトリガーを作成します。
sessionsテーブルへのINSERTが発生した場合に、sessions_seqから新たに取得したシーケンスを「:new.seq_no」に代入してからレコードがINSERTされるようにします。

CREATE TRIGGER sessions_trigger
BEFORE INSERT ON sessions
FOR EACH ROW
BEGIN
  SELECT sessions_seq.nextval INTO :new.seq_no FROM dual;
END;

これでOracleでのAutoIncrement用テーブルが作成出来ました。

実際に使ってみる。

実際にsessionsテーブルにレコードをINSERTとしてみます。
以下のINSERTのSQLを実行してみます。

SQL> INSERT INTO sessions (sess_id, data, update_time) VALUES ('session_id_001', 'hogehoge', sysdate);
SQL> INSERT INTO sessions (sess_id, data, update_time) VALUES ('session_id_002', 'foobar', sysdate);
SQL> commit;

で、実際にテーブルのデータを確認してみると、

sessionsテーブルのデータ
seq_no sess_id data update_time
1 session_id_001 hogehoge 2010/07/30 10:00:00
2 session_id_002 foobar 2010/07/30 10:05:12

という感じで、seq_noが自動でインクリメントされた形でレコードが追加されました。


これで、セッション用テーブルの作成は出来ました。
続いては、PHP側でセッション管理用のコールバック関数を作成する必要がありますが、
それは別エントリで書きます。