定制 td7650/superclosure 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

td7650/superclosure

Composer 安装命令:

composer require td7650/superclosure

包简介

Serialize Closure objects, including their context and binding

README 文档

README

Total Downloads Build Status MIT License Code Climate GratiPay ![Gitter](https://badges.gitter.im/Join Chat.svg)

A PHP Library for serializing closures and anonymous functions.

Introduction

Once upon a time, I tried to serialize a PHP Closure object. As you can probably guess, it doesn't work at all. In fact, you get a very specific error message from the PHP Runtime:

Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed'

Even though serializing closures is "not allowed" by PHP, the SuperClosure library makes it possible. Here's the way you use it:

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!

Yep, pretty cool, right?

Features

SuperClosure comes with two different Closure Analyzers, which each support different features regarding the serialization of closures. The TokenAnalyzer is not as robust as the AstAnalyzer, but it is around 20-25 times faster. Using the table below, and keeping in mind what your closure's code looks like, you should choose the fastest analyzer that supports the features you need.

Supported Features Via AstAnalyzer Via TokenAnalyzer
Regular closures (anonymous functions)
$fn = function (...) {...};
Yes Yes
Closures with context
$fn = function () use ($a, $b, ...) {...};
Yes Yes
Recursive closures
$fn = function () use (&$fn, ...) {...};
Yes Yes
Closures bound to an object
$fn = function () {$this->something(); ...};
Yes Yes
Closures scoped to an object
$fn = function () {self::something(); ...};
Yes Yes
Static closures (i.e, preserves the `static`-ness)
$fn = static function () {...};
Yes --
Closures with class name in params
$fn = function (Foo $foo) {...};
Yes --
Closures with class name in body
$fn = function () {$foo = new Foo; ...};
Yes --
Closures with magic constants
$fn = function () {$file = __FILE__; ...};
Yes --
Performance Slow Fast

Caveats

  1. For any variables used by reference (e.g., function () use (&$vars, &$like, &$these) {…}), the references are not maintained after serialization. The only exception to this is recursive closure references.
  2. If you have two closures defined on a single line (why would you do this anyway?), you will not be able to serialize either one since it is ambiguous which closure's code should be parsed (they are anonymous functions after all).
  3. Warning: The eval() function is required to unserialize the closure. This functions is considered dangerous by many, so you will have to evaluate what precautions you may need to take when using this library. You should only unserialize closures retrieved from a trusted source, otherwise you are opening yourself up to code injection attacks. It is a good idea sign serialized closures if you plan on storing or transporting them. Read the Signing Closures section below for details on how to do this.
  4. Cannot serialize closures that are defined within eval()'d code.

Analyzers

You can choose the analyzer you want to use when you instantiate the Serializer. If you do not specify one, the AstAnalyzer is used by default, since it has the most capabilities.

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());

Analyzers are also useful on their own if you are just looking to do some introspection on a Closure object. Check out what is returned when using the 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
//   }
// }

Signing Closures

Version 2.1+ of SuperClosure allows you to specify a signing key, when you instantiate the Serializer. Doing this will configure your Serializer to sign any closures you serialize and verify the signatures of any closures you unserialize. Doing this can help protect you from code injection attacks that could potentially happen if someone tampered with a serialized closure. Remember to keep your signing key secret.

$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.

Installation

To install the Super Closure library in your project using Composer, first add the following to your composer.json config file.

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

Then run Composer's install or update commands to complete installation. Please visit the Composer homepage for more information about how to use Composer.

Why would I need to serialize a closure?

Well, since you are here looking at this README, you may already have a use case in mind. Even though this concept began as an experiment, there have been some use cases that have come up in the wild.

For example, in a video about Laravel and IronMQ by UserScape, at about the 7:50 mark they show how you can push a closure onto a queue as a job so that it can be executed by a worker. This is nice because you do not have to create a whole class for a job that might be really simple.

Tell me about how this project started

It all started back in the beginning of 2010 when PHP 5.3 was starting to gain traction. I set out to prove that serializing a closure could be done, despite that PHP wouldn't let me do it. I wrote a blog post called Extending PHP 5.3 Closures with Serialization and Reflection on my former employers' blog, HTMList, showing how it could be done. I also released the code on GitHub.

Since then, I've made a few iterations on the code, and the most recent iterations have been more robust, thanks to the usage of the fabulous nikic/php-parser library.

Who is using SuperClosure?

  • Laravel - Serializes a closure to potentially push onto a job queue.
  • HTTP Mock for PHP - Serialize a closure to send to remote server within a test workflow.
  • Jumper - Serialize a closure to run on remote host via SSH.
  • nicmart/Benchmark - Uses the ClosureParser to display a benchmarked Closure's code.
  • florianv/business - Serializes special days to store business days definitions.
  • Please let me know if and how your project uses Super Closure.

td7650/superclosure 适用场景与选型建议

td7650/superclosure 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 58 次下载、GitHub Stars 达 0, 最近一次更新时间为 2015 年 09 月 14 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「serialize」 「parser」 「function」 「closure」 「tokenizer」 「serializable」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 td7650/superclosure 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 td7650/superclosure 我们能提供哪些服务?
定制开发 / 二次开发

基于 td7650/superclosure 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 58
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 17
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 1
  • Forks: 94
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2015-09-14