動的な関数追加を行えるクラス(PHP4/5両対応)

だーいぶ前に別のブログに書いた記事ですが、久しぶりに覗いてみたらまだ残ってたので、役に立つとも思えないけどサルベージしてみた。

<?php
class dynamic_func_class {
    function dynamic_func_class() {
        if (version_compare(PHP_VERSION, 5.0, '<'))
            overload(get_class($this));
    }
    function get_dynamic_func($class, $func) {
        return dynamic_func_class::ref_dynamic_func(false, $class, $func);
    }
    function set_dynamic_func($class, $func, $args, $code) {
        dynamic_func_class::ref_dynamic_func(true, $class, $func, $args, $code);
    }
    function remove_dynamic_func($class, $func) {
        dynamic_func_class::ref_dynamic_func(true, $class, $func, "", "");
    }
    function ref_dynamic_func($set, $class, $func, $args = null, $code = null) {
        static $_ = array();
        $class = strtolower($class);
        if ($set) {
            if (class_exists($class))
                $_[$class][$func] = create_function($args, $code);
        } else {
            if (empty($_[$class][$func])) {
                $class = strtolower(get_parent_class($class));
                if ($class != 'df')
                    return dynamic_func_class::get_dynamic_func($class, $func);
            }
            else
                return $_[$class][$func];
        }
    }
    function __call($func, $args) {
        $f = dynamic_func_class::get_dynamic_func(get_class($this), $func);
        if (is_callable($f))
            return call_user_func_array($f, $args);
    }
}
 
// 対象のクラスは上記のクラスを継承する必要がある
class A extends dynamic_func_class {
    function A() { parent::dynamic_func_class(); } // 基底クラスのコンストラクタを必ず呼ぶ
}
$a =& new A();
 
// メンバ関数を追加する前にコールしてみる
echo @$a->fn();
// php4 => 
// php5 => 
 
// メンバ関数を動的に追加してコールする
A::set_dynamic_func('A', 'fn', '$a', 'return $a * $a;'); // function fn($a) { return $a * $a; }
echo @$a->fn(2); // echo 2 * 2;
// php4 => (PHP4では戻り値を取れない)
// php5 => 4
 
// (C) 別の関数を同名で上書きする
A::set_dynamic_func('A', 'fn', '$a, $b', '$b = $a * $a;'); // function fn($a, $b) { $b = $a * $a; }
@$a->fn(3, &$c); // $c = 3 * 3;
echo $c;
// php4 => 9
// php5 => 9
 
// (D) 派生クラスにも追加したメンバ関数は有効
class B extends A {
    function B() { parent::A(); } // 基底クラスのコンストラクタを必ず呼ぶ
}
$b =& new B();
@$b->fn(4, &$d); // $d = 4 * 4;
echo $d;
// php4 => 16
// php5 => 16
 
// (E) 派生クラスでメンバ関数を無効にする
B::remove_dynamic_func('B', 'fn');
@$b->fn(5, &$e); // $e = 5 * 5;
echo $e;
// php4 => 
// php5 => 
 
// (F) 派生元ではまだ有効
@$a->fn(6, &$f);
echo $f;
// php4 => 36
// php5 => 36

うーん、素直にPHP5使うべきだ。