Calling closure assigned to object property direct

2018-12-31 20:13发布

I would like to be able to call a closure that I assign to an object's property directly without reassigning the closure to a variable and then calling it. Is this possible?

The code below doesn't work and causes Fatal error: Call to undefined method stdClass::callback().

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback();

11条回答
看风景的人
2楼-- · 2018-12-31 21:02

Here is another way to successfully call object properties as closure.
When you don't want to change core object use this :

$obj = new AnyObject(); // with or without __invoke() method
$obj->callback = function() {
     return function () {
          print "HelloWorld!";
     };
};
$obj->callback();  

UPDATE:

$obj = new AnyObject(); // with or without __invoke() method
$obj->callback = function() {
     print "HelloWorld!";
};
$callback = $obj->callback;  
$callback();
查看更多
余生无你
3楼-- · 2018-12-31 21:04

I know this is old, but I think Traits nicely handle this problem if you are using PHP 5.4+

First, create a trait that makes properties callable:

trait CallableProperty {
    public function __call($method, $args) {
        if (property_exists($this, $method) && is_callable($this->$method)) {
            return call_user_func_array($this->$method, $args);
        }
    }
}

Then, you can use that trait in your classes:

class CallableStdClass extends stdClass {
    use CallableProperty;
}

Now, you can define properties via anonymous functions and call them directly:

$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
查看更多
大哥的爱人
4楼-- · 2018-12-31 21:05

As of PHP7, you can do

$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');

or use Closure::call(), though that doesn't work on a StdClass.


Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)

class Foo
{
    public function __call($method, $args)
    {
        if(is_callable(array($this, $method))) {
            return call_user_func_array($this->$method, $args);
        }
        // else throw exception
    }
}

$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');

Note that you cannot do

return call_user_func_array(array($this, $method), $args);

in the __call body, because this would trigger __call in an infinite loop.

查看更多
初与友歌
5楼-- · 2018-12-31 21:06

If you're using PHP 5.4 or above you could bind a callable to the scope of your object to invoke custom behavior. So for example if you were to have the following set up..

function run_method($object, Closure $method)
{
    $prop = uniqid();
    $object->$prop = \Closure::bind($method, $object, $object);
    $object->$prop->__invoke();
    unset($object->$prop);
}

And you were operating on a class like so..

class Foo
{
    private $value;
    public function getValue()
    {
        return $this->value;
    }
}

You could run your own logic as if you were operating from within the scope of your object

$foo = new Foo();
run_method($foo, function(){
    $this->value = 'something else';
});

echo $foo->getValue(); // prints "something else"
查看更多
爱死公子算了
6楼-- · 2018-12-31 21:07

It seems to be possible using call_user_func().

call_user_func($obj->callback);

not elegant, though.... What @Gordon says is probably the only way to go.

查看更多
登录 后发表回答