浅谈PHP匿名函数和闭包


Posted in PHP onMarch 08, 2019

概述

闭包和匿名函数在PHP 5.3.0中引入,这两个特性非常有用,每个PHP开发者都应该掌握。

匿名函数其实就是没有名称的函数,匿名函数可以赋值给变量,还能像其他任何PHP函数对象那样传递。不过匿名函数仍然是函数,因此可以调用,还可以传入参数,适合作为函数或方法的回调。

闭包是指在创建时封装周围状态的函数,即使闭包所在的环境的不存在了,闭包中封装的状态依然存在。

创建匿名函数

创建匿名函数很简单:

//将匿名函数赋给一个变量,通过变量名+()的形式来调用
$greet = function () {
 return "Hello World";
};

echo $greet();

结果打印:

Hello World

匿名函数和普通的PHP函数很像:常用的句法相同,也接受参数,而且能返回值。不过闭包没有函数名。

注:我们之所以能调用$greet变量,是因为这个变量的值是一个闭包,而且闭包对象实现了__invoke()魔术方法,只要变量名后有(),PHP就会查找并调用__invoke方法。

我们通常把匿名函数当做函数或方法的回调使用,事实上,很多PHP函数都会用到匿名函数,比如array_mappreg_replace_callback,这是使用PHP匿名函数的绝佳时机。记住,闭包和其他值一样,可以作为参数传入其他PHP函数:

$numberPlusOne = array_map(function ($number) {
 return $number += 1;
}, [1, 2, 3]);

print_r($numberPlusOne);

在匿名函数出现之前,要实现这样的功能,PHP开发者只能单独创建具名函数,然后使用名称引用这个函数:

function incrementNumber ($number) {
 return $number += 1;
}

