简体   繁体   中英

PHP ReflectionClass hasMethod and __call()

I'm creating a dynamic class that responds to magic __call() method. The problem is, since I'm building this on top of a already existing framework (Kohana), it checks if the method of the class exists using ReflectionClass::hasMethod , and it doesn't seem to trigger the __call() magic method for checking for it's existance. What could I do in this case? Seems like if you add the method dynamically (like $this->{$name} = function(){} ) it still can't "see" it

Without more details, I'm unsure if this would suffice, however you could create proxy class to perform intermediate functionality:

   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);
    }

}

By calling $proxy->setMethod('foo', function () { }); , you can dynamically "attach" methods to the object. When you call $proxy->foo() , it'll first do a look-up against the dynamically attached methods; if it finds one, it'll call it. Otherwise, it'll just delegate to the internal object.

Now, the problem with this approach is that attached methods aren't bound to the proxy. In other words, $this doesn't exist in the scope of an attached method.

This can be fixed though, with features from PHP 5.4+.

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

We've refined setMethod to rebind the passed closure to the proxy. Now, in the scope of an attached method, $this will point to the proxy object.

We could have rebound it to the enclosed object, but then the attached methods couldn't talk to the proxy ( or other attached methods ). For completeness, you'll want to add __get and __set magic, to forward property access/mutate calls to the internal object ( or handle them in the proxy, whatever )

Besides, the internal object has no clue about the proxy, so it's methods ( from the class definition ) won't know about any of this dynamic magic anyway.

Seems like if you add the method dynamically (like $this->{$name} = function(){}) it still can't "see" it

Right, as you're creating a new property , not a method. At time of writing, PHP did not support calling anonymous functions in properties without going through __call , which isn't Reflection-friendly. Properties-with-anonymous-functions-as-methods work properly in PHP 5.4.

The only other way to add a method to a class in a way that Reflection will pick up on is using the highly experimental "runkit" extension .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM