浅谈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的引用原因分析
Sep 06 PHP
PHP下获取上个月、下个月、本月的日期(strtotime,date)
Feb 02 PHP
ThinkPHP实现支付宝接口功能实例
Dec 02 PHP
php中二维数组排序问题方法详解
Aug 28 PHP
php求数组全排列,元素所有组合的方法
May 05 PHP
PHP获取页面执行时间的方法(推荐)
Dec 10 PHP
PHP调用Mailgun发送邮件的方法
May 04 PHP
基于win2003虚拟机中apache服务器的访问
Aug 01 PHP
PHP开发中解决并发问题的几种实现方法分析
Nov 13 PHP
PHP有序表查找之二分查找(折半查找)算法示例
Feb 09 PHP
YII2框架中日志的配置与使用方法实例分析
Mar 18 PHP
yii2.0框架数据库操作简单示例【添加,修改,删除,查询,打印等】
Apr 13 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
ajax php 实现写入数据库
2009/09/02 PHP
php curl 登录163邮箱并抓取邮箱好友列表的代码(经测试)
2011/04/07 PHP
CodeIgniter集成smarty的方法详解
2016/05/26 PHP
PHP中常用的魔术方法
2017/04/28 PHP
javascript 面向对象全新理练之原型继承
2009/12/03 Javascript
使用SyntaxHighlighter实现HTML高亮显示代码的方法
2010/02/04 Javascript
js使用正则实现ReplaceAll全部替换的方法
2014/07/18 Javascript
jQuery中insertBefore()方法用法实例
2015/01/08 Javascript
介绍JavaScript中Math.abs()方法的使用
2015/06/14 Javascript
深入分析jsonp协议原理
2015/09/26 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
2016/04/05 Javascript
jQuery Easyui datagrid行内实现【添加】、【编辑】、【上移】、【下移】
2016/12/19 Javascript
vue+vuex+axio从后台获取数据存入vuex实现组件之间共享数据
2017/04/22 Javascript
Vue+webpack项目基础配置教程
2018/02/12 Javascript
微信小程序制作扭蛋机代码实例
2019/09/24 Javascript
jQuery实现手风琴效果(蒙版)
2020/01/11 jQuery
js数组中去除重复值的几种方法
2020/08/03 Javascript
[02:42]DOTA2英雄基础教程 杰奇洛
2013/12/23 DOTA
[52:06]FNATIC vs NIP 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
[02:55]含熏伴清风,风行者至宝、屠夫身心及典藏宝瓶二展示
2020/09/08 DOTA
Python字符串匹配算法KMP实例
2015/07/18 Python
python 自动化将markdown文件转成html文件的方法
2016/09/23 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python 统计字数的思路详解
2018/05/08 Python
根据tensor的名字获取变量的值方式
2020/01/04 Python
8款精美的CSS3表单设计(登录表单/下拉选择/按钮附演示及源码)
2013/02/04 HTML / CSS
JavaScript+Canvas实现自定义画板的示例代码
2019/05/13 HTML / CSS
thinkphp5 redis缓存新增方法实例讲解
2021/03/24 PHP
临床医学专业毕业生的自我评价
2013/10/17 职场文书
财务工作个人求职的自我评价
2013/12/19 职场文书
中式婚礼主持词
2014/03/13 职场文书
校长竞聘演讲稿
2014/05/16 职场文书
会议室标语
2014/06/21 职场文书
2014年创卫工作总结
2014/11/24 职场文书
2015年保险公司内勤工作总结
2015/05/23 职场文书
Mysql效率优化定位较低sql的两种方式
2021/05/26 MySQL