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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4535330/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 13:21:48  来源:igfitidea点击:

Calling closure assigned to object property directly

phpobjectpropertiesclosures

提问by Kendall Hopkins

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

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

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

采纳答案by Gordon

As of PHP7, you can do

从 PHP7 开始,你可以做

$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.

或使用Closure::call(),尽管这不适用于StdClass.



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

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

Note that you cannot do

请注意,您不能这样做

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

in the __callbody, because this would trigger __callin an infinite loop.

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

回答by Brilliand

You can do this by calling __invoke on the closure, since that's the magic method that objects use to behave like functions:

你可以通过在闭包上调用 __invoke 来做到这一点,因为这是对象用来表现得像函数的魔法方法:

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

Of course that won't work if the callback is an array or a string (which can also be valid callbacks in PHP) - just for closures and other objects with __invoke behavior.

当然,如果回调是数组或字符串(也可以是 PHP 中的有效回调),这将不起作用 - 仅适用于具有 __invoke 行为的闭包和其他对象。

回答by Korikulum

As of PHP 7you can do the following:

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

($obj->callback)();

回答by Daniele Orlando

Since PHP 7a closure can be called using the call()method:

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

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

Since PHP 7is possible to execute operations on arbitrary (...)expressions too (as explained by Korikulum):

由于PHP 7也可以对任意(...)表达式执行操作(如Korikulum 所述):

($obj->callback)();

Other common PHP 5approaches are:

其他常见的PHP 5方法是:

  • using the magic method __invoke()(as explained by Brilliand)

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

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

    ($_ = $obj->callback) && $_();
    
  • 使用魔法方法__invoke()(如Brilliand所解释的)

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

    call_user_func($obj->callback);
    
  • 在表达式中使用中间变量

    ($_ = $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();

回答by SteveK

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

我知道这很旧,但我认为如果您使用 PHP 5.4+,Traits 可以很好地解决这个问题

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

回答by Pekka

It seems to be possible using call_user_func().

似乎可以使用call_user_func().

call_user_func($obj->callback);

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

不过,这并不优雅......@Gordon 所说的可能是唯一的出路。

回答by mario

Well, if you reallyinsist. Another workaround would be:

好吧,如果你真的坚持。另一种解决方法是:

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

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

$obj['callback']();

But that's not the nicest syntax.

但这不是最好的语法。

However, the PHP parser always treats T_OBJECT_OPERATOR, IDENTIFIER, (as method call. There seems to be no workaround for making ->bypass the method table and access the attributes instead.

然而,PHP解析器始终将T_OBJECT_OPERATORIDENTIFIER(如方法调用。似乎没有解决方法可以->绕过方法表并访问属性。

回答by Kmtdk

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.

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

回答by M Rostami

Updated:

更新:

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

PHP >= 7 :

PHP >= 7 :

($obj->callback)();

PHP >= 5.4 :

PHP >= 5.4 :

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

回答by Mahn

Here's another alternative based on the accepted answer but extending stdClass directly:

这是基于接受的答案但直接扩展 stdClass 的另一种选择:

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

Usage example:

用法示例:

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

You are probably better off using call_user_funcor __invokethough.

您可能最好使用call_user_funcor__invoke虽然。