PHP基于Closure类创建匿名函数的方法详解


Posted in PHP onAugust 17, 2017

本文实例讲述了PHP基于Closure类创建匿名函数的方法。分享给大家供大家参考,具体如下:

Closure 类

用于代表匿名函数的类。

匿名函数(在 PHP 5.3 中被引入)会产生这个类型的对象。在过去,这个类被认为是一个实现细节,但现在可以依赖它做一些事情。自 PHP 5.4 起,这个类带有一些方法,允许在匿名函数创建后对其进行更多的控制。

这个类不能实例化,里面主要有两个方法,都用来复制闭包,一个静态一个动态,下面分别详细讲解下这两个不好理解的方法。

Closure::bind

public static Closure Closure::bind ( Closure $closure , object $newthis [, mixed $newscope = 'static' ] )

参数说明:

closure
需要绑定的匿名函数。

newthis
需要绑定到匿名函数的对象,或者 NULL 创建未绑定的闭包。

newscope
想要绑定给闭包的类作用域,或者 'static' 表示不改变。如果传入一个对象,则使用这个对象的类型名。 类作用域用来决定在闭包中 $this 对象的 私有、保护方法 的可见性。

The class scope to which associate the closure is to be associated, or 'static' to keep the current one. If an object is given, the type of the object will be used instead. This determines the visibility of protected and private methods of the bound object.

上面是该方法的定义,第一个参数很好理解,就是一个闭包函数;第二个参数就不太好理解,如果要复制的闭包中包含$this,这个对象就表示这个$this,闭包函数里面对这个对象的修改在调用结束之后也会保持一致,比如修改了一个属性;第三个参数就不太好理解了,看官方的说明也是云里雾里的,默认参数情况下,调用$this->访问object $newthis中的属性函数的时候,会有限制,只能访问public属性的函数,如果想访问protected/private属性,就要设置为对应的类名/类实例,就要像在类里面一样,要访问那个类的保护/私有属性函数。

例子

<?php
class T {
  private function show()
  {
    echo "我是T里面的私有函数:show\n";
  }
  protected function who()
  {
    echo "我是T里面的保护函数:who\n";
  }
  public function name()
  {
    echo "我是T里面的公共函数:name\n";
  }
}
$test = new T();
$func = Closure::bind(function(){
  $this->who();
  $this->name();
  $this->show();
}, $test);
$func();

上面的代码会报错Fatal error: Uncaught Error: Call to protected method T::who() from context 'Closure'

加上bind第三个参数为t::class或者new T(),会正常输出每一个结果。

我是T里面的保护函数:who
我是T里面的公共函数:name
我是T里面的私有函数:show

当然了,闭包也可以传递参数

$test = new StdClass();
var_dump($test);
$func = Closure::bind(function($obj){
  $obj->name = "燕睿涛";
}, null);
$func($test);
var_dump($test);

上面的程序跟匿名函数一样,啥对象也没有依赖,上面的程序会输出:

object(stdClass)#1 (0) {
}
object(stdClass)#1 (1) {
 ["name"]=>
 string(9) "燕睿涛"
}

另外还有个特别要说明的例子

<?php
class T {
  private function show()
  {
    echo "我是T里面的私有函数:show\n";
  }
  protected function who()
  {
    echo "我是T里面的保护函数:who\n";
  }
  public function name()
  {
    echo "我是T里面的公共函数:name\n";
  }
}
$func = Closure::bind(function ($obj) {
  $obj->show();
}, null);
$test = new T();
$func($test);

上面的情况会输出什么呢,没错,会报错,提示访问不了私有属性show,这个时候,加上第三个参数就可以了,看了第三个参数不光影响$this的作用域,也可以影响参数的作用域。

Closure::bindTo

bindTo和bind功能类似,这里只是另外一种形式,都是复制当前闭包对象,绑定指定的$this对象和类作用域。,参数比bind少了第一个,后面两个一样,当然还有一个区别就是bindTo不是静态方法,是闭包才会存在的一个属性方法。

例子

<?php
class T {
  private function show()
  {
    echo "我是T里面的私有函数:show\n";
  }
  protected function who()
  {
    echo "我是T里面的保护函数:who\n";
  }
  public function name()
  {
    echo "我是T里面的公共函数:name\n";
  }
}
$func = function () {
  $this->show();
  $this->who();
  $this->name();
};
$funcNew = $func->bindTo(new T(), T::class);
$funcNew();

上面函数的输出和bind的类似

我是T里面的私有函数:show
我是T里面的保护函数:who
我是T里面的公共函数:name

一个trick

这个函数是在看composer生成的自动加载源码的时候碰到的,在composer中用的比较特别,下面是截取部分composer中的代码

// 文件autoload_real.php
call_user_func(\Composer\Autoload\ComposerStaticInit898ad46cb49e20577400c63254121bac::getInitializer($loader));
// 文件autoload_static.php
public static function getInitializer(ClassLoader $loader)
{
  return \Closure::bind(function () use ($loader) {
    $loader->prefixLengthsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixLengthsPsr4;
    $loader->prefixDirsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixDirsPsr4;
    $loader->prefixesPsr0 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixesPsr0;
    $loader->classMap = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$classMap;
  }, null, ClassLoader::class);
}

