Calling closure assigned to object property directly

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();
39053 次浏览

As of PHP7, you can do

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

或者使用 闭包: : call (),尽管这在 StdClass上不起作用。


在 PHP7之前,您必须实现神奇的 __call方法来拦截调用并调用回调(当然,对于 StdClass来说这是不可能的,因为您不能添加 __call方法)

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

请注意,您不能这样做

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

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

使用 call_user_func()似乎是可行的。

call_user_func($obj->callback);

然而,这并不优雅... ... 戈登所说的可能是唯一的解决办法。

如果你坚持的话,另一个解决办法是:

$obj = new ArrayObject(array(),2);


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


$obj['callback']();

但这不是最好的语法。

但是,PHP 解析器总是将 T_OBJECT_OPERATORIDENTIFIER(作为方法调用。似乎没有解决办法可以让 ->绕过方法表来访问属性。

您可以通过对闭包调用 _ _ call 来实现这一点,因为这是对象用来表现得像函数一样的神奇方法:

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

当然,如果回调是数组或字符串(在 PHP 中也可以是有效的回调) ,那么这种方法就不起作用了——只适用于闭包和其他具有 _ _ 调用行为的对象。

well, it should be emphisized that storing the closure in a variable, and call the varible is actually (wierdly) faster, depending on the call amount, it becomes quite a lot, with xdebug (so very precise measuring), we are talking about 1,5 (the factor, by using a varible, instead of directly calling the __invoke. so instead , just store the closure in a varible and call it.

如果使用的是 PHP 5.4或更高版本,那么可以将一个可调用绑定到对象的作用域,以调用自定义行为。例如,如果要设置以下内容。.

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

你在一个类似这样的班级里做手术。

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

您可以运行自己的逻辑,就好像您是在对象的作用域内进行操作一样

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


echo $foo->getValue(); // prints "something else"

下面是另一个基于公认答案但直接扩展 stdClass 的替代方案:

class stdClassExt extends stdClass {
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}

用法例子:

$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();

你可能最好使用 call_user_func或者 __invoke

更新:

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

PHP > = 7:

($obj->callback)();

PHP > = 5.4:

$callback = $obj->callback;
$callback();

我知道这是老的,但我认为 Traits 很好地处理了这个问题,如果您正在使用 PHP 5.4 +

首先,创建使属性可调用的 trait:

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

然后,你可以在你的类中使用这个 trait:

class CallableStdClass extends stdClass {
use CallableProperty;
}

现在,您可以通过匿名函数定义属性并直接调用它们:

$foo = new CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4

PHP7 PHP7 开始,你可以做以下事情:

($obj->callback)();

由于 PHP 7 PHP 7 闭包可以使用 call()方法调用:

$obj->callback->call($obj);

Since 一个 href = “ https://wiki.PHP.net/rfc/united _ variable _ language”rel = “ norefrer”> PHP 7 is possible to execute operations on arbitrary (...) expressions too (as explained by 科里库伦):

($obj->callback)();

其他常见的 PHP5方法有:

  • 使用神奇的方法 __invoke()(如 太棒了所解释的)

    $obj->callback->__invoke();
    
  • using the call_user_func() function

    call_user_func($obj->callback);
    
  • using an intermediate variable in an expression

    ($_ = $obj->callback) && $_();
    

Each way has its own pros and cons, but the most radical and definitive solution still remains the one presented by Gordon.

class stdKlass
{
public function __call($method, $arguments)
{
// is_callable([$this, $method])
//   returns always true when __call() is defined.


// is_callable($this->$method)
//   triggers a "PHP Notice: Undefined property" in case of missing property.


if (isset($this->$method) && is_callable($this->$method)) {
return call_user_func($this->$method, ...$arguments);
}


// throw exception
}
}


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

我注意到这在 PHP5.5中是可行的

$a = array();
$a['callback'] = function() {
print "HelloWorld!";
};
$a['callback']();

允许创建闭包的 psuedo-object 集合。