探索 PHP 的 First-class callable 语法
查看 Laravel 框架最新的更新时,我发现了一些以前没看到过的语法。
比如 laravel/framework 中的 bin/facades.php
文件
$resolvedMethods = $proxies->map(fn ($fqcn) => new ReflectionClass($fqcn))
->flatMap(fn ($class) => [$class, ...resolveDocMixins($class)])
->flatMap(resolveMethods(...))
->reject(isMagic(...))
->reject(isInternal(...))
->reject(isDeprecated(...))
->reject(fulfillsBuiltinInterface(...))
->reject(fn ($method) => conflictsWithFacade($facade, $method))
->unique(resolveName(...))
->map(normaliseDetails(...));
我未见过的语法是函数调用里的(…):
->flatMap(resolveMethods(...))
你可能在各自其他语境下见过 …
操作符(也叫扩展操作符或解包运算符)
你可以用来对数组解包 …
$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];
...
或者获取函数参数:
function myFunction(...$arguments) {
var_dump($arguments); // shows an array ['a', 'b', 'c']
}
myFunction('a', 'b', 'c');
还有一个巧妙的小技巧,即你可以将一个函数的全部参数传入到另外一个函数中。
function myFunction(...$arguments) {
// $arguments now holds an array of all passed arguments
// all elements in the array will be passed as
//separate arguments to `anotherFunction`.
anotherFunction(...$arguments);
}
First-class callable 语法
而这里的情况...
->flatMap(resolveMethods(...))
此处的 … 运算符则完全不同。此处被称为 ”first class callable",是PHP8.1中引入的新语法。它将要使用的函数包装在闭包中。
因此这段代码 ...
$myFunction = strtoupper(...);
... 和下面这段代码是等价的:
$myFunction = function(...$arguments) {
return strtoupper(...$argument);
}
你传入其中的所有参数,都会传入到闭包中包裹的函数中。
我们来试试使用它吧
$myFunction('a') // returns 'A';
我们先用简单的示例试试。假设你有个集合,需要将其中的元素转换成大写。
collect(['a', 'b', 'c'])
->map(function($letter) {
return strtoupper($letter);
});
使用新语法,你可以这样重写代码:
collect(['a', 'b', 'c'])
->map(strtoupper(...));
酷吧?
当然,你也可以使用非全局函数,比如类方法。
class MyClass()
{
public function execute(): Collection
{
return collect(['a', 'b', 'c'])
->map($this->doubleString(...));
}
public function doubleString(string $string): string
{
return $string . $string;
}
}
// returns a collection with 'aa', 'bb, and 'cc'.
(new MyClass)->execute();
很简洁!