上面的代码比较奇特,在call_user_func中,第一感觉是传错参数了,其实不然,这里调用了一个函数,这个函数会返回一个Closure对象,也就是一个匿名函数,最终传入的参数还是一个callable类型。再看看这个返回的闭包,里面使用了use,这是连接闭包和外部变量的桥梁。

至于这里为什么普通传参数就可以,是因为php5里面,对象形参和实参数指向相同的对象,函数里面对对象的修改会反映到对象外面。

所以,上面这么做是没问题的,还有另外一种形式也可以

call_user_func(\Composer\Autoload\ComposerStaticInit898ad46cb49e20577400c63254121bac::getInitializer(), $loader);
public static function getInitializer()
{
  return \Closure::bind(function ($loader) {
    $loader->prefixLengthsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixLengthsPsr4;
    $loader->prefixDirsPsr4 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixDirsPsr4;
    $loader->prefixesPsr0 = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$prefixesPsr0;
    $loader->classMap = ComposerStaticInit25885cdf386fdaafc0bce14bb5a7d06e::$classMap;
  }, null, ClassLoader::class);
}

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP5 字符串处理函数大全
Mar 23 PHP
PHP 强制性文件下载功能的函数代码(任意文件格式)
May 26 PHP
PHP基础教程(php入门基础教程)一些code代码
Jan 06 PHP
浅析Yii中使用RBAC的完全指南(用户角色权限控制)
Jun 20 PHP
如何使用PHP获取指定日期所在月的开始日期与结束日期
Aug 01 PHP
非常好用的Zend Framework分页类
Jun 25 PHP
ThinkPHP使用smarty模板引擎的方法
Jul 01 PHP
PHP5全版本绕过open_basedir读文件脚本漏洞详细介绍
Jan 20 PHP
php安全配置记录和常见错误梳理(总结)
Mar 28 PHP
基于php双引号中访问数组元素报错的解决方法
Feb 01 PHP
PHP正则验证字符串是否为数字的两种方法并附常用正则
Feb 27 PHP
PHP替换Word中变量并导出PDF图片的实现方法
Nov 26 PHP
PHP编译configure时常见错误的总结
Aug 17 #PHP
基于PHP常用文件函数和目录函数整理
Aug 17 #PHP
PHP实现的堆排序算法详解
Aug 17 #PHP
基于php编程规范(详解)
Aug 17 #PHP
PHP数据库操作四:mongodb用法分析
Aug 16 #PHP
PHP Laravel 上传图片、文件等类封装
Aug 16 #PHP
PHP数据库操作三:redis用法分析
Aug 16 #PHP
You might like
[FAQ]PHP中的一些常识:类篇
2006/10/09 PHP
PHP 生成的XML以FLASH获取为乱码终极解决
2009/08/07 PHP
PHP仿盗链代码
2012/06/03 PHP
浅析get与post的一些特殊情况
2014/07/28 PHP
Ajax提交表单时验证码自动验证 php后端验证码检测
2016/07/20 PHP
php基于curl实现的股票信息查询类实例
2016/11/11 PHP
php rsa 加密,解密,签名,验签详解
2016/12/06 PHP
PHP 获取 ping 时间的实现方法
2017/09/29 PHP
JavaScript 学习笔记(十六) js事件
2010/02/01 Javascript
jquery插件制作 自增长输入框实现代码
2012/08/17 jQuery
WordPress中鼠标悬停显示和隐藏评论及引用按钮的实现
2016/01/12 Javascript
原生JS实现图片轮播与淡入效果的简单实例
2016/08/21 Javascript
Vue-router结合transition实现app前进后退动画切换效果的实例
2017/10/11 Javascript
vue-cli 引入、配置axios的方法
2018/05/08 Javascript
js正则相关知识点专题
2018/05/10 Javascript
vue单文件组件无法获取$refs的问题
2020/06/24 Javascript
[44:40]Spirit vs Navi Supermajor小组赛 A组败者组第一轮 BO3 第一场 6.2
2018/06/03 DOTA
[34:44]Liquid vs TNC Supermajor 胜者组 BO3 第二场 6.4
2018/06/05 DOTA
python中as用法实例分析
2015/04/30 Python
tensorflow 输出权重到csv或txt的实例
2018/06/14 Python
Python爬虫运用正则表达式的方法和优缺点
2019/08/25 Python
Python socket实现的文件下载器功能示例
2019/11/15 Python
python绘制规则网络图形实例
2019/12/09 Python
Python如何使用字符打印照片
2020/01/03 Python
Python如何把多个PDF文件合并代码实例
2020/02/13 Python
Pytorch 使用不同版本的cuda的方法步骤
2020/04/02 Python
pycharm进入时每次都是insert模式的解决方式
2021/02/05 Python
荟萃全球保健品:维他购
2018/05/09 全球购物
大学生村官工作感言
2014/01/10 职场文书
民主评议党员个人总结
2015/02/13 职场文书
2015年党员创先争优公开承诺书
2015/04/27 职场文书
领导视察通讯稿
2015/07/18 职场文书
入党宣誓大会后的感想
2015/08/10 职场文书
适合后台管理系统开发的12个前端框架(小结)
2021/06/29 Javascript
《艾尔登法环》1.03.3补丁上线 碎星伤害调整
2022/04/06 其他游戏
CSS文本阴影 text-shadow 悬停效果详解
2022/05/25 HTML / CSS