草庐IT

回调函数和闭包(匿名函数)以及闭包的序列化

PENG先森_晓宇 2023-03-28 原文

回调函数

回调函数:Callback (即call then back 被主函数调用运算后会返回主函数),是指通过函数参数传递到其它代码的,某一块可执行代码的引用。

回调函数其实就是callback类型,在方法中可以使用callable关键字来申明形参是回调函数;在方法体内可以使用is_callable($entry)方法来判断是否可回调。

 public function with(callable $entry){
  if(is_callable($entry)){
           //TODO
       }else{
           throw new Exception('参数entry不是回调函数')
       }
  }

到底怎么使用回调函数呢?其实很多例子

  1. 例如我们最常用的array_filter(),可以看到第二个参数就是一个函数,也就是第二个参数其实就是一个回调函数。
$number=[1,2,'a'];
$filter=array_filter($number,function ($value){
    if(is_numeric($value)){
        return true;
    }else{
        return false;
    }
});
  1. 比如我们需要执行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是不允许序列化的,那如果我们遇到了序列化的场景怎么办

  1. 安装序列化composer包
composer require opis/closure
  1. 序列化闭包
//(2).序列化闭包函数,输出序列化后的字符串
$b = \Opis\Closure\serialize($url);

//(3).反序列化闭包函数,执行还原的闭包函数
$c = \Opis\Closure\unserialize($b);
$c();

有关回调函数和闭包(匿名函数)以及闭包的序列化的更多相关文章

  1. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  2. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  3. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  4. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  5. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  8. ruby - 在匿名 block 中产生 - 2

    我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi

  9. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  10. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

随机推荐