$numberPlusOne = array_map(‘incrementNumber', [1, 2, 3]);
print_r($numberPlusOne);

这样做把回调的实现和使用场所隔离开了,而且使用闭包实现代码更加简洁。

创建闭包

包含自由变量的函数与为所有这些自由变量提供了变量绑定的环境一起,被称为闭包。

function makeHelloWorld($name) { 
 $i = 0;
 return function()use($name, &$i){
  echo $name.$i. ' <br>';
  $i++;
 };

}
$hello1 = makeHelloWorld("itbsl");
$hello2 = makeHelloWorld("kevin");
$hello1();
$hello1();
$hello1();
$hello2();

打印结果:

itbsl0
itbsl1
itbsl2
kevin0

从父作用域继承变量

在PHP中必须手动调用闭包对象的bindTo方法或使用use关键字把父作用域的变量及状态附加到PHP闭包中。而实际应用中,又以使用use关键字实现居多。

use关键字

实际上,Laravel框架中也大量使用了闭包,最常见的比如路由定义:

Route::group(['domain' => '{account}.myapp.com'], function () {
 Route::get('user/{id}', function ($account, $id) {
  //
 });
});

这里面的两个function都是匿名函数。而从父作用域继承变量的使用场景在Laravel底层源码中也是俯拾即是,比如Model.php(Illuminate\Database\Eloquent)的saveOrFail方法:

该方法的作用是使用事务将模型数据保存到数据库,这里面我们使用匿名函数返回保存状态,同时使用use关键字将父作用域的$options传递给该闭包以便其能够访问这个数据。

此外,还支持传递多个父作用域变量到匿名函数,比如还是在Model类中的forceFill方法:

多个变量以逗号分隔即可。

bindTo方法

我们在前面已经提到,闭包是一个对象,所以我们可以在闭包中使用$this关键字获取闭包的内部状态,闭包对象的默认状态没什么用,需要注意的是其中的__invoke魔术方法和bindTo方法。

__invoke的作用前面已经说过,当尝试以调用函数的方式调用一个对象时,__invoke() 方法会被自动调用。

接下来我们来看看bindTo方法,通过该方法,我们可以把闭包的内部状态绑定到其他对象上。这里bindTo方法的第二个参数显得尤为重要,其作用是指定绑定闭包的那个对象所属的PHP类,这样,闭包就可以在其他地方访问邦定闭包的对象中受保护和私有的成员变量。

你会发现,PHP框架经常使用bindTo方法把路由URL映射到匿名回调函数上,框架会把匿名回调函数绑定到应用对象上,这样在匿名函数中就可以使用$this关键字引用重要的应用对象:

class App {
 protected $routes = [];
 protected $responseStatus = '200 OK';
 protected $responseContentType = 'text/html';
 protected $responseBody = 'Laravel学院';

 public function addRoute($routePath, $routeCallback) {
  $this->routes[$routePath] = $routeCallback->bindTo($this, __CLASS__);
 }

 public function dispatch($currentPath) {
  foreach ($this->routes as $routePath => $callback) {
   if( $routePath === $currentPath) {
    $callback();
   }
  }
  header('HTTP/1.1 ' . $this->responseStatus);
  header('Content-Type: ' . $this->responseContentType);
  header('Content-Length: ' . mb_strlen($this->responseBody));
  echo $this->responseBody;
 }

}

这里我们需要重点关注addRoute方法,这个方法的参数分别是一个路由路径和一个路由回调,dispatch方法的参数是当前HTTP请求的路径,它会调用匹配的路由回调。第9行是重点所在,我们将路由回调绑定到了当前的App实例上。这么做能够在回调函数中处理App实例的状态:

$app = new App();
$app->addRoute(‘user/nonfu', function(){
 $this->responseContentType = ‘application/json;charset=utf8';
 $this->responseBody = ‘{“name”:”LaravelAcademy"}';
});
$app->dispatch(‘user/nonfu');
在Larval底层也有用到bindTo方法,详见Illuminate\Support\Traits\Macroable的__call方法:

以上所述是小编给大家介绍的PHP匿名函数和闭包详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
php中通过虚代理实现延迟加载的实现代码
Jun 10 PHP
浅谈PHP调用Webservice思路及源码分享
Jun 04 PHP
PHP利用func_get_args和func_num_args函数实现函数重载实例
Nov 12 PHP
PHP图像处理之使用imagecolorallocate()函数设置颜色例子
Nov 19 PHP
php自定义hash函数实例
May 05 PHP
PHP中你应该知道的require()文件包含的正确用法
Jun 12 PHP
php+jQuery+Ajax实现点赞效果的方法(附源码下载)
Jul 21 PHP
PHP几个实用自定义函数小结
Jan 25 PHP
php 反斜杠处理函数addslashes()和stripslashes()实例详解
Dec 25 PHP
PHP基于ICU扩展intl快速实现汉字转拼音及按拼音首字母分组排序的方法
May 03 PHP
使用 laravel sms 构建短信验证码发送校验功能
Nov 06 PHP
PHP实现将base64编码字符串转换成图片示例
Jun 22 PHP
使用PHPUnit进行单元测试并生成代码覆盖率报告的方法
Mar 08 #PHP
ThinkPHP中图片按比例切割的代码实例
Mar 08 #PHP
PHP的微信支付接口使用方法讲解
Mar 08 #PHP
PHP实现会员账号单唯一登录的方法分析
Mar 07 #PHP
PHP模糊查询技术实例分析【附源码下载】
Mar 07 #PHP
原生PHP实现导出csv格式Excel文件的方法示例【附源码下载】
Mar 07 #PHP
PHP生成二维码与识别二维码的方法详解【附源码下载】
Mar 07 #PHP
You might like
PHP程序员必须清楚的问题汇总
2014/12/18 PHP
Zend Framework框架教程之Zend_Db_Table_Rowset用法实例分析
2016/03/21 PHP
PHP中spl_autoload_register()函数用法实例详解
2016/07/18 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
javascript定时变换图片实例代码
2013/03/17 Javascript
js中精确计算加法和减法示例
2014/03/28 Javascript
jQuery中animate动画第二次点击事件没反应
2015/05/07 Javascript
jquery判断复选框选中状态以及区分attr和prop
2015/12/18 Javascript
jquery插件EasyUI中form表单提交实例分享
2016/01/11 Javascript
郁闷!ionic中获取ng-model绑定的值为undefined如何解决
2016/08/27 Javascript
VUE写一个简单的表格实例
2019/08/06 Javascript
vue 授权获取微信openId操作
2020/11/13 Javascript
[49:31]DOTA2-DPC中国联赛 正赛 Elephant vs LBZS BO3 第二场 1月29日
2021/03/11 DOTA
python基础教程之元组操作使用详解
2014/03/25 Python
python实现巡检系统(solaris)示例
2014/04/02 Python
python调用c++ ctype list传数组或者返回数组的方法
2019/02/13 Python
详解小白之KMP算法及python实现
2019/04/04 Python
CSS3实现苹果手机解锁的字体闪亮效果示例
2021/01/05 HTML / CSS
html5 CSS过度-webkit-transition使用介绍
2013/07/02 HTML / CSS
德国前卫设计师时装在线商店:Luxury Loft
2019/11/04 全球购物
Bandier官网:奢侈、时尚前卫的健身服装首选目的地
2020/07/05 全球购物
如何在Cookie里面保存Unicode和国际化字符
2013/05/25 面试题
几道Java和数据库的面试题
2013/05/30 面试题
六十岁生日答谢词
2014/01/10 职场文书
防邪知识进家庭活动方案
2014/08/26 职场文书
2015年采购部工作总结
2015/04/23 职场文书
2015年“我们的节日·重阳节”活动总结
2015/07/29 职场文书
毕业生入职感言
2015/07/31 职场文书
60条职场经典语录,总有一条能触动你的心
2019/08/21 职场文书
68行Python代码实现带难度升级的贪吃蛇
2022/01/18 Python
上个世纪50年代的可穿戴技术:无线电帽子
2022/02/18 无线电
详解jQuery的核心函数和事件处理
2022/02/18 jQuery
Apache Hudi集成Spark SQL操作hide表
2022/03/31 Servers
微软官方消息,在 2023 年 4 月 11 日之后微软将不再为 Office 2013 和 Skype for Business 2015 提供安全更新
2022/04/21 数码科技
Oracle中DBLink的详细介绍
2022/04/29 Oracle
win sever 2022如何占用操作主机角色
2022/06/25 Servers