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

Ken*_*ins 100 php closures properties object

我希望能够直接调用我分配给对象属性的闭包,而无需将闭包重新分配给变量然后调用它.这可能吗?

下面的代码不起作用和原因Fatal error: Call to undefined method stdClass::callback().

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback();
Run Code Online (Sandbox Code Playgroud)

Bri*_*and 101

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

$obj = new stdClass();
$obj->callback = function() {
    print "HelloWorld!";
};
$obj->callback->__invoke();
Run Code Online (Sandbox Code Playgroud)

当然,如果回调是一个数组或一个字符串(在PHP中也可以是有效的回调),这将无效 - 只是对于具有__invoke行为的闭包和其他对象.

  • @marcioAlmada虽然非常难看. (3认同)
  • @Mahn:当然,这不一致.但是,一致性从未真正成为PHP的强项.:)但是我认为当人们考虑对象性质时它确实有意义:`$ obj-> callback instanceof\Closure`. (3认同)
  • @cerebriform 从语义上讲,当人们期望它是 `$obj->callback()` 时,必须执行 `$obj->callback->__invoke();` 是没有意义的。这只是一致性的问题。 (2认同)

Gor*_*don 96

从PHP7开始,你可以做到

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

或者使用Closure :: call(),虽然这对a不起作用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');
Run Code Online (Sandbox Code Playgroud)

请注意,你做不到

return call_user_func_array(array($this, $method), $args);
Run Code Online (Sandbox Code Playgroud)

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

  • 有时你会发现这个语法很有用 - call_user_func_array($ this - > $ property,$ args); 当它是关于可调用的类属性,而不是一个方法. (2认同)

Kor*_*lum 22

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

($obj->callback)();
Run Code Online (Sandbox Code Playgroud)


Pek*_*ica 7

似乎可以使用call_user_func().

call_user_func($obj->callback);
Run Code Online (Sandbox Code Playgroud)

虽然不优雅,但@Gordon所说的可能是唯一可行的方法.


mar*_*rio 7

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

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

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

$obj['callback']();
Run Code Online (Sandbox Code Playgroud)

但这不是最好的语法.

然而,PHP解析器始终将T_OBJECT_OPERATOR,IDENTIFIER,(如方法调用.似乎没有办法->绕过方法表并访问属性.


Ste*_*veK 7

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

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

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

然后,您可以在您的类中使用该特征:

class CallableStdClass extends stdClass {
    use CallableProperty;
}
Run Code Online (Sandbox Code Playgroud)

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

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


Dan*_*ndo 7

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

$obj->callback->call($obj);
Run Code Online (Sandbox Code Playgroud)

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

($obj->callback)();
Run Code Online (Sandbox Code Playgroud)

其他常见的PHP 5方法是:

每种方式都有自己的优点和缺点,但最激进和最终的解决方案仍然是戈登提出的解决方案.

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();
Run Code Online (Sandbox Code Playgroud)