2

该文档多次警告要确保在 Doctrine 2 实体上实现唤醒和克隆实现,例如:

http://docs.doctrine-project.org/en/latest/cookbook/implementing-wakeup-or-clone.html

它没有说明原因,但是......我认为这与不搞乱 Doctrine 的实体管理器和它可能维护的内存缓存有关,但是粗略地深入研究(巨大的)代码库并没有说得更多,也没有快速搜索用户列表。

任何了解 Doctrine 内部原理的人都知道为什么 Doctrine 2 需要对实体进行安全唤醒和克隆方法吗?

4

2 回答 2

8

Doctrine 2 需要将从数据库中获取的数据转换为实体(php 对象)。这个过程称为水合作用。

Doctrine 2 没有使用传统的使用new操作符的方法(从而构造一个类),而是使用了以下方法:

  • 它构建了一个字符串,该字符串表示您的类的序列化空版本。
  • 它反序列化该字符串以使其成为实际对象。这就是为什么您需要安全地实施__wakeup.
  • 它将该对象存储为原型。

这个过程对每个实体类执行一次。

然后,每当它需要对实体进行水合时,它就会克隆原型。这就是为什么您需要安全地实施__clone.

这都是newInstance()ClassMetadataInfo中的一个小方法完成的。

这样做的好处是当新实体被水合时不使用构造函数。这使开发人员可以自由地在构造函数中做任何他/她想做的事情(包括使用参数)。

更新

您需要安全地实现__wakeupand__clone方法的原因是:

因为 Doctrine 2 在需要水合实体时会反序列化和克隆实体,因此将调用这些方法。但是当 Doctrine 2 这样做时,实体将没有任何数据集(甚至没有标识符)。数据将在之后设置。

因此,当您以仅在实体确实具有标识符时执行您的逻辑的方式实现它们时,您确定您的逻辑仅在反序列化或克隆实体时执行,而不是在 Doctrine 2 执行时执行。

于 2013-11-20T13:58:18.223 回答
2

Jasper 的回答指出了确切的原因,但它没有给出背景——就像Doctrine 继续反序列化和克隆实体的原因一样。

这样做的理由是允许实体定义他们想要的任何构造函数,如这篇博文中所述:

http://www.doctrine-project.org/2010/03/21/doctrine-2-give-me-my-constructor-back.html

在 ConFoo 2010 的演讲中,有人询问了 Doctrine 2 中实体的构造函数以及是否可以使用它。我认为这是值得写的东西,因为在教义 1 中这是不可能的。构造函数被你劫持并被 Doctrine 内部使用。

在 Doctrine 2 中,可以在实体类中定义构造函数,并且不需要是零参数构造函数!没错,Doctrine 2 从不实例化你的实体的构造函数,所以你有完全的控制权!

这是可能的,因为其他两个项目php-object-freezer和 Flow3 使用了一个小技巧。它的要点是我们存储一个原型类实例,该实例是从手工制作的序列化字符串中反序列化的,其中类名连接到字符串中。当我们对字符串进行反序列化时,结果是该类的一个实例,它作为原型存储,并在每次水合期间需要新实例时克隆。

看看负责这个的方法:

<?php
public function newInstance()
{
    if ($this->_prototype === null) {
        $this->_prototype = unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->name), $this->name));
    }
    return clone $this->_prototype;
}
于 2014-03-22T19:22:39.197 回答