回调函数和闭包(匿名函数)以及闭包的序列化
回调函数
回调函数:Callback (即call then back 被主函数调用运算后会返回主函数),是指通过函数参数传递到其它代码的,某一块可执行代码的引用。
回调函数其实就是callback类型
,在方法中可以使用callable
关键字来申明形参是回调函数;在方法体内可以使用is_callable($entry)
方法来判断是否可回调。
public function with(callable $entry){
if(is_callable($entry)){
//TODO
}else{
throw new Exception('参数entry不是回调函数')
}
}
到底怎么使用回调函数呢?其实很多例子
- 例如我们最常用的array_filter(),可以看到第二个参数就是一个函数,也就是第二个参数其实就是一个回调函数。
$number=[1,2,'a'];
$filter=array_filter($number,function ($value){
if(is_numeric($value)){
return true;
}else{
return false;
}
});
- 比如我们需要执行mysql的事务,我们通常的做法是,如下:
class mysql
{
public function init()
{
try{
$con=new Con();
$con->startTrasnaction();
//TODO
$con->commit();
}catch (\Exception $e){
$con->rollBack();
}
}
}
$a = new mysql();
$a->init();
缺点就是:每次都需要重复写一些new Con(),startTrasnaction(),commit(),rollBack()的代码,所以这里我就可以使用回调函数的方式来轻松降低代码的重复复,如下:
class mysql
{
public function init()
{
$a=$this->a;
$b=$this->b;
$con = new SrmCon();//con类的子类
self::transaction(function ()use ($con){
//TODO
},$con);
}
public static function transaction(callable $func,Con $con)
{
if(is_callable($func)){
try{
$con->startTrasnaction();
//call_user_func_array()
call_user_func($func);//调用回调函数
$con->commit();
}catch (\Exception $e){
$con->rollBack();
}
}else{
throw new Exception('参数不是回调函数');
}
}
}
////回调函数
$a = new mysql();
$a->init();
通过封装了transaction()方法,在执行一些事务操作时再也不需要写startTrasnaction、commit、rollBack这些了。
transaction这个方法的封装,有俩大特点需要注意下:
- 创建不同的数据库需要不同的con类,所以这里让用户自行实现,第二个参数指定为con类。
-
call_user_func_array(callable
$callback
, array$param_arr
): mixed
调用回调函数,并把一个数组参数作为回调函数的参数 -
call_user_func(callable
$callback
): mixed 也是调用回调函数的,和上面那个的区别就是这里没有第二个参数,只有一个回调函数参数。 - call_user_func([
$
this, 'test'], ...$this->params),其实call_user_func和call_user_func_array函数并不是只可调用回调函数,普通的方法也可以调。像这个表达式指的是调用本类的test方法,省去了new实例了
闭包(匿名函数)
PHP将匿名函数和闭包视作相同的概念,下面统称闭包,顾名思义就是没有名字的函数。
闭包的定义
匿名函数通常用在回调函数中,同时匿名函数也可以赋值给一个变量后使用,还能像其他任何 PHP 对象那样传递,不过匿名函数仍然是函数,因此可以调用,并且可以传入参数。
闭包函数的定义通常有以下几种方法
- 直接赋值:
$func_name = function($arg){statement}
; - 直接使用匿名函数,在参数处直接定义函数,不赋给具体的变量值,也就是上面回调函数定义的方式;
我们以第一种方式定义一个闭包函数,匿名函数可以作为变量的值
来使用。此时 PHP 会自动把此种表达式转换成内置类Closure
的对象实例
<?php
$url = function (){
return 'http://c.biancheng.net/php/';
};
?>
判断是不是闭包,有俩种方式,通过Closure
内置类或者is_callable
方法都可以判断
if($url instanceof Closure){
echo '是匿名函数';
}else{
echo '不是匿名函数';
}
if(is_callable($url)){
echo '是匿名函数';
}else{
echo '不是匿名函数';
}
闭包的调用
闭包说白了还是一个函数,所以调用闭包函数是在变量后面需要加上()
,可能变量后面加()有点不适应,按照正常的思维,变量是直接调用,所以看到变量后面加()的,就知道该变量代表的是一个匿名函数。
echo $url();
闭包的序列化
$b=serialize($url);
$c=unserialize($url);
var_dump($c);
会报错
Fatal error: Uncaught Exception: Serialization of 'Closure' is not allowed in /Users/sftc/workerDir/sf-odp-2.0/script/xulie.php:114
可以看出闭包在php是不允许序列化的,那如果我们遇到了序列化的场景怎么办
- 安装序列化composer包
composer require opis/closure
- 序列化闭包
//(2).序列化闭包函数,输出序列化后的字符串
$b = \Opis\Closure\serialize($url);
//(3).反序列化闭包函数,执行还原的闭包函数
$c = \Opis\Closure\unserialize($b);
$c();