「PHPでログインしている人リストを表示する必要がある」 という話を聞いたので、 session.save_pathにあるファイルunserializeすればすぐじゃんとか思ったんですが、コレ、serializeの結果とsessionファイルの中身違くね?

Array
(
    [aaa] => 111
    [bbb] => 222
    [ccc] => book Object
        (
            [name] => namae
            [price] => 1000
            [releaeDate] => 2004/12/01
        )

)

こうゆう配列をserializeした結果、

a:3:{s:3:"aaa";s:3:"111";s:3:"bbb";s:3:"222";s:3:"ccc";O:4:"book":3:{s:4:"name";s:5:"namae";s:5:"price";i:1000;s:10:"releaeDate";s:10:"2004/12/01";}}

そして、実際の$_SESSIONに入れた場合のセッションファイルの中身、

aaa|s:3:"111";bbb|s:3:"222";ccc|O:4:"book":3:{s:4:"name";s:5:"namae";s:5:"price";i:1000;s:10:"releaeDate";s:10:"2004/12/01";}

なんか微妙にフォーマット違う。どういうこと?

PHP: コンストラクタの内部での参照 – Manual

注意: (PHP 4以降ではリファレンスカウンティングを使用しているため、)参 照ではなくコピーを返すことで性能が低下することはありません。逆に 多くの場合、参照を使うよりも単純にコピーを使った方が良い結果とな ります。これは、参照の作成には時間がかかりますが、コピーの作成に は理想的には時間が全くかからないからです。(ただし、大きな配列ま たはオブジェクトでその一つが変更されると、次々に参照先の他の要素 に参照先に波及するといった場合を除きます)

まじですか?要調査。

永続化がらみでPHPでアプリケーションスコープを扱うためのクラスを作ってみました。

ApplicationScope#setでアプリケーションスコープに変数をセット。

require_once("Book.php");
require_once("ApplicationScope.php");

$book =& new Book();
$book->name = "Effective Java";
$book->price = 1000;
$book->releaseDate = "2004/12/01";

$app =& new ApplicationScope();
$app->set("book", $book);
$app->set("mail", "bg@p0t.jp");
$app->set("age", 26);

ApplicationScope#getでアプリケーションスコープから変数を取得。

require_once("Book.php");
require_once("ApplicationScope.php");

$app =& new ApplicationScope();
$book =& $app->get("book");
$mail =& $app->get("mail");
$age =& $app->get("age");

var_dump($book);
var_dump($mail);
var_dump($age);

中身は結局ファイルにserializeです。デフォルトでsessionのディレクトリに一時ファイルを作ってます。setの時はその時点で書き込まれるので他のリクエストともやり取りできます。といってもアプリケーションスコープってどういう時に必要なんだろう。ASPやServletでも使ったこと無いです。

ApplicationScope-0.1.tgz

会社の五周年に楽天のKさんが来ていたのでPHP関連の質問を幾つかさせて頂きました。

Q1. PHPのセッションは重複するのか。(PHPのセッションID 参照)

A1. その理解の通り、1台では重複しないが複数台では重複する。

Q2. なぜPHPをApache2のようなマルチスレッドサーバで動かしてもおかしくならないのか。

A2. マルチスレッドでは動かない。Apache2で動かす場合はプロセスベースで動く設定にする。

その他にも楽天のDB構成やPythonの話などいろいろと聞かせてもらって勉強になりました。疑問がズバーン解決してとてもすっきりしました。

はてなダイアリー – Harukiからの平面波

しかし、クラスが外部ファイルで定義されている場合、以下のようにするとエラーが出るので注意する。

$obj = new foo; require_once ‘class.foo.php’;

これ、セッションに保存したオブジェクトを取り出すところでハマりました。 セッションからオブジェクトを復元するのにはunserialize()関数が使われているのでsession_start()より前にクラスファイルをrequireしとかないとPHPが何のオブジェクトなのか分からないので__PHP_Incomplete_Classになってしまいます。

取り出した物を見てみるとデータとしては完全にそろってる。でも何のクラスなのかわからないのでエラー。

これはガムを知らない民族の前に置いてあるガムみたいなものかと。おれらは目の前にあるものがガムクラスのインスタンスだと分かっているのでどんな振る舞いをするのかも知っている。しかしガムというものを知らない人の前に置かれたガムは謎のオブジェクト。ガムを知らないのでどんな振る舞いをするのかはさっぱりわからない、しかしデータだけは目の前にある。

「現実世界はそんなもの(クラスはわからないがインスタンスが目の前にある)ばかりなのでオブジェクト指向を無理矢理当てはめようとしても失敗する。」というのを何かで読んだ気がします。

手軽なPHPオブジェクト永続化のためのクラスStoredObjectを0.2にしました。

1オブジェクト=1ファイルだと10,000件の検索に5秒ぐらいかかるので1クラス=1ファイルにしました。(1オブジェクト=1行)

DataObjectよりシンプルにするためにfactoryを廃止。

下記は永続化のサンプル。

StoredObject#store()で保存。(保存先はデフォルトだと実行したディレクトリ。コンストラクタの第二パラメータでディレクトリ指定できます)

require_once("StoredObject.php");

class Book {
    var $name;
    var $price;
    var $releaseDate;
}

$storedObject =& new StoredObject("Book");

$book =& new Book();
$book->name = "軽快なJava";
$book->price = "1000";
$book->releaseDate = "2004/12/01";
$storedObject->store($book);

$book->name = "Effective C++";
$book->price = "2000";
$book->releaseDate = "2004/12/02";
$storedObject->store($book);

$book->name = "Programming Perl";
$book->price = "3000";
$book->releaseDate = "2004/12/03";
$storedObject->store($book);

