Serializing or hashing closures in PHP

This is required to ask design questions, but I want to serialize or hash the closure in PHP so that I have a unique identifier for this closure.

I don’t need to miss the closure from this, I just need a unique identifier for it, accessible from inside and outside the closure itself, that is, the method that takes closer will have to generate an id for this closure, and the closure itself will need to be able to generate the same id

Things I've tried so far:

$someClass = new SomeClass(); $closure1 = $someClass->closure(); print $closure1(); // Outputs: I am a closure: {closure} print $someClass->closure(); // Outputs: Catchable fatal error: Object of class Closure could not be converted to string print serialize($closure1); // Outputs: Fatal error: Uncaught exception 'Exception' with message 'Serialization of 'Closure' is not allowed' class SomeClass { function closure() { return function () { return 'I am a closure: ' . __FUNCTION__; }; } } 

The Reflection API does not seem to offer anything that I could use to create an identifier.

+6
source share
7 answers

My solution is more general and takes into account the static parameters for closing. To do the trick, you can pass the closure link inside the closure:

 class ClosureHash { /** * List of hashes * * @var SplObjectStorage */ protected static $hashes = null; /** * Returns a hash for closure * * @param callable $closure * * @return string */ public static function from(Closure $closure) { if (!self::$hashes) { self::$hashes = new SplObjectStorage(); } if (!isset(self::$hashes[$closure])) { $ref = new ReflectionFunction($closure); $file = new SplFileObject($ref->getFileName()); $file->seek($ref->getStartLine()-1); $content = ''; while ($file->key() < $ref->getEndLine()) { $content .= $file->current(); $file->next(); } self::$hashes[$closure] = md5(json_encode(array( $content, $ref->getStaticVariables() ))); } return self::$hashes[$closure]; } } class Test { public function hello($greeting) { $closure = function ($message) use ($greeting, &$closure) { echo "Inside: ", ClosureHash::from($closure), PHP_EOL, "<br>" ; }; return $closure; } } $obj = new Test(); $closure = $obj->hello('Hello'); $closure('PHP'); echo "Outside: ", ClosureHash::from($closure), PHP_EOL, "<br>"; $another = $obj->hello('Bonjour'); $another('PHP'); echo "Outside: ", ClosureHash::from($another), PHP_EOL, "<br>"; 
+3
source

Ok, here is the only thing I can think of:

 <?php $f = function() { }; $rf = new ReflectionFunction($f); $pseudounique = $rf->getFileName().$rf->getEndLine(); ?> 

If you like, you can use it with md5 or something else. However, if a function is generated from a string, you must sow it with uniqid()

+5
source

You could all you need to write your own, your own closures having getId() or getHash() or something else.

Example ( Demo ):

 1: Hello world 2: Hello world 

First close (ID: 1), identifier read in the context of the call. Second closure (ID: 2), ID is read from the closure (where self-reference).

code:

 <?php /** * @link http://stackoverflow.com/questions/13983714/serialize-or-hash-a-closure-in-php */ class IdClosure { private $callback; private $id; private static $sequence = 0; final public function __construct(Callable $callback) { $this->callback = $callback; $this->id = ++IdClosure::$sequence; } public function __invoke() { return call_user_func_array($this->callback, func_get_args()); } public function getId() { return $this->id; } } $hello = new IdClosure(function($text) { echo "Hello $text\n";}); echo $hello->getId(), ": ", $hello('world'); $hello2 = new IdClosure(function($text) use (&$hello2) { echo $hello2->getId(), ": Hello $text\n";} ); $hello2('world'); 

I have no clue if this suits your needs, maybe this gives you some ideas. I suggested spl_object_hash , but didn’t understand the discussion of why it doesn’t do this or it works in the end.

+5
source

PHP anonymous functions are displayed as instances of the Closure class . Since they are mainly objects, spl_object_hash will return a unique identifier when passing it. In the PHP interactive prompt:

 php > $a = function() { echo "I am A!"; }; php > $b = function() { echo "I am B!"; }; php > php > php > echo spl_object_hash($a), "\n", spl_object_hash($b), "\n"; 000000004f2ef15d000000003b2d5c60 000000004f2ef15c000000003b2d5c60 

These identifiers may look the same, but they differ by one letter in the middle.

The identifier is good only for this request, so expect it to change between calls, even if the function and any use'd variables are not changed.

+3
source

Superclosure provides a convenience class that allows, among other things, to serialize / non-serialize closures.

+2
source

It looks like you want to generate a signature. Creating a signature from outside the closure will be virtually impossible to reproduce if the closure accepts any parameters. Past data will modify the generated signature.

 $someClass = new SomeClass(); $closure1 = $someClass->closure(); $closure1_id = md5(print_r($closure1, true)); 

Even if your closure does not accept parameters, you still have a problem with storing and keeping the signature inside the closure. Perhaps you can do something with the static variable inside the closure, so it only initializes once and saves the "signature". But it becomes useless how to get it.

It sounds like you want a class, not a closure. This would solve all these problems. You can pass the “salt” when creating the instance and make it generate a signature using the salt (i.e., a random number). This will make the signature unique. Then you can save this salt, recreate the class using the same constructor parameters (i.e. the Salt) and compare this with the signature in the file in the class you already created.

+1
source

Possible solution obtained with @hakre and @dualed:

 $someClass = new SomeClass(); $closure = $someClass->closure(); $closure2 = $someClass->closure2(); $rf = new ReflectionFunction($closure); $rf2 = new ReflectionFunction($closure2); print spl_object_hash($rf); // Outputs: 000000007ddc37c8000000003b230216 print spl_object_hash($rf2); // Outputs: 000000007ddc37c9000000003b230216 class SomeClass { function closure() { return function () { return 'I am closure: ' . __FUNCTION__; }; } function closure2() { return function () { return 'I am closure: ' . __FUNCTION__; }; } } 
0
source

All Articles