What is an efficient way to send email to many users in PHP - php

I have a database of over 12,000 users and I am trying to send an email to all of them, each with a specific information based on their information on the database. I have made this email to send when a cron runs on Sundays by 6am and I completed the feature last friday and it ran on sunday, i.e, Yesterday. Here is what happened.
1.) The email kept sending all day from 6am to 7pm
2.) By that time, it had sent only to 750 users
3.) After that it stopped completely for reasons I don't know
PS:
I am sending the emails using PHPMailer, with a template and I use a loop to loop over all users and perform calculations for each user, fill in the template with the information then send the email.
Below is a code snippet showing what I do...
foreach($users as $user){
// Construct the email template
$htmlContent = file_get_contents(__DIR__ . '/../../templates/weekly_spending_template.html');
// Replace some place holders with user's custom information.
$htmlContent = preg_replace('/\$bars/', $bars, $htmlContent);
$htmlContent = preg_replace('/\$labels/', $labels, $htmlContent);
$htmlContent = preg_replace('/\$total/', $currency . ' ' . number_format($total, 0), $htmlContent);
$htmlContent = preg_replace('/\$budget/', $currency . ' ' . number_format($budget, 0), $htmlContent);
$htmlContent = preg_replace('/\$first_name/', ucfirst($user->first_name), $htmlContent);
$htmlContent = preg_replace('/\$remark/', $remark, $htmlContent);
$htmlContent = preg_replace('/\$percentage_difference/', $percentage_difference, $htmlContent);
$htmlContent = preg_replace('/\$others/', $others, $htmlContent);
try {
// Setup email parameters
$mail = new PHPMailer(true);
$subject = "Your weekly spending breakdown";
$mail->IsSMTP();
$mail->SMTPDebug = 0;
$mail->SMTPAuth = true;
$mail->SMTPSecure = "ssl";
$mail->Host = "smtp.gmail.com";
$mail->Port = 465;
$mail->AddAddress($user->email, ucfirst($user->first_name) . ' ' . ucfirst($user->last_name));
$mail->Username = "mymail#example.com";
$mail->Password = "myPassW0rd";
$mail->SetFrom('mymail#example.com', 'Name');
$mail->AddReplyTo("mymail#example.com", "Name");
$mail->Subject = $subject;
$mail->Body = $htmlContent;
$mail->isHTML(true);
if (!$mail->send()) {
echo "Message was not sent.\n";
echo 'Mailer error: ' . $mail->ErrorInfo . "\n";
} else {
echo "Message has been sent.\n";
}
} catch (\Exception $ex) {
echo $ex->getMessage();
}
}
Please, can anyone give me suggestions on how to make this process more efficient, faster or better options for achieving this goal? Thanks.

You might consider using swiftmailer (below link), as it has mostly everthing you want and is used in many products and frameworks, so you can be sure it's fairly stable.
https://swiftmailer.symfony.com/docs/sending.html#sending-emails-in-batch
And you can only send 500 mails/per day #20 mails/per hour
See: https://support.google.com/a/answer/2956491#sendinglimitsforrelay

Just separate them by comma, like
$email_to = "youremailaddress#yourdomain.com, emailtwo#yourdomain.com, John Doe <emailthree#example.com>"
For more details check this link:- PHP send mail to multiple email addresses

Related

PHPMailer not sending to all emails in mailing list

