调用直接分配给对象属性的闭包

我希望能够直接调用一个闭包给对象的属性,而不必将闭包重新指派给一个variables,然后调用它。 这可能吗?

下面的代码不起作用,并导致Fatal error: Call to undefined method stdClass::callback()

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

从PHP7开始,你可以做

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

或使用Closure :: call() ,虽然这不适用于StdClass


在PHP7之前,你必须实现magic __call方法来拦截调用并调用callback(当然这不可能用于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); 

__call体内,因为这会在无限循环中触发__call

你可以通过在闭包上调用__invoke来做到这一点,因为这是对象用来performancefunction的神奇方法:

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

当然,如果callback是一个数组或一个string(在PHP中也可以是有效的callback函数),那么这种方法就行不通了 – 仅仅用于闭包和其他具有__invoke行为的对象。

PHP 7开始,您可以执行以下操作:

 ($obj->callback)(); 

这似乎有可能使用call_user_func()

 call_user_func($obj->callback); 

虽然不算高雅,但是……戈登所说​​的可能是唯一的出路。

那么,如果你真的坚持。 另一个解决方法是:

 $obj = new ArrayObject(array(),2); $obj->callback = function() { print "HelloWorld!"; }; $obj['callback'](); 

但这不是最好的语法。

但是,PHPparsing器总是将T_OBJECT_OPERATORIDENTIFIER (作为方法调用)对待,似乎没有解决方法->绕过方法表并访问属性。

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

首先,创build一个使属性可调用的特性:

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

然后,你可以在你的课堂上使用这个特质:

 class CallableStdClass extends stdClass { use CallableProperty; } 

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

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

PHP 7开始,可以使用call()方法call()闭包:

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

由于PHP 7可以对任意(...)expression式执行操作(如Korikulum所解释的):

 ($obj->callback)(); 

其他常见的PHP 5方法是:

  • 使用魔术方法__invoke() (由Brilliand解释)

     $obj->callback->__invoke(); 
  • 使用call_user_func()函数

     call_user_func($obj->callback); 
  • 在expression式中使用中间variables

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

每种方式都有自己的优点和缺点,但最激进和最权威的解决scheme仍然是戈登提出的解决scheme。

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

好吧,它应该被emphisized存储在一个variables的闭包,并调用该variables实际上是(奇怪)更快,这取决于通话量,它变得相当多,用xdebug(非常精确的测量),我们正在谈论1,5(使用variables的因子,而不是直接调用__invoke,所以只需将闭包存储在variables中并调用它。

这是成功调用对象属性作为闭包的另一种方法。
当你不想改变核心对象使用这个:

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

更新:

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

如果您使用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级上工作..

 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