PHPでプロトタイプベース(っぽい)オブジェクト指向

PHPでプロトタイプベースのオブジェクト指向をやってみる - #詰んでる日記を見て、PHPでのプロトタイプベースオブジェクト指向について考える。

PHP5.3から、無名関数が使えるので、直感的に書くとこんな感じで、エラーになります。

<?php

$obj = new stdClass();
$obj->hoge = function(){
    echo "hogehoge";
};
$obj->hoge();

$ ./php-5.3.0/sapi/cli/php test1.php

Fatal error: Call to undefined method stdClass::hoge() in /home/yokkuns/work/php/php5_3/test1.php on line 6


これは、PHPのクラスは、プロパティとメソッドを別のハッシュテーブルで管理してるので、$obj->hoge() のように呼ばれると、メソッド用のハッシュテーブルを検索してしまうためです。

そこで、メソッドとしても呼べるように、__callメソッドを使って実装してみます。

<?php
class Prototype
{
    public function __call($method, $args){
        if(property_exists($this, $method)){
            return call_user_func_array($this->$method, $args);
        }
        return false;
    }
}

$obj = new Prototype();
$obj->hoge = function(){
    echo "hogehoge\n";
};
$obj->hoge();

$ ./php-5.3.0/sapi/cli/php test2.php
hogehoge

これで、期待通り、メソッドっぽく動かす事が出来ました!




しかし、まだ、問題があって、この無名関数を使った方法では、$thisを使うことが出来ません。

<?php

$obj = new Prototype();
$obj->foo = 'foo';
$obj->hoge = function(){
    echo $this->foo . "\n";
};
$obj->hoge();

$ ./php-5.3.0/sapi/cli/php test3.php

Fatal error: Using $this when not in object context in /home/yokkuns/work/php/php5_3/test3.php on line 15

$thisは、内部で各メソッドが第一引数として受け取ってるものなので、当然ですね。


なので、もし使いたいのであれば、第一引数(じゃなくてもいいけど)にオブジェクト自身を渡す関数を作成することになります。(サンプルで$SELFとなってるのは、$thisが予約語で使えないから)

<?php

$obj = new Prototype();
$obj->foo = 'foo'
$obj->hoge = function($SELF){
    echo $SELF->foo . "\n";
    $SELF->foo = 'hogehoge';
};

$obj->hoge($obj);
echo $obj->foo . "\n";

$ ./php-5.3.0/sapi/cli/php test4.php
foo
hogehoge

おぉ、これでやっと、PHPでプロトタイプベース(っぽい)オブジェクト指向が出来そうですね!

※追記(2009/08/13)

以下のように、無名関数のuseキーワードを使用すれば、第一引数にオブジェクト自身を渡す必要が無いです。

<?php 
$obj = new Prototype();
$obj->foo = 'foo';
$obj->hoge = function() use ($obj) {
echo $obj->foo . "\n";
$obj->foo = 'hogehoge';
};
$obj->hoge();
echo $obj->foo . "\n";

無名関数については、他の言語の先入観で、PHPのマニュアル全然見てませんでした。。。orz

id:t_komuraさん、ご指摘ありがとうございます!