Search code examples
phpemailqueuephpmailer

How to build an email queue with PHPmailer?


I have built an emailing script with PHPmailer after inserting into a table, however, I'm getting a bad gateway 502 coz the script times out. and sending 300+ emails in response to a web request doesn't sound like a good idea to me. So my question is how can I build a queue that will send the emails in the background?

as far as I understand I will need new table lets say email_queue_table insert the email addresses, content and then have a field called status sent or queued create a while loop something like if($status == "queued"){ //then send the email here} else{ // nothing to be sent.}

If you know of a more efficient/better way of doing this I'm all ears. Thanks for any help.


Solution

  • The logic of how it would work - with a bit of code to help you along the way - is something like this:

    Add the following fields to your email_queue_table table:

    • email_address, Email address (to:)
    • content, Message content. If it's the same message that's to be sent to every user, you'd be better storing this once somewhere else. If it's just a few things that need changing in the content such as the users name then use PHP's str_replace() to do things like str_replace('%name%', 'Andy') where %name% is in your template and will be replaced as appropriate on each loop. Ideally you would not have this column or be repeating the same data here - if your message was 50 Kb and you had 3000 users, for example, you'd be storing 150 Mb of data in a table un-necessarily.
    • sent_status, Sent status (default = "not sent", or 0)
    • retry_attempts Retry attempts (default = 0)

    Create a PHP script which does the following:

    • SELECT email_address, content FROM email_queue_table LIMIT 0, 50. This gets you 50 people per loop. Repeat until the end of the list - to do this you will need to know the total number of records in the table which you can do with COUNT()
    • On each loop (each person you're sending mail to):
      • Use PHPMailer to attempt to send content to email_address
      • Read the return status from PHPMailer. If it's successfully sent flag sent_status as "sent" (1). If not, flag it as "not sent" (0).
    • Put in a delay, e.g. sleep(60) between each batch of 50. This pauses execution of the script for 1 min (60 seconds) and may help mitigate your server as being flagged for sending out large quantities of email in one attempt.

    When all the messages have been sent, you could optionally go back through the table to try and resend any that didn't send. Still keep the LIMIT logic because there may be a large number where it didn't send, e.g.

    SELECT email_address, content FROM email_queue_table WHERE sent_status = 0 AND retry_attempts < 5 LIMIT 0, 50
    

    Increment the retry_attempts field. Stop if it goes beyond, say, 5 attempts.

    You cannot execute the above script through a browser because it will time out.

    Instead, you can manually trigger it from a command line, e.g.

    php send_email.php
    

    Or set the above up on Cron, to run every night, or at whatever frequency are needed.

    Or you can trigger it from an ajax call and update the progress in the browser. See: creating background processes in php for long running process