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

Php 调用直接分配给对象属性的闭包,php,object,properties,closures,Php,Object,Properties,Closures,我希望能够调用直接分配给对象属性的闭包,而无需将闭包重新分配给变量,然后调用它。这可能吗 下面的代码不起作用,并导致致命错误:调用未定义的方法stdClass::callback() 从PHP7开始,您可以 $obj = new StdClass; $obj->fn = function($arg) { return "Hello $arg"; }; echo ($obj->fn)('World'); 或者使用,尽管这在StdClass上不起作用 在PHP7之前,您必须实现mag

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

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

从PHP7开始,您可以

$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');
或者使用,尽管这在
StdClass
上不起作用


在PHP7之前,您必须实现magic
\u调用
方法来拦截调用并调用回调(当然,这对于StdClass是不可能的,因为您无法添加
\u调用
方法)

请注意,您不能这样做

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

\u调用
正文中,因为这将在无限循环中触发
\u调用

似乎可以使用
调用用户函数()

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

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

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

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

$obj['callback']();
但这不是最好的语法


然而,PHP解析器总是处理
T\u OBJECT\u操作符
标识符
作为方法调用。似乎没有办法让
->
绕过方法表而访问属性。

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

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

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

好吧,应该强调的是,将闭包存储在变量中,并调用变量实际上是(wierdly)更快,取决于呼叫量,它变得相当多,使用xdebug(非常精确的测量),我们谈论的是1,5(系数,通过使用变量,而不是直接调用u invoke。因此,相反,只需将闭包存储在变量中并调用它。

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

下面是另一个基于公认答案但直接扩展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 CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
用法示例:

$foo = new stdClassExt;
$foo->blub = 42;
$foo->whooho = function () { return 1; };
echo $foo->whooho();
您最好使用
调用用户函数
调用

更新:

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

($obj->callback)();
PHP>=5.4:

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

我知道这很旧,但如果您使用PHP5.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;
}
现在,您可以通过匿名函数定义属性并直接调用它们:

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 CallableStdClass();
$foo->add = function ($a, $b) { return $a + $b; };
$foo->add(2, 2); // 4
截至,您可以执行以下操作:

($obj->callback)();
由于可以使用以下方法调用闭包:

由于也可以对任意
(…)
表达式执行操作(如下所述):

其他常见的PHP5方法有:

  • 使用神奇的方法(如所述)

  • 使用函数

  • 在表达式中使用中间变量

    ($_ = $obj->callback) && $_();
    
每种方法都有各自的优缺点,但最激进、最确定的解决方案仍然是作者提出的


我注意到这在PHP5.5中起作用

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

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

这正是您需要的:甚至更多:您可以在闭包内部使用$This并使用继承。只有PHP>=5.4!有时您会发现此语法非常有用-call_user_func_array($This->$property,$args);当它是关于可调用的类属性,而不是一个方法时。@marcioAlmada非常难看。@Mahn我认为它比公认的答案更显式。在这种情况下显式更好。如果你真的喜欢“可爱”的解决方案,
调用用户函数($obj->callback)
没有那么糟糕。但是
call\u user\u func
也可以处理字符串,但并不总是这样convinient@cerebriform从语义上讲,当人们期望它是
$obj->callback()时,必须执行
$obj->callback->\uu invoke();
是没有意义的
。这只是一个一致性问题。@Mahn:当然,这是不一致的。但一致性从来都不是PHP的强项。:)但是我认为当考虑对象的本质时,它是有意义的:
$obj->callbackinstanceof\Closure
。你试过了吗?它不起作用。
调用未定义的方法AnyObject::callback()
(当然,类AnyObject存在。)哇:)这比我想做的要优雅得多。我唯一的问题与英国的热/冷抽头连接器元素相同:为什么它还没有内置?谢谢:)。它可能没有内置,因为它会产生歧义。想象一下,在我的示例中,如果真的有一个名为“add”的函数和一个名为“add”的属性。括号的出现告诉PHP寻找具有该名称的函数。这是一个常识,但它纯粹是个坏蛋。PHP7的强大功能!
($obj->callback)();
$obj->callback->__invoke();
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();
$a = array();
$a['callback'] = function() {
    print "HelloWorld!";
};
$a['callback']();