I'm using the example mailing list and trying to tailor it to work with my DB. The problem I'm having is it's only sending to one email from the list of users in the Table (the example table only has 5 users). The email is being sent to the last user listed in the table
//Passing `true` enables PHPMailer exceptions
//$mail = new PHPMailer(true);
$mail = new PHPMailer();
$body = file_get_contents('contents.html');
$mail->SMTPDebug = SMTP::DEBUG_SERVER;
$mail->isSMTP();
$mail->Host = '####';
$mail->SMTPAuth = true;
$mail->SMTPKeepAlive = true; //SMTP connection will not close after each email sent, reduces SMTP overhead
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
$mail->Username = '###';
$mail->Password = '###';
$mail->setFrom('###', 'List manager');
$mail->addReplyTo('###', 'List manager');
$mail->Subject = 'PHPMailer Simple database mailing list test';
//Same body for all messages, so set this before the sending loop
//If you generate a different body for each recipient (e.g. you're using a templating system),
//set it inside the loop
$mail->msgHTML($body);
//msgHTML also sets AltBody, but if you want a custom one, set it afterwards
$mail->AltBody = 'To view the message, please use an HTML compatible email viewer!';
//Connect to the database and select the recipients from your mailing list that have not yet been sent to
//You'll need to alter this to match your database
$connection = mysqli_connect($server, $loginsql, $passsql, "database_name")
or die("Could not connect to database");
$result = mysqli_query($connection, 'SELECT user_login, user_email, db_prefix FROM userstest');
foreach ($result as $row) {
try {
$mail->addAddress($row['user_email'], $row['user_login']);
} catch (Exception $e) {
echo 'Invalid address skipped: ' . htmlspecialchars($row['user_email']) . '<br>';
continue;
}
try {
$mail->send();
echo 'Message sent to :' . htmlspecialchars($row['user_email']) . ' (' .
htmlspecialchars($row['user_email']) . ')<br>';
} catch (Exception $e) {
echo 'Mailer Error (' . htmlspecialchars($row['user_email']) . ') ' . $mail->ErrorInfo . '<br>';
//Reset the connection to abort sending this message
//The loop will continue trying to send to the rest of the list
$mail->getSMTPInstance()->reset();
}
//Clear all addresses and attachments for the next iteration
$mail->clearAddresses();
$mail->clearAttachments();
}
All - thanks for the help. I actually figured it out. The email address for setFrom was not the same as the account that was sending it out so it wouldn't work sending out to external domains.

PHPmailer is giving me a 504 gateway timeout error

