views:

1360

answers:

3

I don't totally understand how all this works, but I'm getting this error:

Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 261858 bytes) in /Users/andrew/Sites/myApp/library/Zend/Mail/Transport/Smtp.php on line 213

I'm running this code locally on my Mac running MAMP. Not sure if that has anything to do with it. This is my code, basically:

$config = array('ssl' => 'tls', 'port' => 587, 'auth' => 'login', 'username' => 'username', 'password' => 'password');
    $smtpConnection = new Zend_Mail_Transport_Smtp('smtp.gmail.com', $config);

 foreach ($subscribers as $subscriber) {
  $message = new Zend_Mail('utf-8');
  $message->setFrom('[email protected]', 'Mailing List')
    ->addTo($subscriber->email)
    ->setSubject($subject)
    ->setBodyText($body);
  $attachment = $message->createAttachment(file_get_contents($filepath));
  $attachment->type = 'application/pdf';
  $attachment->filename = $filename;
  $message->send($smtpConnection);
 }

However, the more subscribers there are, the higher this number ends up getting, and this fix will only help for so long:

ini_set("memory_limit","12M");

I need to figure out how to send an email with an attachment to a couple hundred people. Here's something else I've come up with but it seems a little hacky to only set the bcc and not the to address:

$message = new Zend_Mail('utf-8');
 $message->setFrom('[email protected]', 'Mailing list')
   ->setSubject($subject)
   ->setBodyText($body);
 $attachment = $message->createAttachment(file_get_contents($filepath));
 $attachment->type = 'application/pdf';
 $attachment->filename = $filename;

 foreach ($subscribers as $subscriber) {
  $message->addBcc($subscriber->email);
 }
 $message->send($smtpConnection);

However, even doing this, I need to specify the "memory_limit". Can you please point me in the right direction with this? Is there something I'm not doing?

+2  A: 

I'm guessing your pdf is about 250Kbytes? You're reading it into memory once per email you send out. Don't. Read it once. :) It might also be an encoding-thing in the Zend framework.

  • Call file_get_contents() once before your loop
  • Set the memory limit much higher as long as your server can handle it (I'd say along the lines of 32-128 Mbytes)
  • unset() your variables - should force php to GC it (in theory)
  • You could reuse the $message object (ugly hack, but could save bytes if Zend does some sort of file-encoding and it uses lots of memory)

I'd also make a cron-job for sending the emails, and making sure that each email (or a reference to it) is stored in the database along with a status. This way you won't send duplicate mails if you hit another memory limit, or bug.

Lots of great advice here! However, (without setting the memory limit), and only sending one email, I'm still getting the error. Does the error mean that the memory limit is currently set to 8Mb, and that it's going over by 250K? I'm running this locally (with MAMP), so I wonder...if I set this in my code to be 32Mb, will that be less than what my server is capable of? Will I be doing a disservice by setting this value, rather than finding a better solution?
Andrew
8M is too little. Actually, the default PHP memory_limit is 16M as of PHP5.2.0. Setting it to 32M should not be an issue at all unless you've got a very low-end server/computer. Personally I'm running 128M on both my workstation and my server (4G/2G memory respectively). As to what your current memory_limit is, ini_get('memory_limit'). As to your current usage, memory_get_peak_usage() and memory_get_usage(). As for doing a disservice? Imho, no. Usage is still within acceptable parameters.
+1  A: 

There's no need to create a new attachment with each message. Just create it once and then attach it each time you send.

$config = array('ssl' => 'tls', 'port' => 587, 'auth' => 'login', 'username' => 'username', 'password' => 'password');
$smtpConnection = new Zend_Mail_Transport_Smtp('smtp.gmail.com', $config);

$attachment = new Zend_Mime_Part(file_get_contents($filepath));
$attachment->type = 'application/pdf';
$attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT;
$attachment->filename = $filename;

foreach ($subscribers as $subscriber) {
    $message = new Zend_Mail('utf-8');
    $message->setFrom('[email protected]', 'Mailing List')
      ->addTo($subscriber->email)
      ->setSubject($subject)
      ->setBodyText($body);
    $message->addAttachment($attachment);
    $message->send($smtpConnection);
}
inxilpro
A: 

I have had the similar problem with memory limit and sending by one SMTP connection for a lot of messages. *Zend_Mail_Protocol_Abstract* holds its internal log in memory. There are logged all mail requests in the log. The log is growing up with each message sent. You has to call $protocol->resetLog() sometimes. (It depends on your data amount for each message. You can check your memory use by memory_get_usage() PHP function.) Try something like this:

  $transport = new Zend_Mail_Transport_Smtp();
  $protocol = new Zend_Mail_Protocol_Smtp('localhost');
  $protocol->connect();
  $protocol->helo('localhost');
  $transport->setConnection($protocol);
  foreach(){
    $mail = new Zend_Mail('utf-8');
    ...
    $protocol->rset();
    $mail->send($transport);
    $protocol->resetLog();  // you don't need to resetLog for each message
  }

This also might be helpful: http://framework.zend.com/manual/en/zend.mail.multiple-emails.html