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?
已存檔?
是復刻?
已鎖定?
是鏡像?
是私有?