i am getting a 504 gateway timeout error from my server when using phpmyadmin to send out indivudual emails to a list of about 1200 users
i need the emails to go out to the users one by one as i do not want expose any of the email addresses in the list to any other users
it seems to work well when i am sending out to a small list, but when i want to send to a large list i am getting this error
what would be the best way to send out one email, reset the request and send out the next email in the list as it goes through the loop?
Also i'd like to see message sent to [name] each time the email goes out
heres my php
require('connection.inc.php');
include ("PHPMailer/class.phpmailer.php");
include ("PHPMailer/class.smtp.php");
require 'PHPMailer/PHPMailerAutoload.php';
$mail = new PHPMailer();
if (isset($_POST['submit'])) {
error_reporting(E_STRICT | E_ALL);
date_default_timezone_set('Etc/UTC');
$message = $_POST['message'];
$UeMail = uniqid();
$UDomain = uniqid();
$domain = '#zzz.org';
$mail = new PHPMailer;
$mail->isSMTP();
$mail->SMTPDebug = 0;
$mail->Host = 'zzz.zzz.com';
$mail->SMTPSecure = 'ssl';
$mail->Port = 465;
$mail->SMTPAuth = true;
$mail->SMTPKeepAlive = true; // SMTP connection will not close after each email sent, reduces SMTP overhead
$mail->Username = 'zzz#zzz.com';
$mail->Password = 'zzz';
$mail->setFrom('zzz' . $UeMail . $domain);
$mail->addReplyTo('zzz#zzz.com');
$mail->WordWrap = 9999; // set word wrap
// I ADDED THIS TO BYPASS SSL ERRORS
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
// SET ENVOIRMENT #1 (DONT FORGET TO SET #2)
// LIVE ENVIORMENT EMAIL TO SMS
$result = mysqli_query($con, "SELECT name, mob_email FROM `user` WHERE mob_email<>''");
// TEST ENVIORMENT
// $result = mysqli_query($con, "SELECT email, name FROM user_test WHERE email<>''");
foreach($result as $row) {
set_time_limit(60);
$mail->Body = $message; //HTML Body
$mail->AltBody = $message; //Text Body
// SET ENVOIRMENT #2
// LIVE ENVIORMENT EMAIL TO SMS
$mail->addAddress($row['mob_email'], $row['name']);
// TEST ENVIORMENT
// $mail->addAddress($row['email'], $row['name']);
if (!$mail->send()) {
echo "Mailer Error (" . str_replace("#", "#", $row["email"]) . ') ' . $mail->ErrorInfo . '<br />';
break; //Abandon sending
}
else {
echo "Message sent to :" . $row['name'] . ' (' . str_replace("#", "#", $row['email']) . ')<br />';
// Mark it as sent in the DB (NOT USING THIS RIGHT NOW)
/*
mysqli_query(
$mysql,
"UPDATE mailinglist SET sent = TRUE WHERE email = '" .
mysqli_real_escape_string($mysql, $row['email']) . "'"
);
*/
}
// Clear all addresses and attachments for next loop
$mail->clearAddresses();
$mail->clearAttachments();
}
}
and heres my html
<!DOCTYPE html>
<html>
<head>
<title>Form submission</title>
</head>
<body>
<form action="" method="post">
Message:<br><textarea rows="5" name="message" cols="30" maxlength="110"></textarea><br>
<input type="submit" name="submit" value="Submit">
</form>
</body>
</html>
all code is in a single file (sendEmail.php)
I can see that you've based your code on the PHPMailer mailing list example (though it looks like you may have started with an old version, so check you're using the latest), and it looks basically correct.
However, sending that many messages on page load is never going to work reliably. You need to run this from cron so that it is not subject to such timeouts. There's also no point in the call to set_time_limit(60); that will not extend the overall timeout you're hitting.
Also never disable SSL certificate verification unless you exactly why you are doing so - fix it properly. The PHPMailer troubleshooting guide gives lots of info about how to check what's wrong.

php mail script not working using phpMailer

I am using phpMailer to send emails using PHP.
The mail is being sent but it is not received in inbox/spam or anything.
It is surprising that it was working until few days ago.
I have tested it and almost 500-600 emails were sent and received.
But suddenly it stopped "working".
Here's my Php script:
public static function mailTo($recipients)
{
$f3 = \Base::instance();
$edit = $f3->get('editTrue');
$user = AclHelper::getCurrentUser();
$template= new \Template;
if(isset($edit))
{
$mailBody = $template->render('leave/requestEdit.html');
}
else
{
$mailBody= $template->render('leave/emailTemp.html');
}
// When true, PHPMailer returns exceptions
$mail = new PHPMailer(true);
try {
$mail->isSMTP(); // Set mailer to use SMTP
$mail->isHTML(true);
$mail->addAddress($user['email']);
$mail->addAddress("malakar.rakesh1993#gmail.com");
// foreach($recipients as $recipient){
// $mail->addCC($recipient);
// }
$mail->SMTPAuth = true; // authentication enabled
$mail->SMTPSecure = 'ssl'; // secure transfer enabled REQUIRED for Gmail
$mail->Host = "smtp.gmail.com";
$mail->Port = 465; // or 587
$mail->Username = "malakar.rakesh1993#gmail.com";
$mail->Password = "abcd";
// $mail->Host = $f3->get('GBD.smtp.host'); // Specify main and backup SMTP servers
$mail->setFrom($user['email']);
$userFullName = trim(ucfirst($user['firstname'])) . " " . trim(ucfirst($user['lastname']));
$mail->FromName = $userFullName;
$mail->Body = $f3->get('message');
$mail->Body .="<br>". $mailBody;
if(isset($edit))
{
$mail->AltBody = '';
}
else
{
$mail->AltBody = 'Hello Team,<br>I would like to request leave for the leave dates specified as follows.
Application Date:' . $f3->get('issuedDate') . '<br>Leave requested from:' . $f3->get('leaveFrom') . '<br>Leave requested to:' . $f3->get('leaveTo') . '<br>Leave Description:' . $f3->get('leaveDescription') . 'Leave Type:' . $f3->get('leaveType').'<br><br>Hoping for a positive response.<br><br> Thank you.';
}
$mail->Subject = 'Updates on leave date applied';
$mailStatus = (boolean)$mail->send();
if ($mailStatus === true) {
return $mail;
}
} catch (phpmailerException $e) {
$response = array(
'status'=>'error',
'message'=>'Got some error while sending emails',
'exceptions'=>$e->getMessage()
);
return $response;
} catch (Exception $e) {
$response = array(
'status'=>'error',
'message'=>'Got some error while sending emails',
'exceptions'=>$e->getMessage()
);
return $response;
}
}
I received a junk email though [only one] that says :
This sender failed our fraud detection checks and may not be who they appear to be. Learn about spoofing
I cannot figure out what's going wrong.
It is working until previously. And I have tons of emails in my inbox.
Could it be that there's some limit of sending emails?? Or could it be that some one reported it as spam or spoofing??
Any help is very much appreciated. Thanks.
Because you're using SMTPSecure = 'ssl' you won't get any debug output with SMTPDebug = 2 because that only shows SMTP-level output; You need SMTPDebug = 3 to show connection-level problems. This is probably caused by out of date CA certificates in your PHP config. There have been lots of reports of this because gmail changed theirs recently (why your script stopped working). It's covered in the troubleshooting guide.
Also, why are you putting HTML tags in your plan-text AltBody? They won't work in there.

