Serialize or Hash a Closure in PHP
My solution is more general and respects static parameters for closure. To make the trick, you can pass a reference to the closure 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>";
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 hash it with md5 or whatnot. If the function is generated from a string however, you should seed that with a uniqid()
You could all that you need write your own, your own closures having a getId()
or getHash()
or whatever.
Example (Demo):
1: Hello world
2: Hello world
First closure (ID: 1), ID read in calling context. Second closure (ID: 2), ID read from within 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 that suits your needs, maybe it gives you some ideas. I suggested spl_object_hash
but didn't understood the discussion much why it does not or in the end then does work.