Cara menggunakan php closure vs callable
Once upon a time, I tried to serialize a PHP Show
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? FeaturesSuperClosure comes with two different Closure Analyzers, which each support different features regarding the serialization of closures. The AstAnalyzer Via TokenAnalyzer Regular closures (anonymous functions)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());1YesYesClosures with context 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());2YesYesRecursive closures 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());3YesYesClosures bound to an object 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());4YesYesClosures scoped to an object 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());5YesYesStatic closures (i.e, preserves the `static`-ness) 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());6Yes--Closures with class name in params 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());7Yes--Closures with class name in body 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());8Yes--Closures with magic constants 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());9Yes--PerformanceSlowFast Caveats
AnalyzersYou can choose the analyzer you want to use when you instantiate the 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 // } // }3. 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 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 ClosuresVersion 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. InstallationTo 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 startedIt 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?
AlternativesThis 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. If you wish to export your closures as executable PHP code instead, you can check out the brick/varexporter library. |