intro
先日拝聴しに行った PHP カンファレンス2007にて「大規模サイトを構築するためのいろは」みたいなセッションで「セッションを複数の web サーバで共有するにはデータベースを使うのが簡単だよ」というお話がでておりました。さらに、ストレージをデータベースにした際にセッションハンドラを簡単にデータベースに変更するためには「PEAR::HTTP_session が導入楽チンだよ」とのこと。
PEAR::HTTP_session は確かに楽チンなのですが、ドライバに使えるのが PEAR::DB、PEAR::MDB、PEAR::MDB2、memcache といったところしかございません。
「memcache を導入すんのは面倒くせえけど、DB とか MDB とか MDB2 じゃおせえんじゃねえの」という向きがおられるかもしれませんので、僕が使ってる PDO 用のドライバを up しました。どなたでもご利用下さい。ソースは一番下にあります。
使い方
他のストレージドライバと同じディレクトリにコピーして普通に HTTP_session::setContainer() メソッドを呼び出して下さい。
HTTP_session::setContainer() は
HTTP_session::setContainer('ドライバの名前', array('オプション'));
と言う呼び出し方となっておりますので、データベースに接続したオブジェクトを渡すのであれば他のドライバと同様に
HTTP_session::setContainer('PDO', array('dsn' => $db, 'table' => 'sessiondata', 'autooptimize' => TRUE));
と言う呼び出し方で問題ありませんが、DSN を渡す場合は接続ユーザとパスワードが DSN に含まれていないので、別に渡してやる必要があります。ので
HTTP_session::setContainer('PDO', array('dsn' => $dsn, 'username' => $username, 'password' => $password, 'table' => 'sessiondata', 'autooptimize' => TRUE));
と言う形で指定して下さい。$username には接続ユーザ名、$password にはパスワードをそれぞれ代入して下さい。
後は普通にセッションを開いてあれじゃないこれじゃないと試してみて下さい。
注意点
PEAR::HTTP_session は PHP4 から利用可能ですが、PDO はPHP5.0からしか使えません。しかも5.0だと導入面倒です。5.1から本体にバンドルされてます。
そこらへんに関してはマニュアルを読んで頂いた方が理解が早いかと思います。
クエリを投げるメソッドでは PDOException を拾ってますが基本的に FALSE を返しているだけです。HTTP_Session_Container_PDO::_connect() メソッドだけ PDOException オブジェクトをそのまま返しています。
そのほか
up したクエリやスクリプトを使用して発生した障害や損害などいかなる問題についても僕は責任を取れませんので、ご利用の際は自己責任でお使い下さい。
PEAR::HTTP_session でストレージドライバに memcache を使うくらいなら memSession の方が楽チンかと思います。
セッションハンドラを上書きしているだけなのでファイルをインクルードするだけで使えます
ソースコード
/** * PEAR::HTTP_session_container の PDO ドライバ * * @author Ryo Suyama <ryo@spais.jp> * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version 0.0.1 2006/08/10 */ require_once 'HTTP/Session/Container.php'; /** * PEAR::HTTP_session_container の PDO ドライバ * * セッション格納テーブル作成するクエリ * <code> * CREATE TABLE `sessiondata` ( * `id` CHAR(32) NOT NULL, * `expiry` INT UNSIGNED NOT NULL DEFAULT 0, * `data` TEXT NOT NULL, * PRIMARY KEY (`id`) * ); * </code> * * @author Ryo Suyama <ryo@spais.jp> * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version 0.0.1 2006/08/10 */ class HTTP_Session_Container_PDO extends HTTP_Session_Container { /** * PDO 接続オブジェクト * * @var object PDO * @access private */ var $db = null; /** * セッションデータのキャッシュ ID * * @var mixed * @access private */ var $crc = false; /** * コンストラクタ * * @param array $options Options array('dsn' => DSN, 'username' => データベースユーザ, 'password' => パスワード) * * @access public * @return void */ function HTTP_Session_Container_PDO($options) { $this->_setDefaults(); if (is_array($options)) { $this->_parseOptions($options); } else { $this->options['dsn'] = $options['dsn']; $this->options['username'] = $options['username']; $this->options['password'] = $options['password']; } } /** * データベースへの接続とインスタンスの生成メソッド * * @param string $dsn DSN * @param string $username データベースユーザ * @param string $password パスワード * * @access private * @return mixed エラーの場合は PDOException を返す そうでない場合は TRUE */ function _connect($dsn, $username, $password) { try { if (is_string($dsn) && is_string($username) && is_string($password)) { $this->db = new PDO($dsn, $username, $password); } else if (is_object($dsn) && is_a($dsn, 'PDO')) { $this->db = $dsn; } else { return new PEAR_Error("The given dsn was not valid in file " . __FILE__ . " at line " . __LINE__, 41, PEAR_ERROR_RETURN, null, null ); } return true; } catch(PDOException $e) { return $e; } } /** * オプションのデフォルト設定メソッド * * @access private * @return void */ function _setDefaults() { $this->options['dsn'] = null; $this->options['username'] = 'username'; $this->options['password'] = 'password'; $this->options['table'] = 'sessiondata'; $this->options['autooptimize'] = false; } /** * HTTP_Session_Container_PDO::_connect() のエイリアスみたいなもの * TRUE が返ってくれば TRUE を、そうでない場合は FALSE を返す * * @param string $save_path Save path * @param string $session_name Session name * * @return bool */ function open($save_path, $session_name) { $c = $this->_connect($this->options['dsn'], $this->options['username'], $this->options['password']); return $c === TRUE? $c: FALSE; } /** * Free resources * * @return bool */ function close() { return true; } /** * Read session data * * @param string $id Session id * * @return mixed */ function read($id) { try { $query = sprintf("SELECT data FROM %s WHERE id = '%s' AND expiry >= %d", $this->options['table'], md5($id), time()); $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_NUM); $this->crc = strlen($result[0]) . crc32($result[0]); return $result[0]; } catch(PDOException $e) { return false; } } /** * Write session data * * @param string $id Session id * @param mixed $data Data * * @return bool */ function write($id, $data) { try { if ((false !== $this->crc) && ($this->crc === strlen($data) . crc32($data))) { // $_SESSION hasn't been touched, no need to update the blob column $query = sprintf("UPDATE %s SET expiry = %d WHERE id = '%s'", $this->options['table'], time() + ini_get('session.gc_maxlifetime'), md5($id)); } else { // Check if table row already exists $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = '%s'", $this->options['table'], md5($id)); $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_NUM); if (0 == intval($result[0])) { // Insert new row into table $query = sprintf("INSERT INTO %s (id, expiry, data) VALUES ('%s', %d, '%s')", $this->options['table'], md5($id), time() + ini_get('session.gc_maxlifetime'), $data); } else { // Update existing row $query = sprintf("UPDATE %s SET expiry = %d, data = '%s' WHERE id = '%s'", $this->options['table'], time() + ini_get('session.gc_maxlifetime'), $data, md5($id)); } } $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_ASSOC); return true; } catch(PDOException $e) { return false; } } /** * Destroy session data * * @param string $id Session id * * @return bool */ function destroy($id) { try { $query = sprintf("DELETE FROM %s WHERE id = '%s'", $this->options['table'], md5($id)); $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_ASSOC); return true; } catch(PDOException $e) { return false; } } /** * Replicate session data to table specified in option 'replicateBeforeDestroy' * * @param string $targetTable Table to replicate to * @param string $id Id of record to replicate * * @access private * @return bool */ function replicate($targetTable, $id = null) { try { if (is_null($id)) { $id = HTTP_Session::id(); } // Check if table row already exists $query = sprintf("SELECT COUNT(id) FROM %s WHERE id = '%s'", $targetTable, md5($id)); $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_ASSOC); // Insert new row into dest table if (0 == intval($result)) { $query = sprintf("INSERT INTO %s SELECT * FROM %s WHERE id = '%s'", $targetTable, $this->options['table'], md5($id)); } else { // Update existing row $query = sprintf("UPDATE %s dst, %s src SET dst.expiry = src.expiry, dst.data = src.data WHERE dst.id = src.id AND src.id = '%s'", $targetTable, $this->options['table'], md5($id)); } $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_ASSOC); return true; } catch(PDOException $e) { return false; } } /** * Garbage collection * * @param int $maxlifetime Maximum lifetime * * @return bool */ function gc($maxlifetime) { try { $query = sprintf("DELETE FROM %s WHERE expiry <%d", $this->options['table'], time()); $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_ASSOC); if ($this->options['autooptimize']) { switch($this->db->getAttribute(PDO::ATTR_DRIVER_NAME)) { case 'mysql': $query = sprintf("OPTIMIZE TABLE %s", $this->options['table']); break; case 'pgsql': $query = sprintf("VACUUM %s", $this->options['table']); break; default: $query = null; break; } if (isset($query)) { $r = $this->db->query($query); $result = $r->fetch(PDO::FETCH_ASSOC); } } return true; } catch(PDOException $e) { return false; } } }
- 新しい: テスト
- 古い: 携帯電話のIPアドレス範囲テーブル
コメント:0
トラックバック:0
- この記事のトラックバック URL
- http://spais.jp/php/pearhttp_session%e3%81%ae%e3%82%b9%e3%83%88%e3%83%ac%e3%83%bc%e3%82%b8%e3%83%89%e3%83%a9%e3%82%a4%e3%83%90%e3%81%abpdo%e3%82%92%e5%88%a9%e7%94%a8%e3%81%99%e3%82%8b/2007-09-29/trackback
- トラックバックの送信元リスト
- PEAR::HTTP_sessionのストレージドライバにPDOを利用する - SPaiS より