Code snippets for symfony 1.x

Navigation

Sending batch emails

Sending a large volume of emails using symfony's built-in email support can be problematic because of memory overhead involved in calling the sendEmail action for every email to be sent. In order to overcome this, we will have to use another way. In essence, instead of making multiple symfony mail calls, we will grab the presentation for the email action, then use sfMail directly to send our emails.

In an action

// Get the presentation only for the actual email.
// email/newsletter is the module/action pair that will
// generate the template for the actual email in this example.
$raw_email = $this->getPresentationFor('email', 'newsletter');
 
require_once sfConfig::get('sf_symfony_lib_dir') . '/addon/sfMail/sfMail.class.php';
$mail = new sfMail();
$mail->setMailer('sendmail');
$mail->setCharset('utf-8');
$mail->setContentType('text/html');
 
$mail->setFrom($sender_email, $sender_name);
$mail->setSubject($newsletter_title);
$mail->setBody($raw_email);
 
// To further ensure that we don't choke up on the
// emails, we introduce a throttle. In this example,
// we use 100, which we will use to sleep for a period
// of time for every 100 emails sent.
 
$throttle = 100;
$i = 0;
 
// Now, run a loop through all the email addresses in the mailing list.
 
foreach($email_addrs as $email_addr)
{
    if ($i % $throttle == 0)
    {
        sleep(10); // Here, we sleep for 10 seconds for every $throttle emails sent.
    }
 
    $mail->addAddress($email_addr);
    $mail->prepare();
    $mail->send();
    $mail->clearAllRecipients();
    $i++;
}

That's it! Now, just go ahead and set up the email/newsletter pair which will set up the template for the actual email.

Update: In order to beat the maximum execution time limit in PHP, I also use

set_time_limit(0);

to remove the limit at the start of my actual script. In addition, it may also be a good idea to run the action as a batch script in the background so that the mass mailing can occur in the background.

by Ron Lim on 2006-06-26, tagged batch  email 

Comments on this snippet

gravatar icon
#1 Matt Robinson on 2006-06-26 at 04:48

For what it's worth, unless I've gone crazy, the 10 second delay means that at best you can only send 300 emails through this method with the default script execution maximum time set by PHP. For true mass-mailing, it'd be better to queue up the email addresses in a persistent manner (batching them into temporary files or whatever), and then use a redirect (not a forward) to keep running the send-mail action until it runs out of addresses (and then redirects to a report page, or whatever). That way you don't run into the php.ini max_execution_time limit, and you can even rig up a sort of progress bar.

gravatar icon
#2 Ron Lim on 2006-06-26 at 06:43

In real usage, I actually have the action running as a background batch script, and I also use set_time_limit(0) to remove the script execution time limit. By pushing the action to the background, I could free up the screen so the user can continue with other tasks.

gravatar icon
#3 Benoit Masson on 2006-10-21 at 02:46

I would really consider using swift: http://www.symfony-project.com/trac/wiki/sfSwiftPlugin

and use the bacth example here : http://www.swiftmailer.org/docs/faq/mass-mailing

You can then add a simple plugin to monitor sending of email ( a dot for each mail or a number etc..)

The thing that might be interresting is adding a plugin to recover from a fail batch, knowing how many mail were sent and restarting from there... but swift seems to be fast(reaaly better than phpmailer) and safe so ...

Benoit