一级可调用语法

从 PHP 8.1.0 开始引入一级可调用语法,作为从 callable 创建匿名函数的一种方法。其取代了使用字符串和数组的现有 callable 语法。这种语法的优点是可以进行静态分析,并使用获得可调用对象的作用域。

CallableExpr(...) 语法用于从 callable 创建 ClosureCallableExpr 接受任何可以在 PHP 语法中直接调用的表达式:

示例 #1 简单的一级可调用语法

<?php

class Foo {
   public function method() {}
   public static function staticmethod() {}
   public function __invoke() {}
}

$obj = new Foo();
$classStr = 'Foo';
$methodStr = 'method';
$staticmethodStr = 'staticmethod';


$f1 = strlen(...);
$f2 = $obj(...);  // 可调用对象
$f3 = $obj->method(...);
$f4 = $obj->$methodStr(...);
$f5 = Foo::staticmethod(...);
$f6 = $classStr::$staticmethodStr(...);

// traditional callable using string, array
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
?>

注意:

... 是语法的一部分,不是遗漏。

CallableExpr(...)Closure::fromCallable() 语义相同。也就是说,与使用字符串和数组的 callable 不同,CallableExpr(...) 尊重其创建时的作用域:

示例 #2 CallableExpr(...) 与传统 callable 的作用域比较

<?php

class Foo {
    public function getPrivateMethod() {
        return [$this, 'privateMethod'];
    }

    private function privateMethod() {
        echo __METHOD__, "\n";
    }
}

$foo = new Foo;
$privateMethod = $foo->getPrivateMethod();
$privateMethod();
// Fatal error: Call to private method Foo::privateMethod() from global scope
// 这是因为调用是在 Foo 外部执行的,并且将从此时检查可见性。

class Foo1 {
    public function getPrivateMethod() {
        // Uses the scope where the callable is acquired.
        return $this->privateMethod(...); // identical to Closure::fromCallable([$this, 'privateMethod']);
    }

    private function privateMethod() {
        echo __METHOD__, "\n";
    }
}

$foo1 = new Foo1;
$privateMethod = $foo1->getPrivateMethod();
$privateMethod();  // Foo1::privateMethod
?>

注意:

不支持通过此语法(例如 new Foo(...))创建对象,因为不会视 new Foo() 语法为调用。

注意:

一级可调用语法不能与 nullsafe 运算符结合使用。以下两种情况都会导致编译时错误:

<?php
$obj?->method(...);
$obj?->prop->method(...);
?>