3

我将某些对象存储在数据库中。数据库中的每个节点都携带一个序列化对象。节点可以有孩子,因此对象也可以有孩子。因此,我想$children用包含其直接子级的数组填充对象中的属性。当对象被反序列化时$children应该被填充。

为了避免从外部这样做,我想我宁愿让对象本身来做。但是,该对象不知道它属于哪个数据库条目。因此,当__wakeup()被调用时,对象缺少加载其自己的相应数据库条目所需的信息。

看起来最好的方法是能够将变量传递给__wakeup(),尽管我知道它unserialize()没有提供这样的选项。

我无法找到有关此的任何信息。我正要得出结论,除了使用全局之外,这是不可能的,这不是我选择采取的路线。

是否可以?如果是,如何?

4

1 回答 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]
);

运行该代码将打印(演示):

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
)

这是有效的,因为该函数tmp向序列化表单添加了一个属性,该表单保存了作为传递的序列化数组$args。因此,当对象被唤醒时,它将可以访问这些值。该函数将在返回未序列化的对象之前删除该属性,因此您只能在__wakeup.

但要明确这个解决方案:这是绝望的措施。首先尝试以序列化形式提供缺失的信息。

于 2014-03-23T09:57:03.900 回答