super_closure

Serialize closures with this uncanny PHP library.

  • 所有者: jeremeamia/super_closure
  • 平台:
  • 许可证: MIT License
  • 分类:
  • 主题:
  • 喜欢:
    0
      比较:

Github星跟踪图

PHP SuperClosure – Version 2

Total Downloads
Build Status
MIT License
Gitter

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.

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 function 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. This
    includes re-serializing a closure that has been unserialized.

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, simply
require the project with Composer:

$ composer require jeremeamia/superclosure

You may of course manually update your require block if you so choose:

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

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.

Or... you might have a dependency injection container or router object that is
built by writing closures. If you wanted to cache that, you would need to be
able to serialize it.

In general, however, serializing closures should probably be avoided.

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.
  • zumba/json-serializer - Serializes PHP variables into JSON format.
  • PHP-DI - Compiles closure definitions into optimized PHP code.
  • Please let me know if and how your project uses Super Closure.

Alternatives

This year the Opis Closure library has been introduced, that also provides
the ability to serialize a closure. You should check it out as well and see
which one suits your needs the best.

主要指标

概览
名称与所有者jeremeamia/super_closure
主编程语言PHP
编程语言Makefile (语言数: 2)
平台
许可证MIT License
所有者活动
创建于2010-01-20 21:15:07
推送于2023-07-21 07:03:17
最后一次提交2021-03-29 08:20:09
发布数15
最新版本名称2.4.0 (发布于 )
第一版名称0.9.0 (发布于 )
用户参与
星数1.7k
关注者数33
派生数93
提交数176
已启用问题?
问题数48
打开的问题数0
拉请求数48
打开的拉请求数1
关闭的拉请求数18
项目设置
已启用Wiki?
已存档?
是复刻?
已锁定?
是镜像?
是私有?