Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在PHP中序列化或散列闭包_Php_Serialization_Closures - Fatal编程技术网

在PHP中序列化或散列闭包

在PHP中序列化或散列闭包,php,serialization,closures,Php,Serialization,Closures,这必然会引起设计问题,但我想在PHP中序列化或散列一个闭包,这样我就有了该闭包的唯一标识符 我不需要能够从中调用闭包,我只需要它的唯一标识符,该标识符可以从闭包本身的内部和外部访问,即接受闭包的方法需要为该闭包生成id,闭包本身需要能够生成相同的id 到目前为止我已经尝试过的事情: $someClass = new SomeClass(); $closure1 = $someClass->closure(); print $closure1(); // Outputs: I am a

这必然会引起设计问题,但我想在PHP中序列化或散列一个闭包,这样我就有了该闭包的唯一标识符

我不需要能够从中调用闭包,我只需要它的唯一标识符,该标识符可以从闭包本身的内部和外部访问,即接受闭包的方法需要为该闭包生成id,闭包本身需要能够生成相同的id

到目前为止我已经尝试过的事情:

$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__; };
    }
}

反射API似乎也没有提供我可以用来创建ID的任何东西。

听起来像是要生成签名。如果闭包接受任何参数,那么从闭包外部创建签名几乎不可能重现。传入的数据将更改生成的签名

$someClass = new SomeClass();
$closure1 = $someClass->closure();
$closure1_id = md5(print_r($closure1, true));
即使您的闭包不接受参数,您仍然存在在闭包中存储和持久化签名的问题。您可能可以使用闭包中的静态变量执行某些操作,这样它只初始化一次并保留“签名”。但是如何取回它会变得很麻烦

听起来你真的想要一门课,而不是结束。它将解决所有这些问题。您可以在实例化时传入一个“salt”,并让它使用salt生成一个签名(即一个随机数)。这将使签名独一无二。然后,您可以保留该salt,使用完全相同的构造函数参数(即salt)重新创建一个类,并将其与已创建的类中的文件签名进行比较。

PHP作为的实例公开。因为它们基本上都是对象,所以当递给它们时,它们将返回一个唯一的标识符。从PHP交互式提示符:

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

这些标识符可能看起来相同,但是它们在中间有一个字母不同。


标识符仅适用于该请求,因此,即使函数和任何
使用的
'd变量没有改变,也希望它在调用之间发生变化。

好的,我能想到的只有一件事:

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


如果你喜欢,你可以用md5或者其他什么东西来散列它。但是,如果函数是从字符串生成的,则应使用
uniqid()

在@hakre和@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__; };
    }
}

您可以编写自己需要的所有代码,您自己的闭包有一个
getId()
getHash()
或任何东西

示例():

第一个闭包(ID:1),在调用上下文中读取ID。第二个闭包(ID:2),从闭包内读取的ID(自引用)

代码:


我的解决方案更通用,并且尊重闭包的静态参数。要实现此技巧,可以在闭包内传递对闭包的引用:

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>";
类ClosureHash
{
/**
*哈希表
*
*@var SplObjectStorage
*/
受保护的静态$hashes=null;
/**
*返回闭包的哈希值
*
*@param可调用$closure
*
*@返回字符串
*/
来自的公共静态函数(Closure$Closure)
{
如果(!self::$hashes){
self::$hashes=new SplObjectStorage();
}
if(!isset(self::$hashes[$closure])){
$ref=新的ReflectionFunction($closure);
$file=newsplfileobject($ref->getFileName());
$file->seek($ref->getstartine()-1);
$content='';
而($file->key()<$ref->getEndLine()){
$content.=$file->current();
$file->next();
}
self::$hashes[$closure]=md5(json_编码(数组(
$content,
$ref->getStaticVariables()
)));
}
返回self::$hash[$closure];
}
}
课堂测试{
公共功能hello($greeting)
{
$closure=函数($message)使用($greeting,&$closure){
echo“Inside:”,ClosureHash::from($closure),PHP_EOL,“
”; }; 返回$closure; } } $obj=新测试(); $closure=$obj->hello('hello'); $closure('PHP'); echo“Outside:”,ClosureHash::from($closure),PHP_EOL,“
”; $other=$obj->hello('Bonjour'); $other('PHP'); echo“Outside:”,ClosureHash::from($other),PHP_EOL,“
”;
提供了一个方便的类,允许您序列化/取消序列化闭包等。

我相信您不能在PHP中以可靠的方式散列
闭包
实例,因为您无法访问属于函数体的AST中的大多数符号

就我所知,只有
闭包使用的外部范围变量
,类型
T_变量
的函数体中的符号(
$a
$b
等),类型信息和函数签名可以以各种方式进行说明。如果没有关于函数体的重要信息,当应用于
闭包的实例时,哈希函数不可能以幂等方式运行

spl\u object\u hash
spl\u object\u id
不会保存您的数据——可能(在实际应用程序中几乎经常)更改refcount会使问题复杂化,因此这些函数通常也不是幂等函数

散列
闭包
实例的唯一可能情况是,它已在某个PHP源文件中定义,并且您当前的实例不使用其外部作用域中的其他
闭包
实例。在这种情况下,通过将
Closure
实例包装到
ReflectionFunction
实例中,您可能会获得一些成功。现在,您可以尝试获取声明
闭包的文件名和行号。然后,您可以加载源文件并提取行号之间的部分,将该部分转储为字符串,并使用
token\u get\u all()
标记它。接下来移除不属于
Closure
声明的标记,并查看外部标记
<?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');
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>";
$zhash = function ($input, callable $hash = null, callable $ob_callback = null) {
    if (\is_scalar($input)) {
        return \is_callable($hash) ? $hash($input) : \hash('md5', $input);
    }

    \ob_start(
        \is_callable($ob_callback) ? $ob_callback : null,
        4096,
        PHP_OUTPUT_HANDLER_STDFLAGS
    );
    \debug_zval_dump($input);
    $dump = \ob_get_clean();

    return \is_callable($hash) ? $hash($dump) : \hash('md5', $dump);
};

$zhash_algo_gz = function ($input, string $algo = 'sha256', int $compress = -1) use ($zhash) {
    return $zhash(
        $input,
        function ($data) use ($algo) {
            return \hash($algo, $data);
        },
        function ($data) use ($compress) {
            return \gzcompress($data, $compress, ZLIB_ENCODING_GZIP);
        }
    );
};
$b = 42;
$zhash_algo_gz(function ($a) use ($b) { return $a * $b + 5; });
$zhash_algo_gz(function ($a) use ($b) { return $a * $b + 6; });
a0cd0738ea01d667c9386d4d9fe085cbc81c0010f30d826106c44a884caf6184
a0cd0738ea01d667c9386d4d9fe085cbc81c0010f30d826106c44a884caf6184
$f1 = function ($a) use ($b) { return $a * $b + 5; });
$f2 = function ($a) use ($b) { return $a * $b + 6; });
$zhash_algo_gz($f1);
$zhash_algo_gz($f2);
085323126d01f3e04dacdbb6791f230d99f16fbf4189f98bf8d831185ef13b6c
18a9c0b26bf6f6546d08911d7268abba72e1d12ede2e9619d782deded922ab65
use function Opis\Closure\{serialize as opisSerialize, unserialize as opisUnserialize};


    $serialized = opisSerialize(new SerializableClosure($closure));
    $wrapper = opisUnserialize($serialized);