StoreObject#get()で取ってきます。

require_once("StoredObject.php");

class Book {
    var $name;
    var $price;
    var $releaseDate;
}

$storedObject =& new StoredObject("Book");

$book1 =& $storedObject->get("name", "軽快なJava");
var_dump($book1);

$book2 =& $storedObject->get("name", "Effective C++");
var_dump($book2);

$book3 =& $storedObject->get("name", "Programming Perl");
var_dump($book3);

実行結果はこんな感じです。_StoredObject_hashというのはStoreObjectが勝手に割り当てているハッシュ値です。

object(book)(4) {
  ["name"]=>
  string(10) "軽快なJava" 
  ["price"]=>
  string(4) "1000" 
  ["releaseDate"]=>
  string(10) "2004/12/01" 
  ["_StoredObject_hash"]=>
  string(32) "8141cda9579ef74e052390de23770469" 
}
object(book)(4) {
  ["name"]=>
  string(13) "Effective C++" 
  ["price"]=>
  string(4) "2000" 
  ["releaseDate"]=>
  string(10) "2004/12/02" 
  ["_StoredObject_hash"]=>
  string(32) "2d37647c506ed9a84f3e2966afcf40e0" 
}
object(book)(4) {
  ["name"]=>
  string(16) "Programming Perl" 
  ["price"]=>
  string(4) "3000" 
  ["releaseDate"]=>
  string(10) "2004/12/03" 
  ["_StoredObject_hash"]=>
  string(32) "ec905200936acd1ae7ecdcc6e7a4099d" 
}

find()メソッドや正規表現での検索を追加予定です。

StoreObject-0.2.tgz

はてなダイアリー – Amethyst製作日記

一つ考えられる方法として、オブジェクト管理クラスを用意して、そのオブジェクト管理クラスは管理対象のオブジェクトをシリアライズしてローカルにファイルとして保存、読み出しを行えるようにするとか。

まぁ、ファイル読み書きのオーバーヘッドが発生しちゃうけど、現実的な実装方法なのかも知れない。

コレを見てPHPで手軽にオブジェクトの永続化ができたらいいなと思ったのでちょっと作ってみました。

早い話が設定やDBがいらないDB_DataObjectといったものです。

require_once("StoredObject.php");

class Book {
    var $name;
    var $price;
    var $releaseDate;
}

$book =& new Book();
$book->name = "Effective Java";
$book->price = "1000";
$book->releaseDate = "2004/12/01";

$storedBook =& StoredObject::factory("Book");
$storedBook->store($book);

クラス名でStoredObjectのfactoryを動かして、store()でオブジェクトを保存します。

require_once("StoredObject.php");

$book =& StoredObject::factory("Book");
$book->get("name", "Effective Java");
var_dump($book);

DataObjectライクに永続化していたオブジェクトを取得。

仕組みは簡単でクラス名で作成したディレクトリに1オブジェクトに対応する1ファイルを作ってserialize()保存しているだけです。取得するところはオブジェクトが増えると半端じゃなく遅くなると思いますが、何のインストールも設定ファイルもいらないのでプロトタイプの作成にはいいかもしれないです。

StoredObject-0.1.tgz

BMediaNode: PHPでのオブジェクトプーリング

かなり大げさになりますが、memcachedというデーモンを使うとPHPでもオブジェクトのプーリング・キャッシングは可能になるみたいです。PHP用のクライアントAPIはPECLに含まれています。

PHPでオブジェクトプーリングを実現するもの。なるほど、PHPだとリクエストごとに消えちゃうし、別リクエストでは別ものになっちゃうので常駐してるデーモンに確保しといてもらうって感じですか。

・・・Servletでいいじゃん。

先日、PHPのセッション絡みの障害があり、調べてみました。何かというと「セッションIDが重複することがあるのか」ということについて。

PHP4.3.9のソースで見てみるとextというディレクトリがあります。これは拡張機能関連のソースがある場所のようで、pgsqlやmingやxmlなどが入ってます。ここにsessionディレクトリもありました。。拡張といっても本当のコア機能以外は全部ここに入っているようです。

php-4.3.9/ext/session の中のsession.cを見てみるとphp_session_create_idという露骨な名前の関数が定義されています。中身は大雑把に見ると、時間と php_combined_lcg()をMD5でハッシュ化しているようです。php_combined_lcgをググってみると、

[PHP-users 11996] Re: 「他人の情報を表示するサイト」

php_combined_lcgはマルチプロセスサーバー(プロセスをフォームするサーバー) ではPIDをマルチスレッドサーバーではthread_idを利用して疑似乱数を生成してい ます。したがって、仮に、micro秒単位で同じ時刻にアクセスしても同じセッション IDは生成されません。

msessionはセッション情報を全てメモリに保持しているのでセッションID のコリージョンを検出しています

どうやらPHPのセッションIDは理論的に絶対に重複しないようです。でもこれって、Webサーバが複数ある状態で1つのDBにセッションを保存している場合、同じ時刻で偶然プロセスIDも一緒だったら重複しちゃうんじゃないでしょうか。もう少し調べてみたいです。

2005/05/17 追記: 一部間違いがありました。 ref: PHPのセッションID2

guesswork

button_guesswork.png

guessworkは、「ややこしいフレームワークは使いたくない」けど「なんでもありになりがちなPHPスクリプトをスッキリさせる指針が欲しい」と考えている人のための軽量なフレームワークです。

個人的に超注目のPHPフレームワークguesswork

何が超注目なのかっていうと、「サイトがかわいい」

まだVersion 0.0.1なのになんでこんなにかわいいんですか?