PHP 闭包
浅谈PHP闭包 Closure和Callable
区别
Closure 必须是匿名函数, callable 同时还可以为可调用函数名
区别示例
function func1(callable $callable)
{
return $callable();
}
function func2(Closure $closure)
{
return $closure();
}
function test()
{
echo "test\n";
}
$demo = function (){
echo 'demo';
};
func1('test'); //输出 test
func1($demo);//输出demo
//报错 Fatal error: Uncaught TypeError: func2(): Argument #1 ($closure) must be of type Closure, string given, called
func2('test');
func2($demo);//输出demo
在PHP>=7.1的版本中有一种写法可以在Closure中调用常规函数
<?php
function func1(callable $callable)
{
return $callable();
}
function func2(Closure $closure)
{
return $closure();
}
function test()
{
echo "test\n";
}
$demo = function (){
echo 'demo';
};
//报错 Fatal error: Uncaught TypeError: func2(): Argument #1 ($closure) must be of type Closure, string given, called
func2('test');
func2(Closure::fromCallable('test'));//输出 test
再来说一下闭包的一些技巧
bindTo 复制当前闭包对象,绑定指定的$this对象和类作用域。
/**
* 声明一个a类
*/
class a{
/**
* @var int 这里声明一个a成员变量 初始值为1
*/
public $a = 1;
}
//这里声明一个闭包函数 函数输出this指向a变量
$closure = function (){
echo $this->a;
$this->a = 3;
};
$a = new a; //实例化a类
//调用闭包中的bindTo方法,将闭包函数内的this指向到实例a
$closure = $closure->bindTo($a);
//执行闭包
$closure();//输出1
var_dump($a->a);//输出3
需要注意的是如果bindTo第二个参数为默认值的时候在闭包内访问的this指针内的成员变量或成员函数的访问权限必须是public
/**
* 声明一个a类
*/
class a{
/**
* @var int 这里声明一个a成员变量 初始值为1
*/
public $a = 1;
protected $b=2;
private $c = 3;
public function a()
{
echo $this->a;
}
protected function b()
{
echo $this->b;
}
private function c()
{
echo $this->c;
}
}
//这里声明一个闭包函数 函数输出this指向a变量
$closure = function (){
echo $this->a;//输出1
echo $this->b;//报错 Fatal error: Uncaught Error: Cannot access protected property a::$b
echo $this->c;//报错 Fatal error: Uncaught Error: Cannot access private property a::$c
echo $this->a();//输出1
echo $this->b();//报错 Fatal error: Uncaught Error: Call to protected method a::b() from scope Closure
echo $this->c();//报错 Fatal error: Uncaught Error: Call to private method a::c() from scope Closure
};
$a = new a; //实例化a类
//调用闭包中的bindTo方法,将闭包函数内的this指向到实例a
$closure = $closure->bindTo($a);
//执行闭包
$closure();
那么如果想在闭包内访问其他私有或受保护的成员变量或成员函数时,就需要用到bindTo第二个参数,默认是static。如果传入当前绑定实例的类名
$closure = $closure->bindTo($a,'a');
这个时候再执行上面的代码
/**
* 声明一个a类
*/
class a{
/**
* @var int 这里声明一个a成员变量 初始值为1
*/
public $a = 1;
protected $b=2;
private $c = 3;
public function a()
{
echo $this->a;
}
protected function b()
{
echo $this->b;
}
private function c()
{
echo $this->c;
}
}
//这里声明一个闭包函数 函数输出this指向a变量
$closure = function (){
echo $this->a;//输出1
echo $this->b;//输出2
echo $this->c;//输出3
$this->a();//输出1
$this->b();//输出2
$this->c();//输出3
};
$a = new a; //实例化a类
//调用闭包中的bindTo方法,将闭包函数内的this指向到实例a
$closure = $closure->bindTo($a,'a');
//执行闭包
$closure();
以上的bindTo写法也可以换成bind写法
$closure = Closure::bind($closure,$a,'a');
执行效果和上面的bindTo方法结果一样
再来谈谈Closure提供的另一个方法 fromCallable 将callable类型转换为Closure类型
callable类型可以是以下任何一种
/**
* 声明一个a类
*/
class a{
/**
* @var int 这里声明一个a成员变量 初始值为1
*/
public $a = 1;
protected $b=2;
private $c = 3;
public function a()
{
echo $this->a;
}
protected function b()
{
echo $this->b;
}
private function c()
{
echo $this->c;
}
public static function t()
{
}
}
function test()
{
};
$a = new a;
is_callable('test');//True
is_callable(function (){});//True
is_callable([$a,'a']);//True
is_callable(['a','t']);//True
那么fromCallable方法提供的作用就是把上面任何一种类型转换为Closure类型
/**
* 声明一个a类
*/
class a{
/**
* @var int 这里声明一个a成员变量 初始值为1
*/
public $a = 1;
protected $b=2;
private $c = 3;
public function a()
{
echo $this->a;
}
protected function b()
{
echo $this->b;
}
private function c()
{
echo $this->c;
}
public static function t()
{
echo 1;
}
}
function test()
{
echo 'test';
};
$a = new a;
$closure = Closure::fromCallable('test');
$closure();//输出test
$closure = Closure::fromCallable(['a','t']);
$closure();//输出1
$closure = Closure::fromCallable([$a,'a']);
$closure();//输出1
世人慌慌张张,不过图碎银几两