PHP基于Closure类创建匿名函数的方法详解
本文实例讲述了PHP基于Closure类创建匿名函数的方法。分享给大家供大家参考,具体如下:
Closure类
用于代表匿名函数的类。
匿名函数(在PHP5.3中被引入)会产生这个类型的对象。在过去,这个类被认为是一个实现细节,但现在可以依赖它做一些事情。自PHP5.4起,这个类带有一些方法,允许在匿名函数创建后对其进行更多的控制。
这个类不能实例化,里面主要有两个方法,都用来复制闭包,一个静态一个动态,下面分别详细讲解下这两个不好理解的方法。
Closure::bind
publicstaticClosureClosure::bind(Closure$closure,object$newthis[,mixed$newscope='static'])
参数说明:
closure
需要绑定的匿名函数。
newthis
需要绑定到匿名函数的对象,或者NULL创建未绑定的闭包。
newscope
想要绑定给闭包的类作用域,或者'static'表示不改变。如果传入一个对象,则使用这个对象的类型名。类作用域用来决定在闭包中$this对象的私有、保护方法的可见性。
Theclassscopetowhichassociatetheclosureistobeassociated,or'static'tokeepthecurrentone.Ifanobjectisgiven,thetypeoftheobjectwillbeusedinstead.Thisdeterminesthevisibilityofprotectedandprivatemethodsoftheboundobject.
上面是该方法的定义,第一个参数很好理解,就是一个闭包函数;第二个参数就不太好理解,如果要复制的闭包中包含$this,这个对象就表示这个$this,闭包函数里面对这个对象的修改在调用结束之后也会保持一致,比如修改了一个属性;第三个参数就不太好理解了,看官方的说明也是云里雾里的,默认参数情况下,调用$this->访问object$newthis中的属性函数的时候,会有限制,只能访问public属性的函数,如果想访问protected/private属性,就要设置为对应的类名/类实例,就要像在类里面一样,要访问那个类的保护/私有属性函数。
例子
who(); $this->name(); $this->show(); },$test); $func();
上面的代码会报错Fatalerror:UncaughtError:CalltoprotectedmethodT::who()fromcontext'Closure'。
加上bind第三个参数为t::class或者newT(),会正常输出每一个结果。
我是T里面的保护函数:who 我是T里面的公共函数:name 我是T里面的私有函数:show
当然了,闭包也可以传递参数
$test=newStdClass(); 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)"燕睿涛" }
另外还有个特别要说明的例子
show(); },null); $test=newT(); $func($test);
上面的情况会输出什么呢,没错,会报错,提示访问不了私有属性show,这个时候,加上第三个参数就可以了,看了第三个参数不光影响$this的作用域,也可以影响参数的作用域。
Closure::bindTo
bindTo和bind功能类似,这里只是另外一种形式,都是复制当前闭包对象,绑定指定的$this对象和类作用域。,参数比bind少了第一个,后面两个一样,当然还有一个区别就是bindTo不是静态方法,是闭包才会存在的一个属性方法。
例子
show(); $this->who(); $this->name(); }; $funcNew=$func->bindTo(newT(),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 publicstaticfunctiongetInitializer(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); publicstaticfunctiongetInitializer() { 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常用函数与技巧总结》、《php字符串(string)用法总结》、《PHP数组(Array)操作技巧大全》、《PHP数据结构与算法教程》及《php程序设计算法总结》
希望本文所述对大家PHP程序设计有所帮助。