プラグイン機構を実装したいことがあって調べた。
基本的なやりかたとしては、
- 「メソッド名」と「コールバック」をひもづける配列を用意しておく
- __call()の中で、call_user_func_array()で追加したメソッドを実行する
- Closure::bindTo()で$thisをバインドする(コールバックから他のメソッドを呼び出すため)
という感じ。
以下のようになる。
test1.php
class Object
{
static private $_methods = array();
static public function addMethod($name, Closure $cb)
{
self::$_methods[$name] = $cb;
}
public function __call($name, array $args)
{
$func = self::$_methods[$name];
return call_user_func_array($func->bindTo($this, get_class($this)), $args);
}
public function add($a, $b)
{
return $a + $b;
}
}
foreach (array(10,100,1000) as $base) {
Object::addMethod('add'.$base, function($number) use ($base) {
return $this->add($number, $base);
});
}
$object = new Object();
echo $object->add10(1)."\n";
echo $object->add100(1)."\n";
echo $object->add1000(1)."\n";
Closure::bindTo()は、5.4からの機能なので、
もし5.3で同じように行うためには、
コールバックの引数の最初に$thisを追加するように対応する。
test2.php
<?php
class Object
{
static private $_methods = array();
static public function addMethod($name, Closure $cb)
{
self::$_methods[$name] = $cb;
}
public function __call($name, array $args)
{
array_unshift($args, $this);
return call_user_func_array(self::$_methods[$name], $args);
}
public function add($a, $b)
{
return $a + $b;
}
}
foreach (array(10,100,1000) as $base) {
Object::addMethod('add'.$base, function($self, $number) use ($base) {
return $self->add($number, $base);
});
}
$object = new Object();
echo $object->add10(1)."\n";
echo $object->add100(1)."\n";
echo $object->add1000(1)."\n";
Closure::bindTo()と違うのは、
privateのメソッドが呼び出せないところ。
参考
PHP 5.3でクラスにメソッドを動的に追加する
http://lab.tricorn.co.jp/toda/4082
PHPでメソッドを動的に追加する方法
http://qiita.com/suin/items/99c95c6b2182b54d9405