td7650 / superclosure
序列化闭包对象,包括其上下文和绑定
Requires
- php: >=5.3
- nikic/php-parser: ~1.2
Requires (Dev)
- codeclimate/php-test-reporter: ~0.1.2
- phpunit/phpunit: ~4.0
This package is not auto-updated.
Last update: 2024-09-28 18:34:59 UTC
README

一个用于序列化闭包和匿名函数的PHP库。
简介
曾经,我尝试序列化一个PHP Closure
对象。正如你可能会猜到的,它根本不起作用。实际上,你会从PHP运行时得到一个非常具体的错误消息
未捕获的异常 'Exception',消息为 '不允许序列化 'Closure''
尽管PHP不允许序列化闭包,但SuperClosure库使这成为可能。以下是使用方法
use SuperClosure\Serializer; $serializer = new Serializer(); $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附带两个不同的 闭包分析器,每个分析器都支持不同方面的闭包序列化。与 AstAnalyzer
相比,TokenAnalyzer
不那么健壮,但速度快大约20-25倍。使用下表,并考虑到你的闭包代码看起来像什么,你应该选择一个支持你所需功能的 最快分析器。
注意事项
- 对于任何通过引用使用的变量(例如,
function () use (&$vars, &$like, &$these) {…}
),在序列化后不会保留引用。唯一的例外是递归闭包引用。 - 如果你在一行中定义了两个闭包(你为什么要这样做呢?),你将无法序列化任何一个,因为不清楚应该解析哪个闭包的代码(毕竟它们是 匿名函数)。
- 警告:需要
eval()
函数来反序列化闭包。许多人都认为这个函数很危险,因此你必须评估在使用此库时可能需要采取哪些预防措施。你应该只从可信源检索闭包进行反序列化,否则你将面临代码注入攻击的风险。如果计划存储或传输序列化的闭包,最好对其进行签名。以下是关于如何签名的详细信息。 - 不能序列化在
eval()
代码中定义的闭包。
分析器
在实例化 Serializer
时,你可以选择你想要使用的分析器。如果没有指定,默认使用 AstAnalyzer
,因为它具有最丰富的功能。
use SuperClosure\Serializer; use SuperClosure\Analyzer\AstAnalyzer; use SuperClosure\Analyzer\TokenAnalyzer; // Use the default analyzer. $serializer = new Serializer(); // Explicitly choose an analyzer. $serializer = new Serializer(new AstAnalyzer()); // OR $serializer = new Serializer(new TokenAnalyzer());
分析器本身也很有用,如果你只是想对闭包对象进行一些内省。看看使用 AstAnalyzer
返回了什么
use SuperClosure\Analyzer\AstAnalyzer; class Calculator { public function getAdder($operand) { return function ($number) use ($operand) { return $number + $operand; }; } } $closure = (new Calculator)->getAdder(5); $analyzer = new AstAnalyzer(); 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 PhpParser\Node\Expr\Closure#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+ 版本允许您在实例化 Serializer 时指定签名密钥。这样做将配置您的 Serializer 对序列化的任何闭包进行签名,并验证反序列化的任何闭包的签名。这样做可以帮助您防止潜在的代码注入攻击,如果有人篡改了序列化的闭包,则可能会发生此类攻击。请记住,要保密您的签名密钥。
$serializer1 = new SuperClosure\Serializer(null, $yourSecretSigningKey); $data = $serializer1->serialize(function () {echo "Hello!\n";}); echo $data . "\n"; // %rv9zNtTArySx/1803fgk3rPS1RO4uOPPaoZfTRWp554=C:32:"SuperClosure\Serializa... $serializer2 = new SuperClosure\Serializer(null, $incorrectKey); try { $fn = $serializer2->unserialize($data); } catch (SuperClosure\Exception\ClosureUnserializationException $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 在项目中安装 Super Closure 库,首先将以下内容添加到您的 composer.json
配置文件中。
{ "require": { "jeremeamia/superclosure": "~2.0" } }
然后运行 Composer 的安装或更新命令以完成安装。有关如何使用 Composer 的更多信息,请访问Composer 主页。
我为什么需要序列化闭包?
嗯,既然您在这里查看这份 README,您可能已经有了某个用例在心中。尽管这个概念最初是一个实验,但在野外已经出现了一些用例。
例如,在 UserScape(UserScape)关于 Laravel 和 IronMQ 的视频中,大约在第 7:50 处,他们展示了如何将闭包推送到队列作为作业,以便由工作进程执行。这很棒,因为您不需要为可能非常简单的作业创建整个类。
告诉我这个项目是如何开始的
这一切始于 2010 年初,当时 PHP 5.3 开始流行。我着手证明即使 PHP 不允许我这样做,序列化闭包也可以实现。我在前雇主博客 HTMList 上写了一篇名为 使用序列化和反射扩展 PHP 5.3 闭包 的博客文章,展示了如何实现。我还将代码发布在 GitHub 上。
从那时起,我对代码进行了一些迭代,最近的迭代由于使用了令人惊叹的 nikic/php-parser 库而变得更加健壮。
谁在使用 SuperClosure?
- Laravel - 将闭包序列化,以便可能将其推送到作业队列。
- PHP 的 HTTP Mock - 将闭包序列化,以便在测试工作流程中发送到远程服务器。
- Jumper - 将闭包序列化,以便通过 SSH 在远程主机上运行。
- nicmart/Benchmark - 使用
ClosureParser
显示基准测试闭包的代码。 - florianv/business - 将特殊日期序列化以存储工作日定义。
- 请告诉我您的项目是否以及如何使用 Super Closure。