super_closure, 用这个不可思议的PHP库序列化闭包

分享于 

11分钟阅读

GitHub

  繁體 雙語
Wraps a PHP Closure to make it Serializable
  • 源代码名称:super_closure
  • 源代码网址:http://www.github.com/jeremeamia/super_closure
  • super_closure源代码文档
  • super_closure源代码下载
  • Git URL:
    git://www.github.com/jeremeamia/super_closure.git
    Git Clone代码到本地:
    git clone http://www.github.com/jeremeamia/super_closure
    Subversion代码到本地:
    $ svn co --depth empty http://www.github.com/jeremeamia/super_closure
    Checked out revision 1.
    $ cd repo
    $ svn up trunk
    
    PHP SuperClosure - 版本 2

    Total DownloadsBuild StatusMIT LicenseGitter

    用于序列化闭包和匿名函数的PHP库。

    简介

    以前,我尝试序列化一个 PHP Closure 对象。 你可能猜到了,它根本不工作。 事实上,从PHP运行时得到一个非常特定的错误消息:

    不允许带有消息'序列化'闭包的未捕获异常'异常'"'

    尽管序列化闭包是由PHP实现的,但SuperClosure库使它成为可能的 以下是你使用它的方式:

    useSuperClosureSerializer;$serializer=newSerializer();$greeting='Hello';$hello=function ($name='World') use ($greeting) {echo"{$greeting}, {$name}!n";};$hello('Jeremy');//> Hello, Jeremy!$serialized=$serializer->serialize($hello);//.. .$unserialized=$serializer->unserialize($serialized);$unserialized('Jeremy');//> Hello, Jeremy!

    是的非常酷?

    特性

    SuperClosure附带两个不同的闭包分析器,每个都支持关于闭包序列化的不同特性。 TokenAnalyzer 不像 AstAnalyzer 那样鲁棒,但它的速度快于 20 -25倍。 在使用 table below 时,要记住你的闭包代码,应该选择支持所需特性的最快的分析器。

    通过 AstAnalyzer 通过 TokenAnalyzer 支持的功能
    常规闭包( 匿名函数)
    $fn = function (...) {...};
    是的是的
    带有上下文的闭包
    $fn = function () use ($a, $b,.. .) {...};
    是的是的
    递归闭包
    $fn = function () use (&$fn,.. .) {...};
    是的是的
    绑定到对象的闭包
    $fn = function () {$this->something();.. .};
    是的是的
    封闭的对象范围
    $fn = function () {self::something();.. .};
    是的是的
    static 闭包( 例如,保留 `static`-ness )
    $fn = static function () {...};
    是的- -
    参数中带有类 NAME的闭包
    $fn = function (Foo $foo) {...};
    是的- -
    正文中带有 NAME 类的闭包
    $fn = function () {$foo = new Foo;.. .};
    是的- -
    带有魔术常量的闭包
    $fn = function () {$file = __FILE__;.. .};
    是的- -
    效率快速

    警告

    • 对于引用使用的任何变量( 比如, function () use (&$vars, &$like, &$these) {...} ) 在序列化后不维护引用。 唯一的例外是递归闭包引用。
    • 如果在单个行( 你为什么要这么做) 上定义了两个闭包,那么你将无法序列化其中一个,因为它不明确应该解析哪一个闭包。
    • 警告: 需要 eval() 函数才能关闭闭包。 这个函数被认为是危险的,因这里你必须评估使用这里库时可以能需要采取的措施。 你应该只unserialize从可靠来源检索的关闭闭包,否则你将打开代码注入攻击。 如果你打算存储或者传输它们,那么最好对序列化闭包进行签名。 有关如何执行这里操作的详细信息,请阅读签名Closures部分。
    • 无法序列化在 eval() 中定义的闭包具有代码。 这包括序列化一个非串行的闭包。

    分析器

    你可以选择在实例化 Serializer 时要使用的分析器。 如果不指定,则默认使用 AstAnalyzer,因为它具有最强大的功能。

    useSuperClosureSerializer;useSuperClosureAnalyzerAstAnalyzer;useSuperClosureAnalyzerTokenAnalyzer;// Use the default analyzer.$serializer=newSerializer();// Explicitly choose an analyzer.$serializer=newSerializer(newAstAnalyzer());// OR$serializer=newSerializer(newTokenAnalyzer());

    如果你只是想在闭包对象上做一些自省,分析器也是很有用的。 查看使用 AstAnalyzer 时返回的内容:

    useSuperClosureAnalyzerAstAnalyzer;classCalculator{publicfunctiongetAdder($operand) {returnfunction ($number) use ($operand) {return$number+$operand; }; }}$closure= (newCalculator)->getAdder(5);$analyzer=newAstAnalyzer();var_dump($analyzer->analyze($closure));// array(10) {// 'reflection' => class ReflectionFunction#5 (1) {...}// 'code' => string(68)"function ($number) use($operand) {// return $number + $operand;// };"// 'hasThis' => bool(false)// 'context' => array(1) {// 'operand' => int(5)// }// 'hasRefs' => bool(false)// 'binding' => class Calculator#2 (0) {...}// 'scope' => string(10)"Calculator"// 'isStatic' => bool(false)// 'ast' => class PhpParserNodeExprClosure#13 (2) {...}// 'location' => array(8) {// 'class' => string(11)"Calculator"// 'directory' => string(47)"/Users/lindblom/Projects/{...}/SuperClosureTest"// 'file' => string(58)"/Users/lindblom/Projects/{...}/SuperClosureTest/simple.php"// 'function' => string(9)"{closure}"// 'line' => int(11)// 'method' => string(22)"Calculator::{closure}"// 'namespace' => NULL// 'trait' => NULL// }// }

    标记关闭

    SuperClosure版本 2.1允许你在实例化序列化程序时指定签名密钥。 这样做将配置你的序列化程序来签署你序列化的任何闭包,并验证你所选择的任何闭包的签名。 这样做有助于防止代码注入攻击,如果有人篡改了序列化的闭包,可能会发生这种情况。 记住,请记住你的签名密钥。

    $serializer1=newSuperClosureSerializer(null, $yourSecretSigningKey);$data=$serializer1->serialize(function () {echo"Hello!n";});echo$data."n";// %rv9zNtTArySx/1803fgk3rPS1RO4uOPPaoZfTRWp554=C:32:"SuperClosureSerializa...$serializer2=newSuperClosureSerializer(null, $incorrectKey);try {$fn=$serializer2->unserialize($data);} catch (SuperClosureExceptionClosureUnserializationException$e) {echo$e->getMessage() ."n";}// The signature of the closure's data is invalid, which means the serialized// closure has been modified and is unsafe to unserialize.

    安装

    要使用 Composer 在项目中安装超级关闭库,只需使用 Composer:

    $ composer require jeremeamia/superclosure

    你当然可以手动更新你的要求 block,如果你选择:

    {
     "require": {
     "jeremeamia/superclosure": "^2.0" }
    }

    有关如何使用 Composer的更多信息,请访问 Composer 主页

    为什么我需要序列化一个闭包?

    既然你在这里看这个自述文件,你可以能已经注意到了一个用例。 即使这个概念开始作为一个实验,但是已经有一些用例出现在野外。

    例如在关于使用 UserScape ( ) 和视频中,关于如何将闭包推到队列中,使工作人员可以执行一个闭包,这样就可以执行一个命令。 this,因为你不必为一个可能很简单的工作创建一个完整的类。

    或者。你可能有一个依赖注入容器或者路由器对象,它是通过编写闭包来构建的。 如果想缓存它,就需要能够序列化它。

    但是,一般情况下,应该避免序列化闭包。

    告诉我这个项目是如何启动

    在 2010开始时,当 PHP 5.3开始获得牵引力时,一切都开始了。 尽管PHP不允许我做,但是我设法证明序列化一个闭包是可以完成的。 i i Extending Extending的博客文章 Extending 5.3闭包,展示了我以前的雇主的博客,展示了如何做到这一点的。 我还在GitHub上发布了代码。

    此后,我在代码上做了一些迭代,最近的迭代更加健壮,这归功于groovy的 nikic/php解析器库的使用。

    ,使用 SuperClosure?

    • Laravel 序列化一个关闭以潜在地推送到作业队列。
    • 针对PHP的HTTP模拟- 序列化一个闭包,以便在测试工作流中发送到远程服务器。
    • 跳线 - 序列化一个关闭,通过SSH在远程主机上运行。
    • 使用 ClosureParser 来显示关闭代码的基准代码。
    • florianv/business - 序列化特殊天数以存储业务日定义。
    • zumba/JSON序列化程序将PHP变量序列化为JSON格式。
    • PHP di - 将闭包定义编译为优化的PHP代码。
    • 请告诉我你的项目是否使用超级闭包。

    今年已经介绍了 Opis闭包插件库,这也提供了序列化闭包的能力。 你也应该看看它,看看哪一个适合你的需要最好的。


    PHP  CLOS  serialize  UNC  closure  Closures  
    相关文章