Sending multiple emails with PHPmailer

Edit: I forgot I'd created the SendMail(); function myself, which is why the explanation doesn't mention at first what it does.
I'm having some trouble with PHPMailer (https://github.com/PHPMailer/PHPMailer) when attempting to send two emails, one directly after the other.
The script is almost completely 'out of the box', with only a few modifications such as a foreach loop to allow for multiple addresses, and everything still works perfectly.
However, if I attempt to call more than one instance of SendMail(); I get the error message:
Fatal error: Cannot override final method Exception::__clone() in .... online 0
Previously I was using the in-built mail(); function, which allowed me to use it as many times as I liked in quick succession , but it doesn't appear to be that simple with PHPmailer:
$to = me#me.com;
$to2 = me2#me2.com';
$headers = 'php headers etc';
$subject = 'generic subject';
$message = 'generic message';
mail($to, $subject, $message, $headers);
mail($to2, $subject, $message, $headers);
The above would result in two identical emails being sent to different people, however I can't easily replicate this functionality with PHPmailer.
Is there a way of stacking these requests so that I can send successive emails without it failing? Forcing the script to wait until the first email has been sent would also be acceptable, although not preferential.
As I mentioned I know it works when only one instance is called, but I don't seem to be able to re-use the function.
I haven't included the source code, although it is all available on the link provided above.
Thanks in advance
Edit as requested
// First Email
$to = array(
'test#test.com',
'test2#test.com',);
$subject = "Subject";
$message = $message_start.$message_ONE.$message_end;
sendMail();
// Second Email
$to = array(
'test#test.com',
'test2#test.com',);
$subject = "Subject";
$message = $message_start.$message_TWO.$message_end;
sendMail();
The above is how I want this to work, as it would work with mail();. The first email will work fine, the second will not.
SendMail() code
This is from the PHPmailer website, and is what is defined as SendMail();. The only difference from the example is the loop for AddAddress, and the inclusion of $to as a global variable.
$mail = new PHPMailer();
$mail->IsSMTP(); // set mailer to use SMTP
$mail->Host = "smtp1.example.com;smtp2.example.com"; // specify main and backup server
$mail->SMTPAuth = true; // turn on SMTP authentication
$mail->Username = "jswan"; // SMTP username
$mail->Password = "secret"; // SMTP password
$mail->From = "from#example.com";
$mail->FromName = "Mailer";
foreach($to as $to_add){
$mail->AddAddress($to_add); // name is optional
}
$mail->AddReplyTo("info#example.com", "Information");
$mail->WordWrap = 50; // set word wrap to 50 characters
$mail->AddAttachment("/var/tmp/file.tar.gz"); // add attachments
$mail->AddAttachment("/tmp/image.jpg", "new.jpg"); // optional name
$mail->IsHTML(true); // set email format to HTML
$mail->Subject = "Here is the subject";
$mail->Body = "This is the HTML message body <b>in bold!</b>";
$mail->AltBody = "This is the body in plain text for non-HTML mail clients";
if(!$mail->Send())
{
echo "Message could not be sent. <p>";
echo "Mailer Error: " . $mail->ErrorInfo;
exit;
}
echo "Message has been sent";
You haven't posted this code that lets me make this a complete conclusion, but from the Exception and the way you've defined an overriding class inside a function, you probably have class.phpmailer.php loading every time like this:
require('class.phpmailer.php');
or
include('class.phpmailer.php');
You should change that line to
require_once('class.phpmailer.php');
The reason you need to change it to require_once is so that PHP will not load the class file the second time when you try to create the new/second PHPMailer class. Otherwise, the line class PHPMailer throws the __clone() exception.
Added an example below:
<?php
/**
* This example shows how to send a message to a whole list of recipients efficiently.
*/
//Import the PHPMailer class into the global namespace
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
error_reporting(E_STRICT | E_ALL);
date_default_timezone_set('Etc/UTC');
require '../vendor/autoload.php';
//Passing `true` enables PHPMailer exceptions
$mail = new PHPMailer(true);
$body = file_get_contents('contents.html');
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->SMTPKeepAlive = true; // SMTP connection will not close after each email sent, reduces SMTP overhead
$mail->Port = 25;
$mail->Username = 'yourname#example.com';
$mail->Password = 'yourpassword';
$mail->setFrom('list#example.com', 'List manager');
$mail->addReplyTo('list#example.com', 'List manager');
$mail->Subject = 'PHPMailer Simple database mailing list test';
//Same body for all messages, so set this before the sending loop
//If you generate a different body for each recipient (e.g. you're using a templating system),
//set it inside the loop
$mail->msgHTML($body);
//msgHTML also sets AltBody, but if you want a custom one, set it afterwards
$mail->AltBody = 'To view the message, please use an HTML compatible email viewer!';
//Connect to the database and select the recipients from your mailing list that have not yet been sent to
//You'll need to alter this to match your database
$mysql = mysqli_connect('localhost', 'username', 'password');
mysqli_select_db($mysql, 'mydb');
$result = mysqli_query($mysql, 'SELECT full_name, email, photo FROM mailinglist WHERE sent = FALSE');
foreach ($result as $row) {
try {
$mail->addAddress($row['email'], $row['full_name']);
} catch (Exception $e) {
echo 'Invalid address skipped: ' . htmlspecialchars($row['email']) . '<br>';
continue;
}
if (!empty($row['photo'])) {
//Assumes the image data is stored in the DB
$mail->addStringAttachment($row['photo'], 'YourPhoto.jpg');
}
try {
$mail->send();
echo 'Message sent to :' . htmlspecialchars($row['full_name']) . ' (' . htmlspecialchars($row['email']) . ')<br>';
//Mark it as sent in the DB
mysqli_query(
$mysql,
"UPDATE mailinglist SET sent = TRUE WHERE email = '" .
mysqli_real_escape_string($mysql, $row['email']) . "'"
);
} catch (Exception $e) {
echo 'Mailer Error (' . htmlspecialchars($row['email']) . ') ' . $mail->ErrorInfo . '<br>';
//Reset the connection to abort sending this message
//The loop will continue trying to send to the rest of the list
$mail->getSMTPInstance()->reset();
}
//Clear all addresses and attachments for the next iteration
$mail->clearAddresses();
$mail->clearAttachments();
}
In addition to #Amr most excellent code.
In order to use this in a cron fasion, two adds are useful.
$mail-> SMTPDebug = true;
$mail-> Debugoutput = function( $str, $level ) {_log($str);};
The function _log is up to you. Writing to a file, to a database or wherever. I personally have reduced this to
$mail-> Debugoutput = function( $str, $level ) {if( $level===3 ) {_log( $str ); } };
to only write the more juicier messages
the solution is to reset recipients data like this:
$Mailer->clearAddresses()
use your own variable as an instance of PHPMailer (instead of $Mailer)
$Mailer->clearAddresses()
This is the solution to avoid multiple msj to be send to the same recipient.

