Page 1 of 1

Best Way to Send Email

Posted: Thu Sep 23, 2004 7:04 am
by Nick Lee
Hi,

I am currently writing some software to run a mailing list with PHP4 and MySQL.
I am looking at a number of methods of doing this and I would like some advice on the most efficient method. It is first worth noting that the PHP I'm using is in safe mode. Secondly it is worth noting I am sending to an array (taken from a MySQL db of about 1000 email addresses)
The problem is I can't keep testing these methods on 1000 people as it will annoy them if they keep getting test emails from the script... so I just have to try it whenever I have an email I actually have to send, which is not very often.

Initially I started using this function to send emails:

Code: Select all

function sendEmail($subject, $to, $message, $from) {
$headers = "From: $from \r\nReply-To: $from\r\nX-Mailer: Van Mildert JCR Mailing Lists";

if (is_array($to))  {
for($y = 0; $y < sizeof($to); $y++) {
               $sendto = $to[$y];
			$sendmail = mail($sendto, $subject, $message, $headers);

			if (!$sendmail) { $error =+ $to[$y] . "\n"; }
           							}
			if ($error) { print "An error occured. Emails could not be sent to the following people:";
						  print "<pre>" . $error . "</pre>";
						  exit;
						}

				   }
else {
mail ($to, $subject, $message, $headers)
	or die("Error: Mail not sent..");
	 }
}
This works fine but does take a while to send, understandably, because mail() is opening a socket, sending, closing, opening, sending, closing etc... not very efficient.

As a result I was advised to use a socketmail method, i.e. directly conversing with the SMTP server. I borrowed the code of the internet to test the principle, I don't claim this code as my own!

Code: Select all

function socket_mail($toArray, $subject, $message, $fromF) {

  // Setup
  $fromEmail = "van-mildert.jcr@durham.ac.uk";
  $fromMailer = "Socketmail v2.0";
  $smtp = "localhost";
  $smtp_port = 25;
  $charset = "ISO-8859-1";

  ini_set(sendmail_from, $fromEmail);

  $connect = @fsockopen ($smtp, $smtp_port, $errno, $errstr, 5);
    if (!$connect) return false; 
    $rcv = fgets($connect, 1024); 

  fputs($connect, "HELO {$_SERVER['SERVER_NAME']}\r\n");
    $rcv = fgets($connect, 1024); 

  foreach ($toArray as $to) {

    $toBits = explode(" ", $to);
    $toRcpt = trim($toBits[count($toBits) - 1], "<> ");

    fputs($connect, "RSET\r\n");
      $rcv = fgets($connect, 1024);

    fputs($connect, "MAIL FROM:$fromEmail\r\n");
      $rcv = fgets($connect, 1024);
    fputs($connect, "RCPT TO:$toRcpt\r\n");
      $rcv = fgets($connect, 1024);
    fputs($connect, "DATA\r\n");
      $rcv = fgets($connect, 1024);

    fputs($connect, "Subject: $subject\r\n");
    fputs($connect, "From: $fromF\r\n");
    fputs($connect, "To: $to\r\n");
    fputs($connect, "X-Sender: <$fromF>\r\n");
    fputs($connect, "Return-Path: <$fromF>\r\n");
    fputs($connect, "Errors-To: <$fromF>\r\n");
//    fputs($connect, "Message-Id: <".md5(uniqid(rand())).".".preg_replace("/[^a-z0-9]/i", "", $from)."@$smtp>\r\n");
    fputs($connect, "X-Priority: 3\r\n");
    fputs($connect, "Date: ".date("r")."\r\n");
    fputs($connect, "Content-Type: text/plain; charset=$charset\r\n");
    fputs($connect, "\r\n");
    fputs($connect, $message);

    fputs($connect, "\r\n.\r\n");
      $rcv = fgets($connect, 1024);
  }

  fputs ($connect, "QUIT\r\n");
    $rcv = fgets ($connect, 1024);
  fclose($connect);
  ini_restore(sendmail_from);
  return true;
}
This second instance functions correctly for the first load of emails but timesout after 30 seconds. I don't know then how fast it is, or how I might stop the script timing out.

A third alternative is to use sendmail via the popen command, thus:

Code: Select all

