一文详解 PHP 的箭头函数和在 WordPrss 中的应用
PHP 的箭头函数(Arrow functions)也叫做“短闭包”(short closures),它是作为定义匿名函数的一种更简洁的方式,在 PHP 7.4 版本中被引入的。
箭头函数彻底改进了代码的简洁性,与传统的匿名函数不同,箭头函数无需显式定义参数即可无缝地访问相同作用域内的变量,此外也无需通过引用传递变量,或者使用 global
关键字。
箭头函数的语法
箭头函数使用 fn
关键字定义,后面紧跟着函数参数、箭头符号(=>
)和函数体:
$func = fn() => VALUES TO RETUEN HERE
箭头函数在语法层面是更加简洁性的,可以显著减少闭包中常见的样板代码。
- 无需
function
关键字:箭头函数使用fn
关键字来声明,而不是传统的function
关键字。 - 隐式返回值:箭头函数的主体是一个表达式而不是一个语句块,该表达式的结果会被自动返回,所以不需要显式写
return
语句。 - 省略花括号:当箭头函数只包含一个表达式时,可以省略花括号
{}
。 - 捕获语法作用域变量:箭头函数会自动捕获定义时的语法作用域中的变量,而无需使用
use
关键字,这减少了闭包中常见的样板代码。
比如下面我们使用 array_map
求一组数字的平方的例子,可以明细看出箭头函数的简洁性:
$numbers = [1, 2, 3, 4, 5];
// 使用传统的闭包函数:
$square = array_map(function ($n) {
return $n * $n;
}, $numbers);
// 使用箭头函数:
$square = array_map(fn($n) => $n * $n, $numbers);
箭头函数变量作用域
箭头函数的一个显著优势是它还可以捕获周围作用域中的变量,与传统匿名函数不同,箭头函数继承了它们被定义时的作用域,这种语法作用域行为可以导致更可预测且错误更少的代码。
$multiplier = 10;
// 使用传统的闭包函数:
$calculate = function ($value) use ($multiplier){
return $value * $multiplier;
};
// 使用箭头函数:
$calculate = fn($value) => $value * $multiplier;
如上例子所示,传统的匿名函数使用 use
关键字从外部作用域导入 $multiplier
变量,相比之下,箭头函数自动继承变量,不需要使用 use
关键字了。
箭头函数的限制和约束
箭头函数虽然带来了简洁性,但是它同样也有是限制的,它的一个主要的约束就是箭头函数仅限于单个表达式。这种限制确保了简单性,但需要更复杂的逻辑时,使用传统的匿名函数可能会更合适。
// 使用传统的闭包函数:
$traditionalFunction = function ($value) {
if ($value > 0) {
return true;
} else {
return false;
}
};
// 使用箭头函数:
$arrowFunction = fn($value) => $value > 0 ? true : false;
如上所示,如果需要在函数内使用多个语句,特别是涉及到控制流结构(如 if-else
代码块)时,传统的匿名函数(使用 function
关键字定义的函数)就显得更为适合,这是因为传统匿名函数可以包含任意数量的语句,并允许更复杂的逻辑,而箭头函数支持单个表达式。
箭头函数的具体例子
最简单的箭头函数
下面代码我们定义了一个名为 $add
简单箭头函数,它有两个参数 $a
和 $b
,结果是返回它们的和。
$add = fn($a, $b) => $a + $b;
嵌套箭头函数
嵌套箭头函数涉及在一个箭头函数中使用另一个箭头函数,当需要动态定义函数或想要封装功能时,这尤其有用,下面是一个嵌套箭头函数的具体示例:
$outerFunction = fn($x) => fn($y) => $x * $y;
$result1 = $outerFunction(5)(3); // 输出:15
$result2 = $outerFunction(2)(4); // 输出:8
在 array_map 中使用
下面的例子我们在 array_map
中使用箭头函数求一组数字的平方:
$numbers = [1, 2, 3, 4, 5];
// 使用传统的闭包函数:
$squared = array_map(function($n) {
return $n * $n;
}, $numbers);
// 使用箭头函数:
$squaredArrow = array_map(fn($n) => $n * $n, $numbers);
在 array_filter 中使用
下面的例子我们在 array_filter
中使用箭头函数过滤出一组数字的偶数:
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 使用传统的闭包函数:
$evenNumbers = array_filter($numbers, function($n) {
return $n % 2 === 0;
});
// 使用箭头函数:
$evenNumbersArrow = array_filter($numbers, fn($n) => $n % 2 === 0);
在 array_reduce 中使用
下面的例子我们在 array_reduce
中使用箭头函数计算一组数字的和:
$numbers = [1, 2, 3, 4, 5];
// 使用传统的闭包函数:
$sumNumbers = array_reduce($numbers, function($carry, $number) {
return $carry + $number;
}, 0);
// 使用箭头函数:
$sumNumbersArrow = array_reduce($numbers, fn($carry, $number) => $carry + $number, 0);
在 usort 中使用
下面的例子我们在 usort
中使用箭头函数对数组进行自定义排序:
$people = [
['name' => 'Alice', 'age' => 28],
['name' => 'Bob', 'age' => 22],
['name' => 'Charlie', 'age' => 25],
];
// 使用传统的闭包函数:
usort($people, function($a, $b) {
return $a['age'] <=> $b['age'];
});
// 使用箭头函数:
usort($people, fn($a, $b) => $a['age'] <=> $b['age']);
在 WordPress 中使用
在 WordPress 中可以向上面的例子一样使用箭头函数,WordPress 中唯一需要知道的是,我们还可以在 WordPress 的 Hook 中使用箭头函数:
add_action('wp_loaded', fn() => ob_start(fn($html) => apply_filters('wpjam_html', $html)));
比如上面代码,我们使用嵌套的方式,首先定一个箭头函数作为 wp_loaded
这个 action
的回调函数,这个函数在定义一个箭头函数作为 ob_start
的回调函数,这里只需要一行就搞定了,如果使用传统的闭包函数,则需要这样编码:
add_action('wp_loaded', function(){
return ob_start(function($html){
return apply_filters('wpjam_html', $html);
});
});
再举个使用 filter
的例子,比如 WPJAM Basic 中彻底关闭 WordPress 的 pingback 功能,他是这样处理的,就是把 XMLRPC 中 pingback 相关的方法简单返回 false
:
add_filter('xmlrpc_methods', fn($methods) => array_merge($methods, [
'pingback.ping' => '__return_false',
'pingback.extensions.getPingbacks' => '__return_false'
]));
如果使用传统的闭包函数,则需要这样编码:
add_filter('xmlrpc_methods', function($methods){
return array_merge($methods, [
'pingback.ping' => '__return_false',
'pingback.extensions.getPingbacks' => '__return_false'
]);
});
简单总结
1. 箭头函数是在 PHP 7.4 版本中引入,它提供一种更简洁的方式来写单行的闭包表达式,使代码更加清晰和简洁。
2. 箭头函数具有隐式返回特性,这意味着无需 return
语句即可自动返回表达式的结果。
3. 箭头函数会自动捕获上层作用域中的变量,所以在写类似数组操作的回调时,开发者不需要显式地使用 use
语句,这减少了闭包使用时的样板代码,并使得代码更易于理解。
4. 从上面例子可以看出,箭头函数对于高阶函数中使用的回调函数特别方便和适用,并且在 WordPress 的 Hook 中使用也非常方便。
5. 但是也要注意考虑到它只能包含单个表达式,当需要更复杂的逻辑时,使用传统的匿名函数可能会更合适。