24

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?
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. Commented May 31, 2012 at 10:35

3 Answers 3

38

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...
Sign up to request clarification or add additional context in comments.

Comments

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).

1 Comment

That's the way to go, especially when using ORM (i.e. Doctrine2).
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;
}

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.