PHP deep clone object
Asked Answered
E

3

23

The scenario: fetch an email template from the database, and loop through a list of recipients, personalising the email for each.

My email template is returned as a nested object. It might look a little like this:

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

Then I loop through the recipients, passing this $email object to a personalise() function:

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

The issue, of course, is that I need to pass the $email object by reference in order for it to replace the personalisation tags - but if I do that, the original object is changed and no longer contains the personalisation tags.

As I understand, clone won't help me here, because it'll only create a shallow copy: the content object inside the email object won't be cloned.

I've read about getting round this with unserialize(serialize($obj)) - but everything I've read says this is a big performance hit.

So, two finally get to my two questions:

  1. Is unserialize(serialize($obj)) a reasonable solution here?
  2. Or am I going about this whole thing wrong? Is there a different way that I can generate personalised copies of that email object?
Euhemerus answered 31/5, 2012 at 10:28 Comment(1)
I find swiftmailer does a great job of doing what you are trying to do with the decorator plugin (swiftmailer.org/docs/plugins.html#decorator-plugin), plus it does a good job with the headers; we are getting very low spam results with it.Ruyter
E
34

You could add a __clone() method to your email class. Which is automatically called when an instance of this class is cloned via clone(). In this method you can then manually add the template.

Example:

class Email {
    function __clone() {
        $this->template = new Template();
    }
}

.

unserialize(serialize($object)); // would be another solution...
Eloisaeloise answered 31/5, 2012 at 10:33 Comment(0)
C
17

Another more generic and powerful solution: MyCLabs\DeepCopy.

It helps creating deep copy without having to overload __clone (which can be a lot of work if you have a lot of different objects).

Christiansen answered 4/6, 2014 at 9:53 Comment(1)
That's the way to go, especially when using ORM (i.e. Doctrine2).Scriabin
P
0

Recursive cloning can be done this way:

public function __clone(): void {
  foreach(get_object_vars($this) as $name => $value)
    if(is_object($value)) $this->{$name} = clone $value;
}
Piccalilli answered 26/8, 2022 at 5:46 Comment(1)
We would need to override the clone function not only for the given class but also for its dependencies. Otherwise, we will clone deeply main class, but dependencies will be cloned shallowly.Wheelwright

© 2022 - 2024 — McMap. All rights reserved.