使用 PHP 序列化维护对象关系
我遇到了一个关于实现 Serialized 接口的对象的非常棘手的问题。让我们举个例子:
class A {
public $b;
}
class B {
public $a;
}
$a = new A;
$b = new B;
$a->b = $b;
$b->a = $a;
echo serialize($a); // O:1:"A":1:{s:1:"b";O:1:"B":1:{s:1:"a";r:1;}}
echo serialize($b); // O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"b";r:1;}}
$a = unserialize(serialize($a));
var_export($a === $a->b->a); // true
在这个例子中我们可以看到,当使用内置的PHP序列化功能时(无论是否使用__sleep()
函数),A之间的交叉引用
& B
被保留 (r:1;
)。
但是,如果我们想强制使用 Serialized 接口,就会出现问题:
class A implements Serializable {
public $b;
public function serialize() { return serialize($this->b); }
public function unserialize($s) { $this->b = unserialize($s); }
}
class B implements Serializable {
public $a;
public function serialize() { return serialize($this->a); }
public function unserialize($s) { $this->a = unserialize($s); }
}
$a = new A;
$b = new B;
$a->b = $b;
$b->a = $a;
echo serialize($a); // infinite loop crashes PHP
因为每个对象序列化都是独立管理的,所以没有全局方法可以查看对象是否已经序列化,从而创建对其的引用。然后在无限循环中调用 serialize()
函数。
对于这个问题有好的解决方法吗?用于 serialize()
函数的模式?
I came across a quite tough problem to solve regarding objects implementing the Serializable
interface. Let's take an example:
class A {
public $b;
}
class B {
public $a;
}
$a = new A;
$b = new B;
$a->b = $b;
$b->a = $a;
echo serialize($a); // O:1:"A":1:{s:1:"b";O:1:"B":1:{s:1:"a";r:1;}}
echo serialize($b); // O:1:"B":1:{s:1:"a";O:1:"A":1:{s:1:"b";r:1;}}
$a = unserialize(serialize($a));
var_export($a === $a->b->a); // true
We can see in this example, that when using the built-in PHP serialization feature (whether or not we use the __sleep()
function), the cross references between A
& B
are preserved (r:1;
).
However, if we want to enforce the use of the Serializable interface, the problem arises:
class A implements Serializable {
public $b;
public function serialize() { return serialize($this->b); }
public function unserialize($s) { $this->b = unserialize($s); }
}
class B implements Serializable {
public $a;
public function serialize() { return serialize($this->a); }
public function unserialize($s) { $this->a = unserialize($s); }
}
$a = new A;
$b = new B;
$a->b = $b;
$b->a = $a;
echo serialize($a); // infinite loop crashes PHP
Because each object serialization is independently managed, there is no global way to see whether an object has already been serialized, to create a reference to it. The serialize()
functions are then called in an infinite loop.
Is there a good workaround for this problem? A pattern to use for the serialize()
functions?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是一个技巧:
Here is a trick:
Doctrine 项目在其关于 序列化实体:
所以,据我所知,目前这个问题还没有通用的解决方案。
This problem has been named cyclic object references by the Doctrine Project in their documentation on Serializing entities:
So, as far as I know, there is currently no generic solution to this problem.
标准 PHP 序列化提供两种引用,它们仅以顺序方式针对单个序列化调用起作用:递归和递归引用。
当您实现
Serialized
接口时,只要您提供一个存储,就可以将其扩展到多个对象和序列化步骤。例如,当内存中的每个对象都有一个标识符时,中央序列化服务可以在序列化时填充这些标识符,并在反序列化时以不同的顺序解包。该服务可以负责决定从存储中获取具有多个引用的对象 - 或者如果已经反序列化 - 运行时对象。由于服务是中心的,因此它可以轻松决定,并且
Serialized
方法可以只使用该服务。这也允许循环引用。但是,由于映射的原因,您需要自己实现这一点。
从字面上看,学说项目实际上已经有了这个。它的对象序列化形式是数据库本身。只是为了打开一点视野,这肯定不是您想要的。
Standard PHP serialization offers two kind of references which work only per the single serialization call in a sequential manner: recursion and recursion ref.
When you implement the
Serializable
interface, you can potentially stretch this across multiple objects and serialization steps as long as you offer one store. When each object in memory for example has an identifier, a central serilaization service can be stuffed with these on serialization and unpacked in even different order on unserialization.The service can take care of deciding to fetch an object with multiple references to it from the store - or if already unserialized - the runtime object. As the service is central, it can easily decide and the
Serializable
methods can just consume this service.This does also allow cyclic references. However, you need to implement this on your own because of the mapping.
Taken by the word, the doctrine project actually has this already. Its form of object serialization is the database itself. Just to open the view a little, sure this is not what you look for.