将变量传递给__wakeup()

3
我正在将某些对象存储在数据库中。数据库中的每个节点都携带一个序列化的对象。该节点可以有子节点,因此该对象也可以有子对象。因此,我想要在对象中填充一个名为$children的属性,其中包含其直接子对象的数组。当反序列化对象时,$children应该被填充
为了避免从外部进行操作,我认为最好让对象自己来完成这个任务。但是,该对象不知道它属于哪个数据库条目。因此,当调用__wakeup()时,该对象缺少加载其自己对应的数据库条目所需的信息。
最好的方法似乎是能够向__wakeup()传递一个变量,尽管我知道unserialize()没有提供这样的选项。
我找不到任何关于这个问题的信息。我几乎可以得出结论,除了使用全局变量之外,不可能实现这一点,但我不会选择这种方法。
这是否可能?如果可以,怎么做呢?
1个回答

3
魔术方法 __wakeup() 不允许传递参数。这里的想法是在序列化形式中已经包含对象需要的一切,以便将自身反序列化为有效状态,例如,它期望在序列化时对象处于有效状态。
这意味着,如果缺少指示子项属于哪个数据库条目的信息,但该信息对于对象处于有效状态是必需的,则应首先将其添加到对象中。然后,在反序列化对象时可以简单地访问该值。这将是最清晰的解决方案。
另一个选择是使用全局变量。我同意,没有人希望那样做。
还有另一个同样可疑且难以置信的魔术选项是在反序列化对象之前操纵序列化形式以添加缺失的信息。
这是一个概念证明:
function unserialize_with_args($string, array $args, $key = 'tmp')
{
    // serialize $args to $key for inserting into $string
    $tmps = sprintf(
        's:%d:"%s";%s}',
        strlen($key),
        $key,
        serialize($args)
    );

    // increase property count in original string
    $string = preg_replace_callback(
        '/^(O:\d+:"[a-z]+":)(\d+)/i',
        function (array $matches) {
            return $matches[1] . ($matches[2] + 1);
        },
        $string
    );

    // insert $tmps, unserialize, remove tmps
    $instance = unserialize(substr_replace($string, $tmps, -1));
    unset($instance->$key);

    return $instance;
}

使用它,您可以做到:

class Foo
{
    public function __wakeup()
    {
        print_r($this->tmp);
    }
}

unserialize_with_args(
    serialize(new Foo), 
    [1,2,3]
);

运行该代码将打印出(demo):
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
)

这种方法有效是因为该函数向序列化表单中添加了一个属性tmp,该属性保存了作为$args传递的序列化数组。因此,当对象被唤醒时,它将可以访问这些值。在反序列化对象返回之前,函数将删除该属性,因此只能在__wakeup期间访问它。
但是,要明确这个解决方案:这是不得已而为之的措施。请尝试在第一次序列化表单中使缺失的信息可用。

非常有用的信息!谢谢。对我来说,两种解决方法(全局变量和玩弄序列化数据)都不是选项,但你提供了这个例子真是太棒了!确实,我应该将信息与对象一起存储。只是在一个我存储在数据库数据集中的对象中存储databaseId字段感觉非常奇怪。也许这只是我的半颠倒的完美主义不想配合。 - SquareCat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接