一等可调用语法

一等可调用语法从 PHP 8.1.0 开始引入,作为从 可调用 创建 匿名函数 的一种方式。它取代了使用字符串和数组的现有可调用语法。这种语法的优点是它可以被静态分析访问,并且使用可调用获取时的作用域。

CallableExpr(...) 语法用于从可调用创建 Closure 对象。CallableExpr 接受任何可以在 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(...);

// 使用字符串、数组的传统可调用
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
?>

注意:

... 是语法的一部分,而不是省略。

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

示例 #2 CallableExpr(...) 和传统可调用的作用域比较

<?php

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

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

$foo = new Foo;
$privateMethod = $foo->getPrivateMethod();
$privateMethod();
// 致命错误:从全局范围调用私有方法 Foo::privateMethod()
// 这是因为调用是在 Foo 之外执行的,并且可见性将从此时检查。

class Foo1 {
public function
getPrivateMethod() {
// 使用获取可调用时的作用域。
return $this->privateMethod(...); // 等同于 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(...);
?>

添加笔记

用户贡献笔记 1 个笔记

bienvenunet at yahoo dot com
1 年前
这种语法有一个主要的陷阱,你可能在使用它之前不会注意到,直到你使用这种语法并发现你在一些随机库代码中遇到“无法重新绑定从方法创建的闭包的作用域”异常。

如文档所述,一等可调用使用获取可调用时的作用域。只要你的代码中没有任何内容会尝试使用 \Closure::bindTo 方法绑定可调用,这都没问题。

我通过将传递给 Laravel 的 Macroable 功能的可调用从数组样式更改为一等可调用样式来发现这个问题。Macroable 功能会对可调用调用 \Closure::bindTo。

据我所知,唯一的解决方法是使用更丑陋的数组语法。
To Top