$mailer = popen ("/usr/sbin/sendmail -t -i","w");
fwrite ($mailer,"Subject: $subject
From: $sender
To: $toArray

$message
");
pclose ($mailer);
I've no idea what would happen if I tried this.

I would like to hear any suggestions about the best way to send the emails; the first instance just seems to take so long even if it works so I'd like a quicker alternative if at all possible. I've taken this as far as I can with my limited understanding of the PHP architecture. I'm hoping someone with a bit more knowledge might be able to help me out!

Cheers

Nick.

Posted: Thu Sep 23, 2004 9:30 am
by lostboy
script timeout => see here

try phpmailer..works very well

Posted: Thu Sep 23, 2004 11:48 am
by feyd
Please use

Code: Select all

tags when posting code. Read:  [url=http://forums.devnetwork.net/viewtopic.php?t=21171]Posting Code in the Forums[/url]

Posted: Thu Sep 23, 2004 12:32 pm
by Nick Lee
Right, sorry mate, didn't twig.

N.

Posted: Thu Sep 23, 2004 12:36 pm
by Nick Lee
Also, thanks lostboy, I will try phpmailer but can't use the script timeout function because PHP is in safe mode... ultimately, I need to be able to send a command to the shell and be able to leave it to do its business whilst the script finishes execution, displaying a finished page.
I thought popen would do that but it appears it still takes time; I am wondering whether I am using this particular function incorrectly, but I don't really know enough about how it works to see if I am.

N.

Posted: Thu Sep 23, 2004 12:54 pm
by lostboy
What about the ini_set command, can you use that to over ride the timeout for safe mode?

Another option is to use run the program in a loop and use some js to reload the page to send out x emails at a time.

Posted: Thu Sep 23, 2004 1:07 pm
by Nick Lee
the idea with ini_set is one I will look at. The javascript loop was going to be my fallback but that way it will STILL take ages, and I think anything over 20-30 secs is going to <span style='color:blue' title='I'm naughty, are you naughty?'>smurf</span> off the end user.

N.

Posted: Thu Sep 23, 2004 1:08 pm
by Weirdan
most you can do with php is create php script which sends one email at once and call it from main script via system or exec. Perhaps this way you will be able to circumvent max_execution_time, as long as execution time of the main script falls under limit.

PS: starting the whole script via process control functions won't help, as max_execution_time affects any instance of php script regardless of whether particular script was started in foreground or backround.

Posted: Thu Sep 23, 2004 1:15 pm
by Nick Lee
Weirdan wrote:PS: starting the whole script via process control functions won't help, as max_execution_time affects any instance of php script regardless of whether particular script was started in foreground or backround.
I think my priority remains the quickest possible execution rather than it falling under max execution time as this wasn't a problem with thefirst piece of code above.

In theory what is the most efficient way of doing this? I assumed example 2, a direct conversation with the SMTP server would be.

N.

Posted: Thu Sep 23, 2004 1:49 pm
by Weirdan
direct smtp connection would be the fastest method. Btw, you might want to check $rcv variable, chances there are timeouts on smtp server side.

Posted: Thu Sep 23, 2004 1:59 pm
by ldomingues
My experience tells me that the php mail() function is faster than talking with the SMTP server (if you send one message at a time).

I think that happens because mail() function just calls sendmail, which will create a file in the user's queue, instead of talking with the SMTP server.

Sending using SMTP (like phpmailer does) can be faster if you logon once, and send all your e-mails in batchs.

You could send one mail at a time, and redirect the user to the same page, which will send the next email in the queue (repeat it as long as there are emails to send).

Second option is output a message to the user (like "sending email"), flush() the buffer and end the connection and keep the php script working, sending the emails - in this case, you cannot show any error to the user.

Posted: Thu Sep 23, 2004 3:36 pm
by Nick Lee
ldomingues wrote:Second option is output a message to the user (like "sending email"), flush() the buffer and end the connection and keep the php script working, sending the emails - in this case, you cannot show any error to the user.
This may seem a little dim, but how do you end a connection with the user without ending the execution of the script? I am quite comfortable with the use of flush (or ob_get_flush and so on) but I can't see how to stop the browser waiting for the rest of the page without execution stopping at the same time. The display of errors is not a problem as they would not be reported to the user anyway but logged into an errors db thing.

N.

Posted: Thu Sep 23, 2004 4:38 pm
by ldomingues
If you output the complete <HTML> </HTML> the browser will render the page, although the connection might not be closed.

I read somewhere that you can modify the headers to abort the connection.