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

Posted

技术标签:

【中文标题】直接调用分配给对象属性的闭包【英文标题】:Calling closure assigned to object property directly 【发布时间】:2011-05-30 22:29:51 【问题描述】:

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

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

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

【问题讨论】:

这正是您所需要的:github.com/ptrofimov/jslikeobject 更多:您可以在闭包中使用 $this 并使用继承。只有 php>=5.4! 【参考方案1】:

我注意到这在 PHP5.5 中有效

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

允许创建闭包的伪对象集合。

【讨论】:

【参考方案2】:

更新:

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

PHP >= 7 :

($obj->callback)();

PHP >= 5.4:

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

【讨论】:

你试过了吗?它不起作用。 Call to undefined method AnyObject::callback()(当然存在 AnyObject 类。)【参考方案3】:

从 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

【讨论】:

有时你会发现这个语法很有用 - call_user_func_array($this->$property, $args);当它是关于可调用的类属性时,而不是方法。【参考方案4】:

由于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) && $_();

每种方式都有其优缺点,但最激进和最权威的解决方案仍然是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();

【讨论】:

【参考方案5】:

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

($obj->callback)();

【讨论】:

这样的常识,但它纯粹是坏蛋。 PHP7的强大功能!【参考方案6】:

我知道这是旧的,但如果您使用 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

【讨论】:

哇 :) 这比我尝试做的要优雅得多。我唯一的问题与英国热/冷水龙头连接器元件相同:为什么它还没有内置?? 谢谢:)。它可能不是内置的,因为它会产生歧义。想象一下,在我的示例中,如果实际上有一个名为“add”的函数和一个名为“add”的属性。括号的存在告诉 PHP 寻找具有该名称的函数。【参考方案7】:

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

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

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

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

【讨论】:

@marcioAlmada 非常丑陋。 @Mahn 我认为它比公认的答案更明确。在这种情况下,显式更好。如果你真的喜欢“可爱”的解决方案,call_user_func($obj->callback) 还不错。 但是call_user_func 也可以处理字符串,但这并不总是很方便 @cerebriform 从语义上讲,当人们期望它是 $obj->callback() 时,必须执行 $obj->callback->__invoke(); 是没有意义的。这只是一致性的问题。 @Mahn:当然,这并不一致。不过,一致性从来都不是 PHP 的强项。 :) 但我认为这是有道理的,当人们考虑对象性质时:$obj->callback instanceof \Closure【参考方案11】:

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

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

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

$obj['callback']();

但这不是最好的语法。

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

【讨论】:

【参考方案12】:

似乎可以使用call_user_func()

call_user_func($obj->callback);

虽然不优雅....@Gordon 所说的可能是唯一的出路。

【讨论】:

以上是关于直接调用分配给对象属性的闭包的主要内容,如果未能解决你的问题,请参考以下文章

Gradle语法之再解闭包

js 闭包 具体作用?

这个对象生命周期扩展闭包是 C# 编译器错误吗?

js闭包的理解

C# 闭包

Groovy闭包 Closure ( 闭包中调用 Groovy 脚本中的方法 | owner 与 delegate 区别 | 闭包中调用对象中的方法 )