PHP深度克隆对象

22

场景:从数据库中获取电子邮件模板,并循环遍历收件人列表,为每个收件人定制电子邮件。

我的电子邮件模板被返回为一个嵌套对象。它可能看起来像这样:

object(stdClass) {
    ["title"] => "Event Notification"
    ["sender"] => "notifications@mysite.com"
    ["content"] => object(stdClass) {
        ["salutation"] => "Dear %%firstname%%,"
        ["body"] => "Lorem ipsum %%recipient_email%% etc etc..."
    }
}

然后我循环遍历收件人,将这个$email对象传递给personalise()函数:

foreach( $recipients as $recipient ){
    $email_body = personalise( $email, $recipient );
    //send_email();
}

问题在于我需要通过引用传递$email对象以替换个性化标签,但如果这样做,原始对象将被更改并且不再包含个性化标签。

据我所知,克隆(clone)在这里无法帮助我,因为它只会创建一个浅层副本:邮件对象中的内容对象不会被克隆。

我已经阅读了有关使用unserialize(serialize($obj))解决此问题的方法 - 但我读到的所有内容都说这会对性能造成很大的影响。

所以,最终有两个问题:

  1. 在这里使用unserialize(serialize($obj))是合理的解决方案吗?
  2. 还是我整个想法有误?是否有其他方法可以生成该电子邮件对象的个性化副本?

我发现SwiftMailer在做装饰器插件(http://swiftmailer.org/docs/plugins.html#decorator-plugin)方面做得非常好,而且它在处理邮件头方面也做得很好;我们使用它时获得了非常低的垃圾邮件结果。 - scott
4个回答

33
你可以为你的电子邮件类添加一个__clone()方法。当通过clone()克隆此类的实例时,该方法将自动调用。在这个方法中,你可以手动添加模板。
示例:
class Email {
    function __clone() {
        $this->template = new Template();
    }
}

.

unserialize(serialize($object)); // would be another solution...

17

另一个更通用和强大的解决方案是:MyCLabs\DeepCopy

它可以帮助创建深层拷贝,而不需要重载__clone(如果您有许多不同的对象,则可能需要做很多工作)。


3
这是正确的做法,特别是在使用ORM(即Doctrine2)时。 - Artur Bodera

0

可以通过递归克隆来完成:

public function __clone(): void {
  foreach(get_object_vars($this) as $name => $value)
    if(is_object($value)) $this->{$name} = clone $value;
}

我们不仅需要为给定的类覆盖克隆函数,还需要为其依赖项进行覆盖。否则,我们将深度克隆主类,但依赖项将被浅层克隆。 - Jsowa
我们不仅需要为给定的类覆盖克隆函数,还需要为其依赖项进行覆盖。否则,我们将深度克隆主类,但依赖项将被浅克隆。 - undefined

-1
更新:我在下面提到的clone关键字是对象的浅拷贝。对于某些用例来说,它可能是一个更简单的解决方案,但对于其他情况,使用上面提到的__clone()unserialize(serialize($object));方法进行深拷贝可能是最理想的。

...

不确定为什么这里没有提到,但是在对象之前使用关键字clone会按照其字面意思执行。 以下是一个示例:
// Prepare shared stream data
$args = (object) [
    'key' => $key,
    'data_type' => $data_type,
    'data' => clone $data_obj,
    'last_update' => $last_update
];

setSharedStreamData($args);

// Object below will not change when data property
// is updated in setSharedStreamData function
print_r($data_obj);

PHP的克隆(clone)创建的是浅拷贝,引用值被复制,但仍然“指向”同一个元素。而深拷贝(deepcopy)会一直创建新的元素。 - pietervanderstar
嗨 @pietervanderstar,谢谢你指出来。你是对的!虽然它对我的用例有效,但确实是浅拷贝。我会更新我的答案。 - Owen Far

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