PHP 8.1: First-class 可调用(callable)语法
PHP 8.1 及以上版本支持在当前作用域中创建 callable 的新语法。
相比用 Closure::fromCallable
,借助该语法使用函数或方法的调用方式来创建 callable 使之更容易。
Closure::fromCallable
从 PHP callable(函数名,方法或匿名函数)中返回一个 callable (Closure
对象)。该语法旨在减少 Closure::fromCallable
的模板代码。
比如,下例返回调用 strtoupper
的 callable:
$callable = Closure::fromCallable('strtoupper');
echo $callable('foo'); // FOO
使用新语法,同样的 callable 可以这样创建:
-$callable = Closure::fromCallable('strtoupper');
+$callable = strtoupper(...);
echo $callable('foo'); // FOO
语法
一个后面跟着(…)的有效的可调用(callable)类型,称之为 Fist-class Callable。
函数名
$callable = strlen(...);
类方法
$callable = $item->doSomething(...);
静态方法
$callable = $item::doSomething(...);
匿名函数
$function = function() {};
$callable = $function(...);
请注意,…
不是被用作特定参数的占位符。虽然该语法允许部分函数使用,此处却不适用。试图传参到这个 First-class callable 中会产生语法错误:
$callable = str_contains($text, ...);
Parse error: syntax error, unexpected token ")" in ... on line ...
PHP 也使用 …
(内部叫做 T_ELLIPSIS
)作为可变函数声明及在调用时作为扩展运算符。这对可变函数声明及扩展运算符的行为没有影响。
限制
不允许对象实例化
这个 First-class callable 语法不支持使用新构造实例化新对象。此行为类似于 Closure::fromCallable
。
$callable = new SplFixedArray(...);
Fatal error: Cannot create Closure for new expression in ... on line ...
不允许 null safe 方法
该语法不允许 nullsafe 方法,因为它不能保证可调用。
$test?->doSomething(...);
Fatal error: Cannot combine nullsafe operator with Closure creation in ... on line ...
注解参数
使用该语法不能声明注解
#[Attribute(...)]
class Test {}
Fatal error: Cannot create Closure as attribute argument in ... on line ...
Callable 作用域
一个 First-class callable 被创建时,它也同时继承了创建该 callable 的调用栈的作用域。
function shout(): void {
$value = 'Banana';
echo $value;
}
$value = 'Apple';
$callable = shout(...);
$callable(); // Banana
因为 First-class callable 有作用域,因此可以返回一个 callable 来调用私有方法,只要它是从对象作用域内返回的。
class Clock {
public function getClockCallable(): callable {
return $this->getTime(...);
}
private function getTime(): int {
return time();
}
}
$clock = new Clock();
$clock_callback = $clock->getClockCallable();
echo $clock_callback();
注意,Clock::getClockCallable
返回了一个 callable,该 callable 调用了私有方法 getTime
。现有使用数组创建 callable 的语法不允许调用私有方法:
class Clock {
public function getClockCallable(): callable {
- return $this->getTime(...);
+ return [$this, 'getTime'];
}
private function getTime(): int {
return time();
}
}
$clock = new Clock();
$clock_callback = $clock->getClockCallable();
echo $clock_callback();
Fatal error: Uncaught Error: Call to private method Clock::getTime() from global scope in ...:...
向后兼容性影响
First-class callable 语法是 PHP 8.1 中新引入的语法,无法打补丁到旧版 PHP 中。尝试使用会导致解析错误:
Parse error: syntax error, unexpected token ")" in ... on line ...
需要兼容旧版的应用可以继续使用 Closure::fromCallable
去创建有范围的 callable。