各クラスは、インスタンス変数とインスタンスメソッドを有しています。 これらの変数およびメソッドのいくつかはカスタマイズが可能で、 いくつかはクラス自体の内部で使用するためのクラスです。 全てのクラスは本文書に記されていますが、 内部変数およびメソッドの改修はサポートされていません。 内部インターフェースは、ライブラリのバージョンが代わった際に 予告無しに変更を受ける可能性が高いです。
本節では、PHPLIB のコア機能をリファレンス形式で解説します。 クラスは、上位のクラスから示しています。これは、 この順番にした方がコア構造の理解がより容易であるためです。 PHPLIBの全ての機能を使用するためには、コア構造を完全に理解する必要があります。
DB_Sql
は、SQLデータベースにアクセスするために
CT_Sql
およびAuth
により使用されます。
このクラスを直接使うこともお勧めです。
Host | SQLサーバーを実行しているホスト | |
Database | サーバーで使用するデータベースまたはインスタンスの名前 | |
User | 接続を行う際に使用されるユーザー名 | |
Password | 接続を行う際に使用されるパスワード | |
Row | 現在の結果行(レコード)の数で、0から始まります。 | |
Errno | 整数: 直近のデータベース操作のエラー番号。 | |
Error | 文字列: 直近のデータベース操作のエラーメッセージ。 | |
Halt_On_Error | "yes", "no", "report" のどれか。 "yes" (デフォルト) に設定した場合、データベースインターフェースはエラーをリポートし、 プログラムを停止します。"report" に設定した場合、 データベースインターフェースは同じく全てのエラーをリポートしますが、 アプリケーションに "false" をリポートしつつ、実行を続け、withErrno および Error を適当に設定します。 "no" に設定した場合、データベースインターフェースはエラーをレポートせず、 アプリケーションに "false" を通知し、適当な Errno および Error が設定されます。 | |
Auto_Free | 論理値: いくつかのDBインターフェースにおいて過去の結果保持用メモリを解放するためのフラグです。 | |
Debug | 論理値: 設定された場合、データベースは全てのクエリーと追加の統計出力を出力します。 | |
type | 定数文字列: データベースインタフェースの名前、例えば、"mysql" や "oracle" | |
revision | 定数バージョン文字列: データベースAPIのバージョン(例えば 1.2)。API.Sql_table を実装するファイルの CVSリビジョンではありません。 | string: nextid() API 関数で使用されるテーブル名。 |
|
Record | いくつかのデータベースインターフェースでは、現在のテーブルの結果を保持する行となります。 |
Link_ID | SQL リンク ID. |
Query_ID | SQL 結果 ID. |
|
コンストラクタ。インスタンスを作成する際に、オプションでクエリー文字列を指定可能です。
$db = new DB_Sql_Subclass("select * from mytable");
query_string
は、データベースに送信する SQL 文です。
SQL文を送信した後、Error
および Errno
が更新されます。
クエリーが文法的に間違っている場合(有効な結果IDは作成されません)、
有意なエラーメッセージを付けて halt()
がコールされます。
データベースへのアクティブな接続がない場合、Host
, Database
,
User
, Password
インスタンス変数からの情報を
使用して、pconnect()
が実行されます。
query()
文の結果を返します。
この結果は、必ず有効な結果ID(Halt_On_Error が "yes" でない場合はfalse)となります。
next_record()
は、
現在のクエリー結果のカーソルを前に進め、
Record
, Row
,
Errno
, Error
インスタンス変数を
更新します。
新規結果レコードがある場合に、trueを返します。現在の結果セットの走査が終了した
場合はfalseを返します。Auto_Free
がtrueの場合、falseが返される前に
free_result()
が自動的にコールされます。
現在のSELECTクエリーにより返される行(レコード)数 を返します。
注意: この情報は、全てのデータベースインタフェースで 取得可能であるわけではありません。 いくつかの先進的なデータベースでは、バックエンドが結果行を追加している間に 非同期にクエリー結果を返し始めます。 このような環境では、結果セット全体の大きさを知ることはできません。
そのような環境ではクエリー文のWHERE句を二重化し、 COUNT(*)により行数を得る必要があります。 この手法は、データベースによりクエリーパスとクエリー結果がキャッシュされているため、 見た目よりも効率が悪いわけではありません。
現在のINSERT, UPDATE, DELETE クエリーで影響を受けた行(レコード)数を返します。
現在のクエリーにより返されたカラムの数を返します。
現在のクエリーにより返された行の数を出力します。
Record[$field]
をアクセスするのと同じです。
Record[$field]を出力するのと同じです。
この関数はhalt()
によりコールされ、
データベースのエラーメッセージを出力します。
使用するDB_Sql
のサブクラスにおいてこのメソッドをオーバーライドし、
アプリケーションの他の部分のレイアウトに適合させるためにエラーメッセージを
フォーマットすることが可能です。
データベースにエラーが発生したことをアプリケーションのオペレータに
メールで通知するといったエラー処理を追加することも可能です。
結果セットにおける Row
ポインタの位置を設定します。
同じ結果セットを2回読む際や結果を不連続に読む際には便利です。
$pos
の指定が正しいかどうかは確認されません。
注意: Auto_Free
が true の場合、
結果の最後のレコードを読み終わった時点で結果セットが既に解放されているために、
seek()
は使用できない可能性があります。
注意: 全てのデータベースインターフェースが、シーク可能なカーソルを提供しているわけではありません。 この機能はそのような環境では使用できません。
この関数は、このリンクIDは内部的に pconnect()
を実行し、
その結果である現在のリンクIDを返します。
この情報は必要ありません。
この関数は、データベースクラスにより内部的に query()
を実行した結果から現在の結果IDを返します。
この情報は必要ありません。
$table
は、現在のデータベースの
SQLテーブル名です。この関数は、$table
の ( 0 から始まる)列番号を添字とした
連想配列を返します。
各連想配列の値は、table
(列が属するテーブル)、name
(列名)、
type
(列のデータ型)、len
(列の幅)、flags
(利用可能なデータベース依存の列のフラグ)で、各テーブルの列毎に1行となります。
各行はテーブルの1つの列を表します。
The data returned by metadata()
により返されたデータは、
Table クラスに渡す際に使用することが可能です。
full
パラメータを指定した場合、
フィールド名を添字とする列 meta
が追加され、
その名前のフィールド番号が返されます。
また、テーブル幅を有するカラム num_fields
も追加されます。
$table
が省略された場合、
この関数は直近に実行されたクエリーの結果を返します。
注意: この機能は、現在、MySQLインターフェースにおいてのみ
実装されています。この機能を他のインターフェースに実装することを推奨します。
注意: 現在、PostgreSQL および ODBC インターフェースが確実に
レポートするのは、table
, name
, type
データのみです。
これを修正することを推奨します。
テーブル名とテーブルスペース名を有する配列を返します。
テーブル名 : $return[$i]["table_name"]
テーブルスペース名 : $return[$i]["tablespace_name"]
テーブルは、$i=0 から最後のテーブルまでです。
db_oracle.inc, db_oci8.inc, db_mysql.inc, db_pgsql.inc で実装されています。
この関数は、
$sequence_name
という名前のシーケンスからシーケンス番号を返します。
この番号はユニークであることが保障されており、プライマリキーとして
使用することが可能です。
必要に応じてLink_ID
を作成するために内部で使用されます。
リンク作成は暗黙のうちに行われるため、connect()
を明示的に呼ぶ必要はありません。
最初のデータベース接続が行えなかった場合や接続先のデータベースが存在しなかった場合に
query()
により使用されます。Halt_On_Error
の設定により、
このメソッドは、エラーをレポートするために haltmsg()
を呼びます。
設定に応じて、結果セットを解放するために next_record()
により内部的に使用されます。
データベース接続用の適当なパラメータを与えるためにサブクラスを使用します。
デフォルトの設定でも充分有用ですが、エラーメッセージをカスタマイズするために
halt()
をオーバーライト(上書き)することができます。
class DB_Article extends DB_Sql {
var $classname = "DB_Article";
var $Host = "sales.doma.in";
var $Database = "shop_project";
var $User = "webuser";
var $Password = "";
function haltmsg($msg) {
printf("</td></table><b>Database error:</b> %s<br>\n", $msg);
printf("<b>MySQL Error</b>: %s (%s)<br>\n",
$this->Errno, $this->Error);
printf("shopmaster@doma.in と連絡を取り、");
printf("正確なエラーメッセージを報告してください。<br>\n");
}
}
問い合わせを管理するには次のようにサブクラスのインスタンスを使用して下さい。
$q = new DB_Article;
$query = sprintf("select * from articles where article like '%%%s%%'",
$searchword);
$q->query($query);
while($q->next_record()) {
printf("<tr><td>%s</td><td>%s</td></tr>\n",
$q->f("art_id"),
$q->f("article"));
}
PHP は、可能ならば接続を再利用します。 同じホストに同じユーザ名とパスワードで接続が行われている場合、 PHP により第2の接続は作成されません。 代わりに既存の接続IDが呼出側に返されます。 これは、全てのPHPデータベースインターフェースの *_connect() および *_pconnect() コールの両方に関して 成り立ちます。
これに関してMySQLのユーザーに注意すべきことがあります。 現在のデータベースを変更するために MySQLの "use" コマンドを決して使用しないで下さい。 これを行うと、セッション管理を正しく行うことができなくなります。 さもなくば、アプリケーションの部分に全てのPHPLIBテーブルを作成して下さい。
(例えば Oracle のような)いくつかのデータベースは、connect() の実行負荷が とても高いです。 これらのデータベースについては、CGI PHP から mod_php に変えた場合に性能が 劇的に向上します。 これは、PHPLIB がデータベースと接続するために "*_pconnect()" メソッドを 使用するからです。 mod_php においては、データベース接続は ページの作成処理が終了した後も Web サーバープロセスにより維持され、 後で同じホスト/ユーザ名/パスワードの組み合わせで接続が要求された場合、 再利用されます。
これによりデータベースサーバーに同時に接続する数は、 最大でも "Webサーバーのプロセス数" × "ホスト名/ユーザー名/パスワードの組み合わせの数" となります。 このことを、ライセンス数およびサーバー負荷に関する計画を立てる際に頭に置いておいて 下さい。CGI PHP を使用した場合、接続設定時の消費時間のせいで データベースサーバーへの同時接続数はおそらく減少することでしょう。 しかし、接続設定時間が無視できるようなデータベースサーバー(例えば、MySQL)では、 こうした手法は実用的なソリューションの 1 つです。 (Oracle ではこの手法は試みないで下さい。)
nextid()の使用法
nextid()
関数をプライマリキーとして使用可能な
シーケンス番号を得るために使用することが可能です。
この関数は任意の数の名前付きのシーケンスを管理するため、
コールする際にシーケンス名を指定する必要があります。
$db = new DB_Article;
$artnr = $db->nextid("article_sequence");
$query = sprintf("insert into articles ( artnr, ...) values ('%s', ...)",
$artnr, ...);
$db->query($query);
reset($articles);
while(list($itemnr, $itemdesc) = each($articles)) {
$itemnr = $db->nextid("item_sequence");
$query = sprintf("insert into items (artnr, itemnr, ...) values ('%s', '%s', ...)",
$artnr, $itemnr, ...);
$db->query($query);
}
ページ管理は、現在、以下の関数から構成されています。
この関数は、 ページ機能/クラス名の組の配列を指定してコールされます。 現在、有効な機能を以下に示します。
このページは、セッション変数を使用します。
このページはセッション認証を使用します。
auth
機能を指定した場合、sess
機能も指定する必要があります。
このページは許可属性により保護されており、
マッチする権利に関して認証を受けたユーザーのみがアクセス可能です。
perm
機能を指定する場合、auth
および sess
機能も指定する必要があります。
このページは、ユーザ変数を使用します。
user
機能を指定する場合、auth
および
sess
機能も指定する必要があります。
各機能では、次の例のように機能を実装したクラス名を指定します。
page_open(array("sess" => "Shop_Session"));
この関数は、$sess
として Shop_Session
のインスタンスを作成し、
初期化します。この関数は、機能の依存性も確認します。クラスShop_Session
の
実装はあなたが提供することが前提となっていることに注意して下さい。
これは、通常 local.inc
で行われ、通常提供されたクラス Session
を拡張することにより行われます。
この実行例は、本ドキュメントのクラスに関する説明の後に示されています。
ページの最後で(全ての結果を計算し終わった後で)、page_close()
をコール
する必要があります。
これにより、全てのページ状態、セッション、ユーザー変数をデータベースが保存されます。
page_close()
をコールした後でのセッション及びユーザー変数への変更は、
記録されません。
現在、1つのページで複数回 page_close()
をコールすることが可能です。
(これが将来のバージョンで保障されているわけではありません!)
コールされる度にセッション変数が保存されます。
注意: この動作は変更される予定です。
レコードのロック機能を導入した際には、page_close()
がコールされるのは各ページで1回のみであることが重要となります。
これは、page_close()
がセッションレコードを暗黙のうちにアンロックするからです。
また、ロック時間を最小に保つためには、ページにおいて
page_close()
を出来るだけ早くコールすることが重要です。
最新の機能。 いくつかのアプリケーションでは、 1つまたは複数のセッションクラスが使用するデータを手動でロードする必要があります。 @@TODO
最新の機能. @@TODO
<?php
page_open(array("sess" => "Shop_Session"));
$sess->register("s"); // 詳細は、以下の "Session" を参照下さい。
?>
<html>
<h1><?php print ++$s ?></h1>
</html>
<?php page_close(); ?>
バージョン5 までのPHPLIBでは、 page_open()
に関して
"cart" 機能がありました。
cart は、ライブラリの小ささ、保守性、構造を保つために PHPLIB の
コア機能から削除されました。
結果的に "cart" 機能はなくなりました。
Cart
クラスはまだ存在し、拡張機能として存在しています。
cart を使用するには、手動で使用するページでインクルードし、インスタンスを作成する
必要があります。
詳細な情報については、Cart
クラスを参照して下さい。
Session
クラスは、データベースへのセッションデータの読み書き用に少々の SQL を
使用します。
セッションをデータベースの種類に依存しないようにするために、この SQL は独立したクラスである
CT_Sql
に分けてあります。
Session
は、現在、SQL コンテナであるか否かに関わらず記憶領域へのアクセスを
常にコンテナクラスを通じて行います。
database_table | 使用するデータベーステーブルの名前 |
database_class | クラス名。CT_Sql はデータの保存・取得の際にこのクラスを使用します |
|
コンテナに適当なパラメータを与えるためにサブクラスを使用します。 通常、サブクラスは次のようになります。
class My_Sql extends CT_Sql {
var $classname = "My_Sql";
var $database_table = "active_sessions";
var $database_class = "DB_Session";
こうして作成したMySQLをSession クラスの中で使用することができます。 このクラスを参照するには、"that_class" 変数に "My_Sql" を指定して下さい。
Session
クラスは、データベースとの間でセッションデータを読み書きするために
少々の SQL を使用します。
セッションをデータベースの種類に依存しないようにするために、Session
は
現在記憶領域へのアクセスを常にコンテナクラスを通じて行います。
CT_split_sql
コンテナは、CT_Sql
コンテナと非常に
良く似ています。
違いは、シリアル化されたデータが指定したバイト数を超えた場合、
フィールド全体を記憶するために複数の行を使用することです。
テーブルの構成が異なり、また、種々のデータベース実装での予約語と
衝突することを避けるための列名が異なっているため、このコンテナは、CT_Sql
と互換ではありません。
このコンテナは、クラスのように DB_Sql
を使用します。
このため、このコンテナで全てのサポートされるデータベースをアクセスすることが可能です。
database_table | 使用するデータベースのテーブル名 |
database_class | クラス名。CT_Sql はデータを保存・取得するためにこのクラスを使用します |
split_length | 数字。テーブルの各行に保存される最大バイト数を指定します。 |
|
コンテナに適当なパラメータを指定するためにサブクラスを使用して下さい。 通常、サブクラスは次のようになります。
class My_Sql extends CT_Split_Sql {
var $classname = "My_Sql";
var $database_table = "active_sessions_split";
var $database_class = "DB_Session";
var $split_length = 4096;
}
こうして、My_Sql をクラス Session で使用できるようになります。 このクラスを参照するには、"that_class" 変数に "My_Sql" を指定して下さい。
Session
クラスは、データベースにセッションデータを読み書きするために
少々の SQL を使用します。
セッションをデータベースの種類に依存しないようにするために、Session
は
現在記憶領域へのアクセスを常にコンテナクラスを通じて行います。
Session
がコンテナとして共有メモリを使用するようにするには、
CT_Shm
を使用して下さい。
max_sessions | このコンテナによりサポートされる最大同時セッション数。 |
shm_key | 使用する共有メモリセグメントのユニークな(重要!) キー。 |
shm_size | 共有メモリセグメントのサイズ。 セグメントが最初にアクセスされる際に、サイズが設定されます。 大量のセッション変数を使用しない場合には、 shm_size = max_sessions * 600 という式で計算した値で充分です。 |
|
コンテナに適当なパラメータを指定するためにサブクラスを使用して下さい。 通常、サブクラスは次のようになります。
class My_Shm extends CT_Shm {
var $classname = "My_Shm";
var $max_sessions = 500;
var $shm_key = 0x1234232;
var $shm_size = 64000;
}
こうして、My_Shm をクラス Session で使用できるようになります。 このクラスを参照するには、"that_class" 変数に "My_Shm" を指定して下さい。
Session
クラスは、データベースにセッションデータを読み書きするために
少々のSQLを使用します。
セッションをデータベースの種類に依存しないようにするために、Session
は
現在記憶領域へのアクセスを常にコンテナクラスを通じて行います。
Session
がコンテナとしてDBMデータベースファイルを使用するようにするには、
CT_Dbm
を使用して下さい。
dbm_file | dbmファイルのパス (既に存在している必要があり、かつ、 サーバープロセスにより書きこみ可能である必要があります) |
|
コンテナに適当なパラメータを指定するためにサブクラスを使用して下さい。 通常、サブクラスは次のようになります。
class My_Dbm extends CT_Dbm {
var $dbm_file = "data/session.dbm";
}
こうして、My_Dbm をクラス Session で使用できるようになります。 このクラスを参照するには、"that_class" 変数に "My_Dbm" を指定して下さい。
Session
クラスは、データベースにセッションデータを読み書きするために
少々の SQL を使用します。
セッションをデータベースの種類に依存しないようにするために、Session
は
現在記憶領域へのアクセスを常にコンテナクラスを通じて行います。
Session
がコンテナとして LDAP データベースを使用させるには、CT_Ldap
を使用して下さい。
ldap_host | 接続するLDAPサーバーのホスト名 |
ldap_port | LDAPサーバーのポート (LDAP デフォルトは 389) |
basedn | LDAPのbasedn |
rootdn | データベースを修正する際に必要な rootdn |
rootpw | rootdn のパスワード |
objclass | PHPLIBのデータに関するオブジェクトクラス |
|
コンテナに適当なパラメータを指定するためにサブクラスを使用して下さい。 通常、サブクラスは次のようになります。
class My_Ldap extends CT_Ldap {
var $classname = "My_Ldap";
var $ldap_host = "localhost";
var $ldap_port = 389;
var $basedn = "dc=your-domain, dc=com";
var $rootdn = "cn=root, dc=your-domain, dc=com";
var $rootpw = "secret";
var $objclass = "phplibdata";
}
こうして、My_Ldap をクラス Session で使用できるようになります。 このクラスを参照するには、"that_class" 変数に "My_Ldap" を指定して下さい。
セッションクラスは、グローバルな変数名の一覧を保持し、 これらの変数をデータ記憶用コンテナ(簡略化するため、これをコンテナと呼びます) からロードあるいは保存するための一連の関数を提供します。 名前のある変数は、スカラー変数(文字列、整数、小数)または配列となります。 オブジェクトも同様に処理され、そのクラス名とその (永続的な) スロットを列挙する2つの インスタンス変数の実装を提供します。
classname | シリアル化のヘルパー: このクラスの名前。 |
magic | ID生成の際に使用される秘密の文字列。これを変更して下さい! |
mode | セッションID伝達の方法。cookie またはget のどちらか。 |
fallback_mode | $mode が動作しない場合のセッションID伝達のモード。
$mode を cookie に、$fallback_mode を
get に設定して下さい。 |
lifetime | セッションクッキーの有効期間を分で、また、セッションクッキーを 使用するには 0 を指定します。 |
gc_time | ガーベッジ・コレクション設定パラメータ。以下を参照して下さい。 |
gc_probability | ガーベッジ・コレクション設定パラメータ。以下を参照して下さい。 |
allowcache | セッションページのキャッシングを制御します。no
(デフォルト値)とした場合、このページはHTTP/1.1またはHTTP/1.0のもとでキャッシュが行われません。
public に設定された場合、このページは HTTP/1.1 および HTTP/1.0 のもとでパブリックに
キャッシュが行われます。private が設定された場合、
このページは、HTTP/1.1 のもとでプライベートにキャッシュが行われ、HTTP/1.0 のもとではキャッシュ
が行われません。
|
allowcache_expires | キャッシュが許可された場合、このページはここで示された分数だけ キャッシュされます。 |
that_class | Session は、データを保存および取得する際にこのクラス名を使用します。 |
auto_init | セッション作成の際にロードされるファイル。 |
secure_auto_init | もし全てのページが常に page_close() をコールするならば、0 に設定して下さい。 (通常はこのようなことはありません!) |
|
pt | 永続化変数の名前を保持する内部配列。 |
in | フラグ: 設定された場合、auto_init が実行されています。 |
name | セッション型のタグ(名前)。 |
id | 現在のセッションのID。 |
that | コンテナのオブジェクトインスタンス |
|
変数名で指定したグローバル変数をセッション変数として登録します。 変数名には、スカラー変数、配列、オブジェクトを指定できます。 オブジェクトが永続化オブジェクトとして作成されている場合、 以下の2つのインスタンス変数を有している必要があります。
オブジェクトクラスの名前を表す文字列。
保存する全てのオブジェクトスロットの名前を有する配列。
セッション変数として登録したグローバル変数名の登録を解除します。 指定した変数はただちに削除されず、ページの処理完了後に消去されます。 この後、データベースに保存することはできなくなります。
$varname という名前の変数がセッションに登録されている場合に true を返し、 その他の場合に false を返します。
現在のセッションを破棄し、現在のセッションIDセッションを用いて put_id()を行います。
delete()
が実行された後では、
全てのセッションデータはデータベースから削除されています。
また、セッションオブジェクトはこのページで使用できなくなります。
結果として、page_close()
はこのセッションではコール
できなくなります。delete()
を実行した後も
セッション変数はこのページでまだ使用可能ですが、
この後のページでは消去されます。
クッキーモードでは、HTML を出力しておらず新規のクッキーを設定できる場合、
delete()
をコールした後でpage_open()
により新規セッションを設定することができます。
これを行うことにより、過去のセッション変数のいくつかを再登録することも
可能です。
また、新規セッションを定義するためにpage_close()
をコールする
ことも可能です。
これにより、セッションを直ちに変更したり、
前のセッションからセッションデータを選択して受け渡すことが
可能になります。
現在のセッションを指すURLを返します。
get
モードを使用している場合、
現在のセッションIDがこのURLに付加されます。
そうでない場合、URLがそのまま返されます。
print $this->url($url);
の省略形。
PHP_SELF
および QUERY_STRING
情報から
現在のページを指すURLを返します。
get
モードの場合、セッションIDが付加されます。
print $this->self_url()
の省略形。
セッション名およびセッションIDを有する hidden フォーム要素を返します。
GETクエリーのパラメータとして現在のURLに付加する文字列を返します。 次のように使用することを意図しています。
<a href="<<?
$sess->pself_url().$sess->padd_query(array("again"=>"yes"))
?>"> Reload</a> and log in?
print $this-> add_query($qarray)
の省略形です。
ある FORM
変数が永続化されているときに、
そのフォーム変数は PHP にインポートされ、そして page_open() がコールされ、
その新しい変数の値はデータベースから取得された値で上書きされます。
結果として、FORM
の値は失われます。
track_vars
を有効にし、HTTP_GET_VARS
に直接
アクセスすれば、上記の問題は解決できます。この方法を推奨します。
しかし、いくつかの古いスクリプトは、永続的な FORM
変数に基づいています。
これらのスクリプトは、適当な reimport
_x_vars()
関数を
コールすることも可能です。
これらの関数は、追跡を行う変数の配列を再び読みこみ、
セッション変数が取得された後で、適当なグローバル変数を再び初期化
します。
この関数の使用は推奨されません。
reimport_get_vars()
を参照下さい。
reimport_get_vars()
を参照下さい。
この関数を直接コールすることは避けるべきです。
この関数は、Session()
の初期化時にstart()
関数により
コールバックされます。
動作を理解した人がSession
のサブクラスでこの実装を上書きすることが
できるように記述されています。
この関数は、セッションのthisインスタンスで使用されるコンテナクラスを 作成し、開始します。
この関数を直接コールするべきではありません。
この関数は、Session()
の初期化の際にstart()
関数により
コールバックされます。
動作が理解できた場合は、
Session
のサブクラスでその実装を上書きすることも可能なように
記述されています。
この関数は、内部的なセッション名を定義し、設定します。
この関数を直接コールするべきではありません。
この関数は、Session()
の初期化の際にstart()
関数により
コールバックされます。
動作が理解できた場合は、
Session
のサブクラスでその実装を上書きすることも可能なように
記述されています。
この関数は、現在のセッションの伝達の方法を定義し、 新規のセッショントークンを生成する必要があるかどうかを定義します。
この関数を直接コールするべきではありません。
この関数は、Session()
の初期化の際にstart()
関数により
コールバックされます。
動作が理解できた場合は、
Session
のサブクラスでその実装を上書きすることも可能なように
記述されています。
この関数は、セッションにより生成されるヘッダー行(キャッシュ制御用のヘッダを含む) を定義します。
get_id()
を参照下さい。
get_id() は、セッションIDを定義するために内部的に 使用されます。現在、セッションIDは32文字の16進数(128ビット)であり、 推定を困難とするためにmd5(uniqid($this->magic))により生成されます。
get_id() はパラメータとして使用するためにオプションでセッションIDを 付けてコールすることが可能です。この機能は、 セッションを壊すことなくセッションIDを変更したい場合に便利です。
セッションIDの作成方法を別のものに変更したい場合には、 get_id() をサブクラスにより上書きすることが可能です。 例えば、(例えばゲストブックといった)いくつかのアプリケーションでは、 永続的な変数の共有領域を使用するために クライアントに伝播されない固定したセッションIDを使用することが望まれます。 これらのアプリケーションは、(間もなく実装される)ロックを行うことが必要です。
put_id() はあるセッションを"使用不可"とする ために内部的に使用されます。 現在、この関数はクライアント側のクッキーを削除し、 そのクッキーに関する $HTTP_COOKIE_VAR[$this->name] を削除します。 変数 ${$this->name} は削除されません。
serialize() は、 prefix という名前の変数を再構築するために必要な PHP コードを strに付加するために内部的に使用されます。
freeze() は、 register() が行われた全ての変数を並べて 現在のセッションIDおよびセッション名をタグとして 結果をデータベースに書きこみます。
thaw() は、データベースから 現在のセッションIDおよびセッション名で freeze() が行われた一連の変数をロードし、それらの変数を再度作成します。
active_sessions
テーブルは、各セッション毎に1行分のデータを有しています。
その行は、sid
および name
の値で一意に指定することが可能です。
(name
は、その行を書いたセッションクラスの名前です。)
行が書きこまれる度に、カラム changed
が現在の時間で更新されます。
gc() 関数は、分単位の時間gc_time
よりも古く、 name
フィールドが
一致する全ての行を削除します。
応答を早くするために、gc() は active_sessions
への更新が行われる度に
コールされず、代わりに gc_probability
の確率でランダムにコールされます。
3つの reimport 関数を実装するために使用されます。
初期化関数であり、オブジェクトのインスタンス作成後にコールされます。
この関数は、get_id() により現在のセッションIDを取得し、データベースに接続した後、
thaw() をコールして全てのセッション変数をロードします。
gc() をランダムにアクティブにします。
ブラウザのキャッシュを制御する際に適当なヘッダーを送るために
allowcache
を確認します。
使用するセッション用の適当なパラメータを与えるためにサブクラスを使用して下さい。 通常、サブクラスは次のようになります。
class My_Session extends Session {
var $classname = "My_Session"; ## 永続化をサポートする
var $mode = "cookie";
var $lifetime = 0; ## セッションクッキーを使用する
## 使用するコンテナの種類
var $that_class = "Session_sql";
}
DB_Sql
のサブクラスを定義する際に、
データベースにアクセスする際に必要なパラメータを指定する
必要があったことを思い出してください。
セッションサブクラスを使用するには(上述の)ページ管理用関数
を使用して下さい。セッション管理用の機能の名前は、sess
です。sess 機能へのパラメータとして次のように
セッションサブクラスの名前を指定してください。
page_open(array("sess" => "My_Session"));
永続化変数を登録するには、
インスタンスメソッドregister()
を使用して下さい。
$sess
がセッションオブジェクトの場合、
永続的なグローバル変数$s
を作成するには、次のように
して下さい。
$sess->register("s");
$s
は、スカラー、配列、永続性をサポートするスロットを有する
オブジェクトとすることが可能です。
インスタンスメソッド freeze()
および thaw()
を
直接使用してはいけません。代わりに
ページ管理用関数を使用して下さい。
いくつかのページはキャッシュされ、その他のページはキャッシュされない場合、 セッションオブジェクトのインスタンスを複数使用して下さい。 例えば、キャッシュされる必要があるページでは、次のようなセッションオブジェクト インスタンスを使用して下さい。
class My_Cached_Session extends My_Session {
## このセッションインスタンスを使用するページはキャッシュされます。
var $allowcache = "private";
}
キャッシュオプション public
を使用する際には注意して下さい。
パブリックにキャッシュされたページは、未認証のユーザーにもアクセスできる
可能性があります。
キャッシュオプション private
は未認証のアクセスを防止しますが、
HTTP/1.1 対応のブラウザでしか動作しません。
Session のサブクラスにおいてインクルードファイルの名前を
$sess->auto_init
に定義することも可能です。
慣習により名前 setup.inc
が使用されます。
class My_Session extends Session {
var $classname = "My_Session";
var $magic = "Calvin+Hobbes";
var $mode = "cookie";
var $gc_probability = 5;
var $auto_init = "setup.inc"; // auto_init ファイルの名前。
}
新規セッションが開始される度、つまり、
セッションIDがないユーザーがアプリケーションに接続を行う度に、
auto_init ファイルが読みこまれ、1度だけ実行されます。
このファイルは、グローバルコンテキスト中ではなく、
page_open()
のコンテキストの中から実行されます。
auto_init ファイルからグローバル変数を定義あるいはアクセスするには、
global
を指定する必要があります。
auto_init が実行された場合、そのページの全機能は既に定義されており、
グローバルに利用可能です。
これにより、アプリケーションで使用する変数
$sess
, $auth
, $perm
,
$user
が存在することを前提にすることが可能です。
どのページが auto_init を起動したのかを知ることは通常できないことに
注意して下さい。
認証を要求するページが複数あり、認証を行わないページもある場合、
一般に $auth
が存在することを前提にすることはできず、
その変数にアクセスする前にis_object($auth)
により
存在を確認する必要があります。
auto_init ファイルを初期化を行う際に使用する場所に置き、
使用する全てのセッション変数を登録して下さい。
setup.inc
の一例は次のようになります。
<?php
global $lang; // アプリケーション言語
$lang = "de"; // デフォルトの言語はドイツ語
$sess->register("lang");
global $cur; // アプリケーションの通貨
$cur = "EUR"; // デフォルトの通貨はユーロ
$sess->register("cur");
global $cart;
$cart = new Shop_Cart; // local.inc で定義されたショッピングカートオブジェクトを作成
$sess->register("cart"); // セッション変数を登録します
?>
注意:
fallback_mode を使用しておらず、ユーザークッキーをオフにした場合、
アプリケーションの各ページをヒットする度に新しいセッションを開始することを
ユーザーは強制されます。
もちろん、各ページを参照する度にsetup.inc
を読みこんで実行する
ことにもなります。この問題に対処することは不可能です。
永続化変数を削除するには、その変数の名前を指定して$sess->unregister()
をコールします。unregister をコールした後も以前に登録した変数の値はまだ利用可能
ですが、この変数はもはや永続化変数ではなく、現在のページを終了した時点で
失われます。
データベース内のセッションレコード、現在のセッションID、
ユーザーのブラウザにあるセッションクッキーを含むセッションに関係する全ての
データを削除するには、$sess->delete()
をコールして下さい。
ショッピングアプリケーションにおいては、買いたい品物を引き渡すために
現在の買い物カゴおよびそれ以外の全てを取り除く際に
このようなことが一般に行われます。
しかしながら、ユーザーが選択した情報を以下のように記憶したいと思うかもしれません。
<?php
page_open(array("sess" => "Shop_Session"));
// 注文をメールで送ります
mail_order($shopowner, $user, $cart);
// 現在のセッションを削除します
$sess->delete();
// 新規にセッションIDを取得しますが、ユーザーのアドレスと名前は前の値を
// 保持します:
page_open(array("sess" => "Shop_Session")); // auto_init を強制的に再実行します!
$sess->register("user"); // 同様にauto_initでも行うことが可能です。
?>
PHPLIBアプリケーションをデバッグする際に、active_sessionテーブルの
内容を読んで理解することができると便利なことが多いです。
このテーブルでは、各セッションが1行分のデータとして表されています。
このテーブルのプライマリ・キーは、name
および sid
の組です。
name
は $this->name
の内容であり、通常は
使用するセッションクラスのクラス名です。
sid
は $this->id
の内容であり、
通常は uniqid による MD5 ハッシュおよび他のユニークな文字列です。
ある組み合わせを選ぶことにより PHPLIB でアプリ―ケーション毎に
複数のセッション型(例えばセッションとユーザーデータ、以下に示す User
クラスを参照) を保持し、全てのデータを1つのテーブルに保存することが可能です。
Example_Session
のようなセッションクラスをデバッグする場合には、
関連するレコードは、name = "Example_Session"
となるものに限定されます。
$sess->id
を出力することにより、
Example_Session
の現在のセッションIDを定義し、
データベースから name
と sid
を有するレコードを
選択して下さい。
changed
フィールドはこのレコードが最後に更新された時間を
示しています。このフィールドは YYYYMMDDhhmmss というフォーマットの
14 文字 (Y2K対応) の文字列です。
changed
で降順にソートすることにより、
最新のセッションレコードを先頭に表示することが可能です。
(ここでは、MySQL の "limit" 文を使うと便利です)
セッションレコードの val
列には、
安全のため最初に stripslashes()
を実行し、その後、
eval()
を実行する PHP プログラムが含まれています。
この PHP プログラムは、複数の代入文からなり、
永続化変数を再生成する際に必要な全ての命令を有しています。
構造とこのプログラムにおける命令の順番は、常に同じです。
最初のアイテムは常に $this->in
に代入されます。
1 に設定されている場合、auto_init はこのセッションで既に実行されています。
1 に設定されていない場合、auto_init はまだ実行されていません。
これは、auto_init ファイルがこのセッションに関して未定義である
ためであると推定できます。
$this->pt = array();
のようなコードを実行した後、
$this->pt["文字列"] = 1;
のように配列への代入を行います。
各「文字列」は、登録される変数の名前です。
変数の登録にはそれ自体に永続性があり、配列 $this->pt
により保存されます。
該当する変数が設定されていない場合でも、登録の解除またはセッションの削除が
行われるまで登録・保持することは可能です。
配列 pt の内容が、セッションに現在登録されている変数となっているか
を確認下さい。
最後に、変数の実際の内容が保存されます。
この保存は、配列 $GLOBALS にアクセスし、永続化変数を構成する
スカラー値を順番に作成することにより行われます。
スカラー値に関しては、
$GLOBALS[somevar] = "value";
のようなコードとなります。
配列については、まず $GLOBALS[someary] = array();
が生成されます。
続いて、配列を構成するスカラー値が次のようなコードで書きこまれます。
$GLOBALS[someary][index] = "value"
オブジェクトに関しては、オブジェクトインスタンスを作成するコード
が保存されます。
$GLOBALS[someobj] = new Classname;
"Classname" は、オブジェクト $classname
による
ものであり、コンストラクタが正常に動作する必要があります。
続いて、
オブジェクトの配列 persistent_slots
の内容に基づき、
次のように保存されるスカラーが書きこまれます。
$GLOBALS[someobj]->slot = "value";
データベースに保存された値を見たい場合には、
単にそのセッションに関する $GLOBALS
の値を見る
ことになります。
以下の情報は、ライブラリの開発者、つまり、PHPLIB の 内部的動作を変更しようとする者、にのみ適用されます。 このセクションを読みとばしても問題ありません。 ここで示すいくつかの情報は、PHP 言語に精通していることを必要とします。
sessionクラスの中心は、内部関数 serialize()
です。
この関数は、式 prefix を引数とし、実行時に式にその式の値を代入するような
PHP コードを生成します。
例えば、式が $GLOBALS["a"]
で、グローバル変数 $a
が値 17
を有するとすると、serializeは、PHP プログラム
$GLOBALS["a"] = "17";
を作成します。
メモリを節約するために、serialize()
は参照パラメータ
$str
に生成されたコードを付加する場所として実行します。
まず最初に serialize()
は、PHP関数 gettype()
を用いて
現在の式の型を定義します。
カレントの型は、$t
に保存されます。
式の型はスカラー値(整数、浮動小数、文字列)、配列、オブジェクトのどれであるかを
示しています。
スカラー値の場合は処理が最も容易です。serialize()
では、
現在の式を評価し、結果の値を $l
に記録するだけです。
現在の値を現在の式に代入するような代入式が生成されます。
現在の値が文字列となることもあり得ます。
その文字列に(バックスラッシュ、2重引用符、ドル記号といった)不適当な文字
が含まれている場合には、これらの文字の前にバックスラッシュが付加されます。
スカラーの場合、serialize()
の動作はこれで完了します。
$t
が配列の場合、空の配列を作成するコード
(expression = array();
) が生成されます。
つづいて現在の式のキーの数が計算され、各キーについて
キーを式に追加した上で serialize()
が再帰的にコールされます。
これにより、各配列スロットに関するコードが追加されます。
$t
がオブジェクトの場合、そのオブジェクトを作成する
コード (expression = new Classname;
) が生成されます。
PHP においては任意のオブジェクトに関してオブジェクトのクラス名
を知ることはできないため、serialize()
に処理されるオブジェクトは
classname
という名前のスロットを有している必要があります。
オブジェクトハンドラはオブジェクトスロット persistent_slots
の内容を調べ、各オブジェクトスロットについて
適当なプレフィックスを付けて再帰的に serialize()
をコールします。
serialize()
で使用される多くの式においては
変数名や場合によってはコードを可変とすることが必要となるため、
eval()
が大量に使用されます。
これにより、コードの可読性が悪化してしまいます。
認証管理はセッションを認証するために使用することが可能です。 この目的は、セッションのクライアント側にいるユーザーを特定することです。
認証は、(.htaccess で保護されたページをアクセスした際に ブラウザのポップアップウインドウを用いて行われる) HTTP 認証ではなく、 HTMLフォームを使用してインラインで行われます。 インライン認証は、HTTP 認証に比べて以下のような複数の利点があります。
classname | シリアル化の補助変数: このクラスの名前。 |
persistent_slots | シリアル化の補助変数:全ての永続的スロットの名前 |
lifetime | 認証解除するアイドル時間の上限。0 に設定すると認証は解除されない (セッションがアクティブの間) |
refresh | 認証情報(permsなどのような)が auth-refreshlogin()
メソッドを呼び出してデータベースから再読み込みされるまでの上限の時間。0 に設定すると
認証情報はログインステージでのみ読まれる。 |
mode | 認証モード:log または reg (以下を参照) |
database_class | クラス名。Auth はデータベースコネクションを作成する際 このクラスを使用する。 |
database_table | セッション変数を保存するのに用いるデータベーステーブル。 |
magic | uniqid を生成するのに用いられる任意の値 |
nobody | フラグ:true ならばデフォルト認証を使用する。 |
cancel login | ログインフォームをキャンセルするために用いることが できるボタンの名前。 |
|
db | 内部変数:データベースコネクションオブジェクトインスタンス |
auth | 内部変数:ユーザー認証情報、下記参照 |
in | 内部変数:デフォルト認証モードで使用される |
|
auth_loginform()
とauth_registerform
において使うことができる関数。
フォームタグにとって適切な"action="を返す。
auth_loginform()
とauth_registerform
で使用できる関数。
フォームタグにとって適切な "action=" を表示する。
現在の用いられているユーザー識別情報を、他のユーザー識別情報に変更するために使用できる関数。 以下のデフォルト認証の使用についての章と例を参照。
この関数は$this->auth
で認証情報を破棄します。
これによって次回保護されているページが読み込まれるときに、ユーザーに再度ログインすることを強制します。
$this->auth["uname"]
は保持されるため、
正しいユーザー名をデフォルトで利用可能です。
バージョン6以降:ユーザーに`nobody'の証明を付与するには、unauthへの最初のパラメーターとして
trueを渡します。これは $this->auth["uname"]
も変更します。
バージョン7.2 以降:$nobodyをこのメソッドに渡すことは止めてください。
この関数は $this->auth
中の全ての認証情報を破棄し、次回保護されている
ページを読み込むときにユーザーに再ログインを強制します。
ほとんどのアプリケーションでは、$this->unauth()
の方が使用されます。
バージョン6以降:
ユーザーに`nobody'の証明を付与するには、logoutへ最初のパラメーターとしてtrueを渡します。
これによりクラスの定義($nobody
)で指定した値へと戻されます。
logout()
はunauth()
を($nobody
を引数として)コールするので、
動作は同じです。
(ただし、logout()
は常に$this->auth["uname"]
をクリアーし、
authクラスの登録を解除します)
バージョン7.2 以降:$nobodyをこのメソッドに渡すことは止めてください。
現行の認証が無効であったり、期限切れのときにfalseを返します。 それ以外の場合は認証済み UID を返します。
この関数は、Auth のサブクラスでオーバーライドすることが可能です。 この関数は認証プロセスの先頭でコールされ、ログインフォームを表示せずに (必要な全ての情報をテレパシーを用いて導出して、あるいはクッキーを使用することで、 またあるいは死んだリスの近親相姦からユーザーIDを占って)ユーザー認証を行う機会を設定します。
この関数がUIDを返し場合、ユーザーは認証済みであり、auth_loginform()も auth_validatelogin()もコールされません。falseが返された場合、全て通常どおりの動作となります。
この関数は、Auth のサブクラスにおいてオーバーライドしなければなりません。 この関数は、ユーザー用のログイン画面を生成するHTMLを出力するべきです。 HTMLファイルを組み込む際にはinclude()文を使用することをお勧めします。
この関数はユーザーがauth_loginform()
で生成されたログインフォームを
送信する際にコールされます。この関数は、ユーザーの入力を検証しなければなりません。
ユーザーの認証に成功した場合、インスタンス変数$auth[]
の以下に示す
複数のフィールドを設定する必要があります。
ログインしたユーザーのユーザーIDを有する必要があります。
ログインしたユーザーのユーザー名を有する必要があります。
変更してはいけません。(フィールドは start()
によって維持されており、
ログインが終了する時間を保持します)
許可属性機能を使いたいなら、ここに認証済みのユーザーの許可属性を格納して おかなければなりません。 (ヒント:sybase と名前が衝突するため、"perm" は全てのデータベーステーブルに おいて"perms"と呼ばれます。この小さな差異を探しましょう!)
さらなる情報は以下の例を参照して下さい。
この関数は、各refresh
分おきにコールされます。
この関数は、auth_validatelogin()
メソッドによって配列auth
に
格納された認証情報を更新します。ユーザーがnobodyとしてログインした場合にはコールされません。
認証が成功したらtrue、そうでなければfalseを返します。 (即ち: userid はもはや有効ではありません。)
auth_doregister()を参照。
これらの関数は登録モードでのauth_loginform()
とauth_validatelogin()
のエイリアスです。
初期化関数であり、認証を行います。log
(ログイン)モードにあるときは、
ログイン画面を表示するためにauth_loginform()
がコールされます。
ログイン画面から入力値が送信された際にログインを検証するためにauth_validatelogin()
が
コールされます。検証が成功した場合、実際のページのコンテンツが表示され、
そうでなければ auth_loginform()
に戻ります。
reg
モードでは登録フォームを表示するためにauth_registerform()
が
コールされます。登録フォームから入力値が送信された際に、ユーザーを登録しセッションを検証する
ためにauth_doregister()
が呼び出されます。
登録が成功した場合、実際のページのコンテンツが表示され、そうでなければ
auth_registerform()
に戻ります。
認証クラスのパラメーターを指定したり、独自のauth_*
関数を実装するために
Auth
のサブクラスを使用します。
class My_Auth extends Auth {
var $classname = "My_Auth"; # オブジェクトのシリアル化サポート
var $lifetime = 15;
## 使用するDB_Sqlサブクラス及びテーブル
var $database_class = "DB_Session";
var $database_table = "auth_user";
## uidを推測しにくくするためのマジックの値。
var $magic = "Abracadabra";
## 独自のログインフォームを使用
function auth_loginform() {
global $sess;
include("loginform.ihtml");
}
function auth_validatelogin() {
global $username, $password; ## loginform.ihtml からのフォーム変数
## もし認証が失敗したら、loginform.ihtml はセットされた
## $this->auth["uname"] を見つけだし利用します。
$this->auth["uname"]=$username;
## 認証が失敗したときに返す値
$uid = false;
## このユーザー及びパスワードの組をデータベースでチェック
$query = sprintf(
"select * from %s where username = '%s' and password = '%s'",
$this->database_table,
addslashes($username),
addslashes($password)
);
$this->db->query($query);
## 該当するユーザーが見つかったら、uid と許可属性を取り出します
while($this->db->next_record()) {
## 必須
$uid = $this->db->f("uid");
## オプション。perm 機能用
$this->auth["perm"] = $this->db->f("perms");
## perm 機能を使う場合、この例にあるデータベーステーブル中の
## db フィールドが sysbase との名前の衝突のために "perms" と
## 呼ばれることに注意。
}
return $uid;
}
}
loginform.ihtml
にはログインフォームを描画するためのHTMLとPHPコードが
含まれています。
$this->auth["uname"]
は最初のログインの試行で空になり、
さらなるログイン試行へとにすすみます。
これをログイン試行の繰り返しを検知し、適切なエラーメッセージを表示するために使うことができます。
自分のフォームのaction属性を生成するために$this->url()
の結果を
表示しなければなりません。
例として配布されているloginform.ihtml
を参照してください。
認証サブクラスを使うためにページ管理関数(上記参照)を使用してください。
認証管理の機能名はauth
です。
サブクラスAuth
の名前をauth
機能へのパラメーターとして指定します。
auth
機能はsess
機能を必要とします。
page_open(array("sess" => "My_Session", "auth" => "My_Auth"));
多くのアプリケーションではページ上のある機能を保護するため$auth
及び$perm
オブジェクトを使用するということが必要となります。それと同時にそのページにアカウント無しのユーザーが
見ることのできる保護されていない部分を作ることも要求されます。これは一種のジレンマを生みます。
というのも、ページの機能性を保護するには$auth
及び$perm
オブジェクトが
必要ですが、デフォルトでログイン画面が表示されることは好ましくないからです。
デフォルト認証では、全ての許可属性チェックに常に失敗する特別なuid及びuname"nobody"を
提供することでこのジレンマを解決します。
nobody
フラグを設定した場合、$auth
はユーザーに認証を強制するログイン
画面を生成せず、nobody
として暗黙のうちに認証します。
アプリケーションは、IDを本来のユーザーIDへと変更するための、アカウントを所持する
ユーザーに対してログインボタンかそれに類するものを提供する必要があります。
デフォルト認証を使用するには、nobody
フラグをセットし、上で示したような
My_Auth
サブクラスを作成します。
(注意: 2段階に拡張する必要はありません。重要なのは nobody
フラグが
設定されていることだけです)
class My_Default_Auth extends My_Auth {
var $classname = "My_Default_Auth";
var $nobody = true;
}
デフォルト認証を使用したページを作成するには、ページ管理関数を使用します。
login_if()
関数を伴う再ログイン要求をチェックしてください。また、同じページに
再ログイン用のリンクを作成して下さい。
<?php
// デフォルト認証を使用
page_open(array("sess" => "My_Session", "auth" => "My_Default_Auth"));
$auth->login_if($again);
if ($auth->auth["uid"] == "nobody"):
?>
このページへ <A HREF="<?php $sess->purl("$PHP_SELF?again=yes") ?>">再ログイン</A>
<?php endif ?>
always does.
配布ファイル中のlocal.inc
にはサンプルとしてチャレンジ−レスポンス方式を用いる
Example_Challenge_Auth
という名前のクラスが含まれています。クライアントブラウザが
Javascriptをサポートしている場合、このクラスによるログイン画面はネットワークに平文の
パスワードを送信しません。クライアントがJavascriptをサポートしていない場合、
ログインは可能ですが、通常の Example_Auth
が常にそうするように、
パスワードは平文で送信されます。
Example_Challenge_Auth
の実装は、PHPとJavascriptの先進的な使用法を示しており、
また、このPHPlibクラスライブラリの柔軟性を示しています。チャレンジ−レスポンス認証方式は、
ライブラリのコードを全く変更することなしに、Auth
をサブクラス化することに
より、local.inc
の中に、完全かつ自然に実装されています。
Example_Challenge_Auth
にはcrloginform.ihtml
が含まれています。
また、md5.js
ファイルがウェブサーバのドキュメントルートディレクトリ上に
あることが必要です。このファイルには、Henri Torgemane 氏により作成されたMD5メッセージ
ダイジェストアルゴリズムが実装されています。この認証方式の背後にある基本的なアイデアは
単純です。$auth->auth_loginform()
はこのフォームに編入されるチャレンジ値を
生成し、この値はフォームの中に組み込まれます。ユーザーがフォームを送信する際に
MD5("username:password:challenge") が計算され、replyフィールドに代入されます。
パスワードフィールドは消去されます。サーバは受信したユーザー名、データベース中のパスワード、
既知のチャレンジ値から予期されるreplyの値を計算することができます。
そして予想されるreplyと実際のreply値を照合できます。それらが一致する場合、そのユーザーは
認証されます。
replyフィールドが空白でパスワードが入力されている場合、そのクライアントでJavascriptが使用 できないことがサーバー側で分かります。この場合にもそのユーザーは認証されますが、 ネットワーク上でパスワードが見えてしまうことになります。
このクラスは、Example_Auth
の代わりとなるものです。
この機能はもともとKristian Koehntopp氏がPHPLIBメイリングリスト用に書いたもので、 のちに文書に加えられました。
Auth
クラスの通常の使用法は?
通常、以下のようなコードを保護したいページの先頭に書きます:
<?php
page_open(array(
"sess" => "My_Session",
"auth" => "My_Auth"));
?>
<!-- あなたのコードをここに -->
<?php
page_close()
?>
$auth
の内部動作
このページにアクセスする際、まず最初にpage_open()
がコールされます。
page_open()
は、$auth
という名前のMy_Auth
の
インスタンスを生成し、初期化します。そして$auth
はあなたが認証されて
いないことを検出し(どのようにしてこの検出を行うかは以下で説明します)、
loginform.ihtml
を表示します。この後、$authの実行処理は終了します。
このため、<!-- あなたのコードをここに --> が再び実行、表示されることは
ありません。
ここで、ユーザーの画面には、loginform.ihtml
の画面が表示されています。
この画面はユーザーが本来アクセスしようとしていたページのURLにより表示されます。
loginformはそれ自体を指すaction URLを有しています。
ユーザーがログインフォームに書き込んでそれを送信したときには、まったく同じURLが要求され、
上記のpage_open()
が再度実行されます。しかし今回はユーザー名とパスワードが
送信されます。$auth
オブジェクトが作成され開始されると、これらの
パラメーターを検出、検証し、NULL値または有効なユーザーidという結果になります。
認証に失敗した場合、空のユーザーidを作成し、ログインフォームが再び表示され、
インタープリターは終了します。この時、<!-- あなたのコードをここに --> の部分は
実行されません。
UIDが返ってきた場合、そのセッションでUIDとタイムスタンプは永続化され、
$auth
はpage_open()
に制御を戻します。
page_open()
が終了した後、<!-- あなたのコードをここに --> の部分
が実行または表示されます。
ただし、パーミッションチェック($perm
)をオプションに指定
した場合には、表示されない可能性もあります。
その後、他のページや同じページを呼び出すと、セッションデータ中のUIDとタイムスタンプの
存在がチェックされます。UIDが存在し、タイムスタンプが適正であれば、UIDは維持され、
タイムスタンプは更新されます。page_close()
実行時に、両方ともユーザーの
データベースに送り返されます。
(注意: 認証によって保護されているページは、読込み専用としてアクセスしたときや、
タイムスタンプが更新されていない場合でもpage_close()
することが必須です)
($auth->logout()
や$auth->unauth()
コールされた時のように)
UIDが存在しなかったり、タイムスタンプが有効期限を超過している場合、$auth
は
ページの表示を中断し、再度ログインフォームを表示します。
$auth
オブジェクトを含むページに入場する唯一の方法は、自分のセッション
データ内にUID及び有効なタイムスタンプを持つことです。
(注意:これはデフォルト認証でも成り立ちます。これらはダミーのUIDとタイムスタンプを
ユーザーのセッションデータ中に作成します)
セッションクラスの名前を有するセッションクッキーがブラウザに保存されます。
これは、PHPLIBのコア機能を動作させる場合に、ブラウザとPHPLIBの間でやりとりされる
唯一のデータです。
セッションクッキーの値は、PHPLIBが生成したPHPコードを取得する際に
active_sessions
へのリファレンスとして使用されます。
page_open()
の処理の中で、このPHPコードはeval()
により
式として評価され、セッション変数が再生されます。
$auth
オブジェクトの部分は永続化されており、page_open()
の
$sess
部分が実行されたときに取得されます。
この操作はpage_open()
の$auth
パートが実行される直前に行われるので、
コールされた時に$auth
は永続化されているデータが存在することを前提に
することができます。
PHPLIBのソースを見ると、$auth
には$auth->auth[]
という名前のハッシュ型の
永続的スロットが1つだけあることが分かります。このハッシュは、uid
,exp
,
uname
という複数のスロットを持っています。
$auth->auth["auth"]
は現在認証されているユーザーIDであり、
$auth->auth["exp"]
はそのUIDに対応する現在アクティブな
有効期限のタイムスタンプ(Unix time_t 形式)です。
$auth->auth["uname"]
は、通常のPHPLIBのAuth
クラスで
使用されているものとは全く無関係です。しかし、提供されているデフォルトのAuth
サブクラスであるExample_Auth
のコンテキストとは関係があります。
したがって、$auth->auth["uid"] != false
であり、
time() < $auth->auth["exp"]
を有していれば、セッションは
認証済みということになります。
PHPLIB に含まれているオリジナルのAuth
クラスは、ログインフォームの
外観やUIDの送信元や送信手段について何の規定もしていません。
Auth
の中には、今のところ上記の二つの条件以外の何かをチェックする
コードがありません。作成者の責任において必要な条件を満たすことのできるよう
Authのサブクラスを修正して下さい。
Auth はログインフォームの表示が必要な場合に自身が持つ関数
$auth->auth_loginform()
をコールし、認証を行います。
しかし、残念なことにAuth自体においてはこの関数は空なので、実装を提供する
必要があります。
local.inc
中のAuth
のサブクラスExample_Auth
の標準的な実装は以下のとおりです:
function auth_loginform() {
include("loginform.ihtml");
}
そして、そのファイルにコードを作成します。このファイル用のサンプルコードが
提供されていいますが、そのコードに制限されるわけではなく、個々要求に合った
loginform.ihtml
を書くことができます。
ログインフォームへ情報が入力され、ユーザーから返信されると、Auth
は
$auth->auth_validatelogin()
をコールします。繰り返しますが、
Auth
自体ではこの関数は空なので、Auth
のみでは正しく
機能しません。
機能させるためには、Auth
のサブクラスを作成し、local.inc
内に$auth->auth_validatelogin()
の実装を提供しなくてはいけません。
この関数に実際にすべきことはAuth
それ自身とは全く関係ありません。
期待されているのは、ユーザー入力の認証データが不正な場合にはfalseを返し、
ユーザーが認証された場合にはユーザーIDを返すということだけです。そして
Auth
はセッションレコード中に適切なエントリー
((code>$auth->auth["uid"] 及び
$auth->auth["exp"]
)を生成するよう面倒を見ます。
local.inc
からExample_Auth
とExample_Default_Auth
とExample_Challenge_Auth
のクラスを除去して
(参考のためコピーをその辺に残しましょう)、local.inc
に自分のコードを
書いてください。。
この文書の冒頭で示したように、My_Auth
という名前のクラスのコードを書き、
page_open
をコールするときにauth
機能への引数として
その名前を後で使って下さい。
コードを作成する際には、PHPLIBで永続クラスを派生させるときの基本ルールに
従ってください。つまり、以下のようにします。
class My_Auth extends Auth {
var $classname = "My_Auth";
// $persistent_slotsを継承しており、修正の必要も無い
// 後のコードをここに挿入します
}
次に認証の有効期限を設定してください。認証の有効期限は、
$auth->auth["exp"]
の現在の値の将来的な生存時間を意味します。
また、データベースコネクタクラスとユーザー名とパスワードをチェックする際に
使用する予定のテーブルの名前を指定してください。
// このコードを上で示されたように挿入する
var $lifetime = 15;
var $database_class = "DB_Example";
var $database_table = "my_special_user_table";
// 残りのコードはここに
これまでの作業により、我々はMy_Auth
の基礎的な実装を手に入れました。
足りないのは、必要な関数auth_loginform()
とauth_validatelogin()
だけです。以下のauth_loginform()
の実装では、$sess
をコンテキスト内で
グローバル変数としています。(こうしたのは簡単だからです。)これにより、$sess
全体にアクセス可能となります。
また、($this
経由で)$auth
変数へアクセスすることができます。
function auth_loginform() {
global $sess;
include("loginform.ihtml");
}
このログインフォームは、認証をするのに必要な値を供給するためのユーザー用フォームを作成する
ためであれば何をしようと自由です。$sess
全体と$this
に関係するもの
には全てアクセスが可能です。
このログインフォームはユーザー用にいくつかの入力フィールドを表示します。
例えば、姓・名・パスワードなどです。
フォームが返信されたとき、auth_validatelogin()
がコールされます。
フォームの値はグローバル変数(もしくは$HTTP_x_VARS[]
)であり、
$auth->auth_validatelogin()
にインポートされなければなりません。
その上で、そのユーザー用のユニークなIDを生成する(もしくはfalse を返す)
ためにしなければならないことをするのは自由です。
ここで、given_name、surname、passwordという名前の入力フィールドを作成するとしましょう。
この場合、$given_name
、$surname
、$password
をグローバルにし、$uid
にfalseを設定してください。
その後、ユーザテーブルにアクセスし、データベースから
$given_name
、$surname
、$password
が指すユーザレコードを取得する際に必要なSQLを文を作成して下さい。
一致する$given_name
、$surname
、$password
が存在した
場合、クエリーは成功するでしょう。この場合、(given_name, surname)の組を厳密にユニークに
識別するuidを返してください。さもなくばfalseを返してください。
コードでは以下のようになります:
function auth_validatelogin() {
// 認証データをインポートする
global $given_name, $surname, $password;
$uid = false;
$query = sprintf("select uid
from %s
where given_name = '%s'
and surname = '%s'
and password = '%s'",
$this->database_table,
$given_name, $surname, $password);
// 私たちは、本当であれば、ここでaddslashes()を使うか、
// magic_quote を有効にしておくべきです
// $auth->db は私たちの DB_Example によるデータベース接続です。
$this->db->query($query);
// さあ結果をチェックしましょう。
while($this->db->next_record()) {
$uid = $this->db->f("uid");
}
// $uidがfalseであるか(結果なし)、
// カラムuidから最後に取得したデータを設定するかのいずれかです。
// とにかく設定し、制御を戻します
return $uid;
}
さて、これで完了し、使用可能です。 しかし、まだ改良の余地はあります。最初に許可属性データを取得していないので、 perm機能も同時に使いたい場合でも機能しません。
これは次のように簡単に変更できます。select uid
だけに実行する代わりに
select uid, perms
とクエリーを修正してください。もちろん、SQLを
適当に調整することにより望ましいpermカラムをコールすることもできます。
また、$uid
の代入の後に1行追加します。コードは次のようになります。
$uid = $this->db->f("uid");
$this->auth["perm"] = $this->db->f("perms");
このコードは$auth->auth[]
配列のperm
キーに、
取得したperms
の値を格納します。$perm
がコールされ、
ユーザーの現在の許可属性を探し始めた時に備えて、その位置に格納されつづけます。
別の可能な改良は、ログインを試み、認証の失敗が正確に行われた際
(auth_validatelogin()
がfalseを返し、再度ログインフォームが表示された時)
に表われます。ログインフォームが空白になっていますが、これは以前にgiven_name
とsurname
のフィールドに打ち込んだデータが記憶されていないからです。
入力したデータを記憶していれば簡単にこの値を返すことができ、ユーザーはそれを
修正することができます。
また、2度目、3度目...のログイン操作である場合、これを検知し、そのログインフォームの
どこかにユーザーのタイプミスを知らせる適切なエラーメッセージを表示することも可能です。
これらの値を格納するのに便利な場所は、永続化された$auth->auth
配列です。
標準のExample_Auth
はその値を格納するために
$auth->auth["uname"]
フィールドを使用しますが、公式に使用されて
いる三つのフィールドuid
, exp
,perm
のうちのどれかと
衝突しないことだけ気をつけていれば、あなたの好きなようにどのフィールドをいくつ使用しても
かまいません。
グローバル変数$given_name
と$surname
を
$sess->register("given_name")
と$sess->register("surname")
をコールすることによって永続変数にしようとしないでください。これらはフォーム変数なのです!
決してフォーム変数を永続化したり、インターネット経由の未認証のユーザーを信用したり
しないでください。
そして、以下のコードを "global" の行の直下に追加してください.
$this->auth["gname"] = $given_name;
$this->auth["sname"] = $surname;
それから、このloginform.ihtml中の2つの変数が適切な場所にあるかチェックしてください。
単純にユーザーが認証されているということを示す証明と考えてください。
各ユーザーにそれぞれ異なった証明が使用されるので、現在処理を行っているユーザーを特定できます。
UIDは、auth_user
テーブル(もしくは現在のアプリケーションにおいてそう
呼ばれているもの)のプライマリキーだと考えてください。(given_name, surname)の組
またはそれを合成したものは、プライマリキーとなる可能性があります。
これは内部UIDの外部的な可読な(そして時にはとても長い)表現です。
passwordフィールドは二つのキー候補のどちらに機能的に依存しています。
内部ユーザー ID は決してユーザーに見せてはいけません。(given_name, surname)の組の方が ユーザーにとってはるかに扱いやすく、覚えやすいのです。 (自分の名前を覚えていないユーザーはきっとどっちみちアプリケーションの残りの部分を 操作するような精神状態じゃないでしょう:-)
内部ユーザーIDは、アプリケーションの中で内部的にユーザーを識別する際に常に使用されます。 UIDは固定長であり、既知の形式及び構造を持っているので、それを前提にすることができます。 given_nameまたはsurnameは長さが未知で、任意の文字を含む可能性があるので、 内部的にユーザーを参照する用途としては好ましくありません。
page_open()
のuser
機能を利用するのであれば、つまり、
ユーザー変数を作成するならば、そうです。
User
クラスは実際にはSession
のサブクラスです。つまり、
ユーザー変数はセッション変数にそっくりなのです。それらもまたactive_sessions
に蓄積されます。唯一の違いは、セッションの名前が異なるというところです。
(local.inc
で提供されているクラスや名前を使用する場合、
セッションの名前はExample_Session
の代わりにExample_User
となります)
そして、Example_User
においては、認証済みユーザーのユーザーIDは
active_sessions
テーブル中のセッションIDとなります。これが、
md5(uniqid("abracadabra"))
形式のUIDを推奨する理由です。
許可属性(パーミッション)の管理は、認証付きのセッションに基づいています。この管理は、 あるページで要求される許可属性のセットと関連付けられています。 実際のページの内容は、許可属性が全て一致するユーザーに対してのみ見えるようになっています。 それ以外の全てのユーザーはあなたがデザインした画面を見せられます。
classname | シリアル化ヘルパ: クラスの名前 |
permissions | (name, permission bit) の組み合わせのハッシュです。 |
|
認証済みユーザーがrequired
で明示されている全ての権利を持っているかどうかを
チェックします。そうでない場合、perm_invalid()
がコールされます。
一つ以上の要求された権利又はユーザー権利が不正の場合、(permissionハッシュの中に
見つけることができなければ)perm_invalid()
が同様にコールされます。
check()
の使い方と同様に、ユーザーが適切な権限を持たなくても、セッションを
中断することはしません。この関数は、ユーザーが必要な権限を持っていたらtrueを、
そうでなければfalseを返します。
この関数は、指定したname
を有するSELECT
タグを返します。
このタグの中に$perm->permissions
から得られる全ての利用可能な
許可属性の値がOPTION
タグとして含まれます。
current
の値を指定した場合、current
に一致する許可属性の値は
SELECTED
となります。class
の値を指定した場合、タグには、
CSSスタイルシートクラスclassが指定されます。
全ての権限の論理和を計算し、(valid, or_result)
の組を返します。
validがtrueの場合、or_result
が提供されます。validがfalseの場合、
or_result
は未定義であり、一つ以上の権限が無かったことになれます。
これは重大なエラーであり、アプリケーションは直ちに停止させられるべきです。
アクセス例外のときにコールされます。does_have
はそのユーザーが実際に
持っている権利をリストした文字列です。must_have
はページが要求する権利です。
ここでは、permissionクラスへパラメータを指定し、perm_invalid
関数を実装するために
Perm
のサブクラスを使用します。
class My_Perm extends Perm {
var $classname = "My_Perm";
var $permissions = array (
"user" => 1,
"author" => 2,
"editor" => 4,
"moderator" => 8,
"admin" => 16
);
function perm_invalid($does_have, $must_have) {
global $perm, $auth, $sess;
include("perminvalid.ihtml");
}
}
permissionサブクラスを用いるために(前期の)ページ管理関数を使ってください。
許可属性管理のための機能の名前はperm
機能です。Perm
の
サブクラスの名前をperm
機能へのパラメータとして指定してください。
perm
機能はsess
機能及びauth
機能を必要とします。
page_open(array("sess" => "My_Session", "auth" => "My_Auth", "perm" => "My_Perm"));
ページを保護するためにcheck()
インスタンスメソッドを用いてください。
$perm->check("admin"); ## このページは管理者権限をもつユーザーだけのもの。
ページ上で保護された機能を作成するためにhave_perm()
を使ってください。
<?php
if ($perm->have_perm("admin")):
?>
<h1>管理者専用の機能</h1>
<?php
endif;
?>
Perm
のサブクラスは、許可情報をビットパターンに変換した配列
$permissions
を定義します。例えば、配布されているlocal.inc
で定義されているExample_Perm
では、
name
、author
、editor
、
supervisor
、admin
という名前を定義していますが、
これらは全て単一のビットセットを持つビットパターンへと変換されます。
ユーザーは、auth_user
テーブルのperms
列に、許可属性名の
コンマ区切りリスト(空白なし!)として任意の個数の許可属性を割り当てることができます。
そのユーザーの実際に有効な許可属性はこれらの許可属性のビットパターンの論理和に
よって決定されます。
ページでは、$perm->check()
関数に許可属性名のコンマ区切りリスト
(これも空白なし!)を指定し、任意の許可属性を要求することが出来ます。
必須の許可属性もまたこれらの許可属性のビットパターンの論理和を取ったものによって定義されます。
同様に、ページ関数は$perm->check()
により必要な権限により
保護することができます。
認証済みユーザーの有効な許可属性が必要な全てのビットセットを持っている場合、 保護されたページまたはページ関数へのアクセスが許可されます。 これはすなわち、ユーザーの実効許可属性と必須許可属性の論理積の結果が必要な許可属性と 等しいということです。
配布されたものの中にあるExample_Perm
で定義されている許可属性名においては、
ユーザーkris
はauth_user
テーブル中でadmin
として
定義されているかもしれません。
$perm->check("user,admin")
としてadmin,user
許可属性を要求するページには、このユーザーはアクセスできません。これは以下のように計算されます。
ユーザーの実効許可属性: admin
変換後: 16
ページで必要な許可属性: user,admin
変換後: 1 OR 16 == 17
許可属性のチェック:
実効許可属性 17
AND 必須許可属性 16
よって 16 & 17 = 16
必要な許可属性は 17 -> アクセス拒否
配布ファイルにあるExample_Perm
で定義されている許可属性の例は、
Atomic許可属性と呼ばれます。というのも、これらはたった一つのビットセットを
持つだけだからです。Atomic許可属性はあらゆる枠組みの中でも最も単純なもので、
容易に許可属性チェックが行えます。
user,admin
として保護されているページへアクセスするためには、少なくとも
user,admin
権限をauth_user
テーブルに保持していることが必要です。
許可属性の定義で用いられるこれとは別の共通の枠組みがinclusive permissions
(複合許可属性)です。この枠組みでは、それぞれの許可属性の定義は、その前のものが持つ全ての
ビットに加えて1つのビットを持ちます。例えば、
class Inclusive_Perm extends Perm {
var $classname = "Inclusive_Perm";
var $permissions = array(
"user" => 1,
"author" => 3,
"editor" => 7,
"supervisor" => 15,
"admin" => 31
);
}
上の定義は複合許可属性のセットを定義します。この例では、admin
許可属性を
持つユーザーkris
はeditor
許可属性を持つ保護されたページへ
容易にアクセスすることができます。これは以下のように計算されます。
ユーザーの実効許可属性: admin
変換後: 31
ページで必要な許可属性: editor
変換後: 7
許可属性のチェック:
実効許可属性: 31
AND 必須許可属性: 7
よって 31 & 7 = 7
必須許可属性は、7でなければならない -> アクセス許可
複合許可属性は取り扱いが簡単です。というのも、より上位のアクセスレベルを もつユーザーは、より低位のアクセスレベルを持つページまたはページ関数へ アクセスすることが出来るからです、
マシンの整数値の大きさの制限のために、最大31の許可属性レベルまでしか定義できません。
UserクラスはSessionクラスの拡張(サブクラス)です。グローバル変数名のリストを保持し、 これらの変数をデータベースに格納したり、そこから取り出すための一連の関数を提供しています。
セッション変数と異なり、ユーザー変数はユーザーがブラウザを停止し再起動したり、 別の場所へ移動した場合でも失われません。 (このような場合、セッションIDは失われ、セッション変数はセッションIDに依存するため 全て失われます)
ユーザー変数はユーザーがログインすることを要求します。というのも、これらは変数を 関連付けるためにユーザーIDが存在することに依存しているからです。
UserクラスはSessionクラスの拡張です。Sessionの全てのインスタンス変数及びインスタンスメソッド を持ちますが、いくつか異なる実装がなされています。この文書はこれらの相違点のみ説明します。
Session及びUserは、name
カラムの値が異なるため、データベース中で単一の
active_sessions
テーブルを共有することが問題なくできることに注意して下さい。
classname | シリアル化ヘルパ。このクラスの名前。 |
magic | Userでは無意味。 |
mode | Userでは無意味。 |
fallback_mode | Userでは無意味。 |
lifetime | Userでは無意味。代わりにAuthでの認証有効期限を参照。 |
gc_time | 動作しますが、Userでは使用されないでしょう。 |
gc_probability | 動作しますが、Userでは0を設定すべきです。 |
that_class | Userは、データの格納・取得の際にこのクラス名を用います。 |
auto_init | Userでは無意味。 |
secure_auto_init | Userでは無意味。 |
|
pt | 永続化変数の名前をもつ内部的な配列 |
name | セッション型のタグ(名前) |
id | 現在のセッションのID |
that | コンテナオブジェクトのインスタンス |
|
きちんと動作します。
きちんと動作します。
きちんと動作します。
Userにとって無意味です。
User にとって無意味です。
User にとって無意味です。
User にとって無意味です。
きちんと動作します。
きちんと動作します。
きちんと動作します。
これは、ページ管理関数によって提供されるユーザーIDに依存するスタブの実装で
しかありません。ページ管理関数は Auth
により準備される
$auth->auth["uid"]
を使うでしょう。
空です。User には役に立ちません。
きちんと動作します。
きちんと動作します。
きちんと動作します。
きちんと動作しますが、通常は使用されません。
きちんと動作します。
オブジェクトの初期化の後にコールされる初期化関数です。現在のセッションIDを取得する ためにget_id()をコールし、データベース接続を行い、そして全てのセッション変数を 読み込むためにthaw()をコールします。 注意: gc()の活性化はコメントアウトされています!ユーザー変数に対してgcを行いたい場合、 コメントを外してください。
ユーザー変数へ適切なパラメータを指定するには、サブクラスを用いてください。 サブクラスは通常以下のようになります。
class My_User extends User {
var $classname = "My_User"; ## 永続化をサポート
var $that_class = "CT_Sql";
}
データベースにアクセスするために必要なパラメータを指定したDB_Sql
サブクラスを提供する必要があることを忘れないでください。
Userのサブクラスを用いるためにページ管理関数(上記参照)を用いてください。
ユーザー変数の機能名はuser
です; user機能へのパラメータとして
作成したUserサブクラスの名前を指定して下さい。
page_open(array("sess" => "My_Session", "auth" => "My_Auth", "user" => "My_User"));
変数を永続的に登録するためには、register()
インスタンスメソッドを用いてください。
$user
がユーザーオブジェクトの場合、$u
を永続的グローバル変数
とするためには以下のようにして下さい。
$user->register("u");
$u
はスカラー変数、配列、永続化サポートを受けているオブジェクト
を指定することが可能です。
インスタンスメソッドfreeze()
及びthaw()
は直接使わず、
代わりにページ管理関数を使ってください。
注意:
デフォルトの認証及びユーザー変数を同時に使用すると問題が起きます。というのも、
User
はいかなるロックも行わないからです。これは、現在DB_Sql
に
移植性のあるロッキング機構が備わっていないためです。