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

发布于 2024-10-09 09:37:34 字数 251 浏览 4 评论 0原文

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

下面的代码不起作用并导致致命错误:调用未定义的方法 stdClass::callback()

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

不回头走下去 2024-10-16 09:37:35

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

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

PHP 7 可以对任意 (... ) 表达式(如 Korikulum 所解释):

($obj->callback)();

其他常见的 PHP 5 方法是:

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

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

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

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

每种方式都有自己的优点和缺点,但最根本和最明确的解决方案仍然是 戈登

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

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

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

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

($obj->callback)();

Other common PHP 5 approaches are:

  • 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 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();
刘备忘录 2024-10-16 09:37:35

似乎可以使用call_user_func()

call_user_func($obj->callback);

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

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.

复古式 2024-10-16 09:37:35

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

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

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

$obj['callback']();

但这不是最好的语法。

但是,PHP 解析器始终将 T_OBJECT_OPERATORIDENTIFIER( 视为方法调用。似乎没有解决方法可以使 -> ; 绕过方法表并访问属性。

Well, if you really insist. 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.

聊慰 2024-10-16 09:37:35

我知道这已经很旧了,但我认为如果您使用 PHP 5.4+,Traits 可以很好地处理这个问题。

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

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

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
剧终人散尽 2024-10-16 09:37:35

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

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.

苏别ゝ 2024-10-16 09:37:35

这是基于已接受答案但直接扩展 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

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

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_func or __invoke though.

ㄟ。诗瑗 2024-10-16 09:37:35

更新:

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

PHP >= 7:

($obj->callback)();

PHP >= 5.4:

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

Updated:

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

PHP >= 7 :

($obj->callback)();

PHP >= 5.4 :

$callback = $obj->callback;  
$callback();
暗恋未遂 2024-10-16 09:37:35

如果您使用 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"

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"
私藏温柔 2024-10-16 09:37:35

我注意到这在 PHP5.5 中

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

有效 允许创建一个闭包的伪对象集合。

I note that this works in PHP5.5

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

Allows one to create a psuedo-object collection of closures.

淡莣 2024-10-16 09:37:34

从 PHP7 开始,您可以执行

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

或使用 Closure::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);

__call 主体中执行此操作,因为这会在无限循环中触发 __call

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.

画中仙 2024-10-16 09:37:34

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

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

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

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

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

逆光下的微笑 2024-10-16 09:37:34

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

($obj->callback)();

As of PHP 7 you can do the following:

($obj->callback)();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文