PHP ReflectionClass hasMethod和__call()

时间:2011-04-09 19:14:48

标签: php reflection dynamic call magic-methods

我正在创建一个响应magic __call()方法的动态类。问题是,因为我在已经存在的框架(Kohana)之上构建它,它使用ReflectionClass::hasMethod检查类的方法是否存在,并且它似乎不会触发{{1}用于检查它是否存在的魔法。在这种情况下我该怎么办?似乎如果您动态添加方法(如__call()),它仍然无法“看到”它

2 个答案:

答案 0 :(得分:2)

如果没有更多细节,我不确定这是否足够,但您可以创建代理类来执行中间功能:

   class MyProxy {

    protected $_object  = null;
    protected $_methods = array();

    public function __construct($object) {
        if (!is_object($object)) {
            throw new InvalidArgumentException('$object must be an object');
        }
        $this->_object = $object;
    }

    public function __call($name, $arguments) {
        return $this->callMethod($name, $arguments);
    }

    public function setMethod($name, Closure $method) {
        $this->_methods[(string) $key] = $method;
    }

    public function callMethod($name, array $arguments) {
        if (isset($this->_methods[$name])) {
            return call_user_func_array($this->_methods[$name], $arguments);
        }
        return call_user_func_array(array($this->_object, $name), $arguments);
    }

}

通过调用$proxy->setMethod('foo', function () { });,您可以动态地“附加”对象的方法。当你致电$proxy->foo()时,它会首先查看动态附加的方法;如果它找到一个,它会调用它。否则,它只会委托给内部对象。

现在,这种方法的问题是附加方法没有绑定到代理。换句话说,$this在附加方法的范围内不存在。

这可以修复,使用PHP 5.4 +。

的功能
public function setMethod($name, Closure $method) {
    $this->_methods[(string) $name] = Closure::bind($method, $this);
}

我们已将setMethod改进为rebind the passed closure代理。现在,在附加方法的范围内,$this将指向代理对象。

我们可以将它反弹到封闭的对象,但随后附加的方法无法与代理(或其他附加方法)对话。为了完整起见,您需要添加__get__set magic,以转发对内部对象的属性访问/ mutate调用(或在代理中处理它们,无论

此外,内部对象对代理没有任何线索,因此它的方法(来自类定义的 )无论如何都不会知道任何这种动态魔法。

答案 1 :(得分:1)

  

好像你是动态添加方法(比如$this->{$name} = function(){})它仍然无法“看到”它

是的,因为您正在创建新的属性,而不是方法。在撰写本文时,PHP不支持在属性中调用匿名函数,而不通过__call,这不是反射友好的。 Properties-with-anonymous-functions-as-methods在PHP 5.4中正常工作。

以反射将采用的方式向类添加方法的唯一方法是使用highly experimental "runkit" extension