浅谈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 xml留言板 xml存储数据的简单例子
Aug 24 PHP
7个超级实用的PHP代码片段
Jul 11 PHP
php利用iframe实现无刷新文件上传功能的代码
Sep 29 PHP
php的array_multisort()使用方法介绍
May 16 PHP
php全局变量和类配合使用深刻理解
Jun 05 PHP
PHP中mb_convert_encoding与iconv函数的深入解析
Jun 21 PHP
php共享内存段示例分享
Jan 20 PHP
VPS中使用LNMP安装WordPress教程
Dec 28 PHP
PHP获取POST数据的几种方法汇总
Mar 03 PHP
PHP+apc+ajax实现的ajax_upload上传进度条代码
Jan 25 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
Jun 14 PHP
PHP MVC框架中类的自动加载机制实例分析
Sep 18 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
数据库的日期格式转换
2006/10/09 PHP
谷歌音乐搜索栏的提示功能php修正代码
2011/05/09 PHP
通过PHP的内置函数,通过DES算法对数据加密和解密
2012/06/21 PHP
php对关联数组循环遍历的实现方法
2015/03/13 PHP
PHP中读取文件的几个方法总结(推荐)
2016/06/03 PHP
CodeIgniter开发实现支付宝接口调用的方法示例
2016/11/14 PHP
Thinkphp 框架扩展之驱动扩展实例分析
2020/04/27 PHP
jQuery-ui中自动完成实现方法
2010/06/10 Javascript
jQuery中parentsUntil()方法用法实例
2015/01/07 Javascript
JavaScript实现获得所有兄弟节点的方法
2015/07/23 Javascript
使用JavaScript实现弹出层效果的简单实例
2016/05/31 Javascript
angularjs中ng-bind-html的用法总结
2017/05/23 Javascript
详解Javascript中new()到底做了些什么?
2018/03/29 Javascript
Node.js npm命令运行node.js脚本的方法
2018/10/10 Javascript
微信小程序学习笔记之跳转页面、传递参数获得数据操作图文详解
2019/03/28 Javascript
layui 实现表单和文件上传一起传到后台的例子
2019/09/16 Javascript
python通过exifread模块获得图片exif信息的方法
2015/03/16 Python
Python数据结构与算法之使用队列解决小猫钓鱼问题
2017/12/14 Python
win8下python3.4安装和环境配置图文教程
2018/07/31 Python
对Pytorch中nn.ModuleList 和 nn.Sequential详解
2019/08/18 Python
Python中list循环遍历删除数据的正确方法
2019/09/02 Python
python 两种方法修改文件的创建时间、修改时间、访问时间
2020/09/26 Python
Matplotlib中rcParams使用方法
2021/01/05 Python
CSS3实现图片抽屉式效果的示例代码
2019/11/06 HTML / CSS
html5记忆翻牌游戏实现思路及代码
2013/07/25 HTML / CSS
美国珠宝精品店:Opulent Jewelers
2019/08/20 全球购物
什么是Web Service?
2012/07/25 面试题
硅酸盐工业控制专业应届生求职信
2013/11/02 职场文书
物理教育专业毕业生推荐信
2013/11/03 职场文书
小学生考试获奖感言
2014/01/30 职场文书
机关道德讲堂实施方案
2014/03/15 职场文书
主管竞聘书范文
2014/03/31 职场文书
从贫穷到富有,是知识技能和学习力的差别
2019/08/20 职场文书
初中运动会闭幕词范本3篇
2019/12/09 职场文书
JavaScript实现酷炫的鼠标拖尾特效
2022/02/18 Javascript
CSS 实现角标效果的完整代码
2022/06/28 HTML / CSS