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 对象的私有、保护方法 的可见性。
上面是该方法的定义, 第一个参数很好理解, 就是一个闭包函数;
第二个参数就不太好理解, 如果要复制的闭包中包含$this, 这个对象就表示这个$this, 闭包函数里面对这个对象的修改在调用结束之后也会保持一致,
比如修改了一个属性;
第三个参数就不太好理解了, 看官方的说明也是云里雾里的,
默认参数情况下, 调用$this->访问object $newthis中的属性函数的时候, 会有限制, 只能访问public属性的函数,
如果想访问protected/private属性, 就要设置为对应的类名/类实例, 就要像在类里面一样, 要访问那个类的保护/私有属性函数。
例子
<?php
class T {
private function show()
{
echo "我是T里面的私有函数:show\r\n";
}
protected function who()
{
echo "我是T里面的保护函数:who\r\n";
}
public function name()
{
echo "我是T里面的公共函数:name\r\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(),会正常输出每一个结果。
<?php
class T {
private function show()
{
echo "我是T里面的私有函数:show\r\n";
}
protected function who()
{
echo "我是T里面的保护函数:who\r\n";
}
public function name()
{
echo "我是T里面的公共函数:name\r\n";
}
}
$test = new T();
$func = Closure::bind(function(){
$this->who();
$this->name();
$this->show();
}, $test, new T());
$func();
?>
/*
输出结果:
我是T里面的保护函数:who 我是T里面的公共函数:name 我是T里面的私有函数:show
*/
当然了, 闭包也可以传递参数
$test = new StdClass();
var_dump($test);
echo "<br>";
$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, 这个时候, 加上第三个参数就可以了(T::class或者new T()), 看了第三个参数不光影响$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);
}