php4 と class PEAR

ふと思い出したのですが、PEARクラスはregister_shutdown_function()をつかってphp4でもデストラクタっぽいものをつくってるらしい。

PEAR.phpの209行目ふきんで

    /**
     * Destructor (the emulated type of...).  Does nothing right now,
     * but is included for forward compatibility, so subclass
     * destructors should always call it.
     *
     * See the note in the class desciption about output from
     * destructors.
     *
     * @access public
     * @return void
     */ 
    function _PEAR() {
        if ($this->_debug) {
            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
        }
    }   

ってのがコメントにあるとおりデストラクタ相当です。

コンストラクタであるfunction PEAR()では

 register_shutdown_function("_PEAR_call_destructors");

した上で、

 global $_PEAR_destructor_object_list;
 $_PEAR_destructor_object_list[] = &$this;

というかんじでPEARの継承クラスのインスタンスをがしがしつっこんでいます。で、_PEAR_call_destructors() でまとめてデストラクタを呼んであげる。

ここと _PEAR_call_destructor() にある while はちょっとおもしろくて、 is_a() つかえばよくね? って思うところですが、 PEAR を継承した class aho extends PEAR に必ずしも function _aho() が定義されてるとは限らないのでこんなふうに回してるようです。もっとも自分にちかい先祖のデストラクタを呼ぶようになっている。

あと、$_PEAR_destructor_object_list はけっこうでかい配列になるかもしれないので、うかつに foreach なんかにつっこんでコピーされたらたまりません。なので

while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
...
}

のように reset(), each() を使っています。オブジェクトのでかい配列を扱うときには参考になりそう。

ところで、実際このデストラクタを使っているのって何があるんでしょう? PEARディレクトリで

% grep -r 'function _[A-Z]' *

としてみたところ、こんなかんじ (関係ないの省略)。

Archive/Tar.php:    function _Archive_Tar()
DB/storage.php:    function _DB_storage()
PEAR/Common.php:    function _PEAR_Common()
PEAR/Registry.php:    function _PEAR_Registry()
...

Archie_Tar はリモート (http://) の gz ファイルをローカルにいったん保存してから gzopen() とかしてますが、そのローカルファイルの削除をデストラクタでやっているようです。そこのコメントがちょっとおもしろい。

// ----- Look if a local copy need to be erase
// Note that it might be interesting to keep the url for a time : ToDo
if ($this->_temp_tarname != '') {
    @unlink($this->_temp_tarname);
    $this->_temp_tarname = '';
}

たしかにとっておいてもいいけど、どういうタイミングで消すんだろう??