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

时间:2010-12-26 20:40:03

标签: php object properties closures

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

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

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

12 个答案:

答案 0 :(得分:99)

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

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

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

答案 1 :(得分:86)

从PHP7开始,你可以做到

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

或使用Closure::call(),但这不适用于StdClass


在PHP7之前,你必须实现魔术__call方法来拦截调用并调用回调(当然,StdClass是不可能的,因为你不能添加{{1}方法)

__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

答案 2 :(得分:16)

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

($obj->callback)();

答案 3 :(得分:7)

似乎可以使用call_user_func()

call_user_func($obj->callback);

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

答案 4 :(得分:7)

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

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

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

$obj['callback']();

但那不是最好的语法。

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

答案 5 :(得分: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

答案 6 :(得分:6)

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

答案 7 :(得分:3)

这是另一种成功调用对象属性作为闭包的方法 如果您不想更改核心对象,请使用:

$obj = new AnyObject(); // with or without __invoke() method
$obj->callback = function() {
     return function () {
          print "HelloWorld!";
     };
};
$obj->callback();  

<强>更新

$obj = new AnyObject(); // with or without __invoke() method
$obj->callback = function() {
     print "HelloWorld!";
};
$callback = $obj->callback;  
$callback();

答案 8 :(得分:2)

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

答案 9 :(得分:1)

这是另一种基于已接受答案的替代方案,但直接扩展了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

答案 10 :(得分:0)

如果您使用的是PHP 5.4或更高版本,则可以将callable绑定到对象的范围以调用自定义行为。例如,如果您要进行以下设置..

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"

答案 11 :(得分:0)

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

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

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