如何调用作为类变量的闭包?

2022-08-30 08:02:58
class MyClass {
  var $lambda;
  function __construct() {
    $this->lambda = function() {echo 'hello world';};
    // no errors here, so I assume that this is legal
  }
}

$myInstance = new MyClass();
$myInstance->lambda();
//Fatal error: Call to undefined method MyClass::lambda()

那么到达类变量的正确语法是什么?


答案 1

在 PHP 中,方法和属性位于单独的命名空间中(可以具有相同名称的方法和属性),访问属性还是方法取决于用于执行此操作的语法。

$expr->something()是一个方法调用,所以 PHP 将在类的方法列表中进行搜索。something

$expr->something是一个属性获取,所以 PHP 将在类的属性列表中进行搜索。something

$myInstance->lambda();被解析为方法调用,因此 PHP 搜索类中命名的方法,但没有这样的方法(因此调用未定义的方法错误)。lambda

因此,您必须使用 fetch 属性语法来获取 lambda,然后调用它。

  • 从 PHP 7.0 开始,您可以使用以下命令执行此操作:($obj->lambda)()

    ($obj->lambda)();
    

    括号确保 PHP 解析为获取名为 lambda 的属性。然后,调用提取属性的结果。($obj->lambda)()

  • 或者您可以使用以下命令执行此操作:->lambda->__invoke()

    $myInstance = new MyClass();
    $myInstance->lambda->__invoke();
    

    __invokePHP的神奇方法之一。当对象实现此方法时,它将变得可调用:可以使用语法调用它。匿名函数是闭包的实例,它实现了 。$var()__invoke

  • 或者将其赋给局部变量:

    $lambda = $myInstance->lambda;
    $lambda();
    
  • 或者使用call_user_func来称呼它:

    call_user_func($myInstance->lambda);
    

    call_user_func可以调用任何可调用的函数,包括匿名函数。

  • 或者,如果这是代码中的常见模式,则可以设置一个方法来将调用转发到您的 lambda:__call

    class MyClass
    {
        private $lambda;
    
        public function __construct()
        {
            $this->lambda = function() {
                echo "Hello world!\n";
            };
        }
    
        public function __call($name, $args)
        {
            return call_user_func_array($this->$name, $args);
        }
    }
    

    现在这有效:

    $myInstance = new MyClass();
    $myInstance->lambda();
    

    从 PHP 5.4 开始,您甚至可以在 trait 中执行此操作:

    trait LambdasAsMethods
    {
        public function __call($name, $args)
        {
            return call_user_func_array($this->$name, $args);
        }
    }
    
    class MyClass
    {
        use LambdasAsMethods;
    
        private $lambda;
    
        public function __construct()
        {
            $this->lambda = function() {
                echo "Hello World!\n";
            };
        }
    }
    
    $myInstance = new MyClass();
    $myInstance->lambda();
    

答案 2

您还可以使用反射函数调用 lambda 函数,而无需更改类中的某些内容。

$myInstance = new MyClass();
$lambda = new ReflectionFunction($myInstance->lambda);
$lambda->invoke();

或者,如果您必须传递参数,则

$args = array('arg'=>'value');
$lambda->invokeArgs($args);

推荐