PHP - Query database for pending messages and call PHPmailer without refresh

I am using phpMailer, and i have a database table "list" with columns id|name|email|status.
What i am doing currently is using the phpMailer example
date_default_timezone_set('America/Toronto');
require_once('../class.phpmailer.php');
//include("class.smtp.php"); // optional, gets called from within class.phpmailer.php if not already loaded
$mail = new PHPMailer();
$body = file_get_contents('contents.html');
$body = eregi_replace("[\]",'',$body);
$id = $_GET['id'];
$mail->IsSMTP(); // telling the class to use SMTP
$mail->Host = "smtp1.xample.com;smtp2.xample.com";
$mail->SMTPAuth = true; // enable SMTP authentication
$mail->SMTPKeepAlive = true; // SMTP connection will not close after each email sent
$mail->Host = "mail.example.com"; // sets the SMTP server
$mail->Port = 25; // set the SMTP port for the GMAIL server
$mail->Username = "no-reply#example.com"; // SMTP account username
$mail->Password = "password"; // SMTP account password
$mail->SetFrom('no-reply#example.com', 'Honda example');
$mail->AddReplyTo('no-reply#example.com', 'Honda example');
$mail->Subject = "PHPMailer Test Subject via smtp, basic with authentication";
#MYSQL_CONNECT("localhost","root","password");
#mysql_select_db("osher");
$query = "SELECT full_name, email, photo FROM employee WHERE id=$id";
$result = #MYSQL_QUERY($query);
while ($row = mysql_fetch_array ($result)) {
$mail->AltBody = "To view the message, please use an HTML compatible email viewer!"; // optional, comment out and test
$mail->MsgHTML($body);
$mail->AddAddress($row["email"], $row["full_name"]);
$mail->AddStringAttachment($row["photo"], "YourPhoto.jpg");
if(!$mail->Send()) {
echo "Mailer Error (" . str_replace("#", "#", $row["email"]) . ') ' . $mail->ErrorInfo . '<br />';
} else {
echo "Message sent to :" . $row["full_name"] . ' (' . str_replace("#", "#", $row["email"]) . ')<br />';
}
// Clear all addresses and attachments for next loop
$mail->ClearAddresses();
$mail->ClearAttachments();
}
Now the code above is db-email.php Where in if my table, email has over 100 entries with status as pending, i want to be able to using php. select each rows id and call db-email.php?id=xx (where x is the id) hence the email gets sent.
This works fine, if its done manually.
i needed you guys to help me, how best I can automate this process, that is to be able to push this 100 emails (without having to refresh the page)
assuming, I am on email.php where I can probably hit (a button) and it would simply start processing, each of the row in the email table, (instead of echo'n the result) I will be updating the database with status to = 'sent'
I am not sure if I have been very clear on what I want to ask, Please do help friends.
I believe someone with expertise in jQuery/AJAX or something could help me achieve this task :)
I'm not sure what your trying to do but, you can't do anything with databases or servers via javascript or jquery because there executed in the browser and not on the server.
You could use ajax to call certain .php files on the server with some parameters to do your job !

Categories