I am using phpmailer to send bulk emails to my subscribers but I am facing a terrible problem that is when I send emails to my subscribers, each subscriber is getting the same email more than once. some are getting 4 times and some are getting 14 times.
I am fetching subscriber emails where flag = 0 through Mysql table and at the end of the while loop I am updating subscriber flag to 1. I know its a issue of loop but I don't know where I am doing wrong or even not sure should I used while loop or something else to fix this issue. Any help would be highly grateful to me.
here is my code:
<?php
$db_host = "localhost";
$db_username = "root";
$db_pass = "";
$link= mysqli_connect("$db_host","$db_username","$db_pass", "mydb") or die ("could not connect to mysql");
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
// Simply:
//Load Composer's autoloader
require 'vendor/autoload.php';
if(isset($_POST['send'])){
$mail = new PHPMailer(true); // Passing `true` enables exceptions
try {
$query = "select customer_id, customer_name, customer_email from subscribers where flag_email = 0";
$result = mysqli_query($link, $query) or die("No customer in the table");;
while($values = mysqli_fetch_array($result)){
$id = $values['customer_id'];
$name = $values['customer_name'];
$toemail = $values['customer_email'];
//Server settings
$mail->SMTPDebug = 1; // Enable verbose debug output
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp server'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'username'; // SMTP username
$mail->Password = 'password'; // SMTP password
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587; // TCP port to connect to
//Recipients
$mail->setFrom('username', 'username');
$mail->addReplyTo('myemail', 'name');
$mail->addBCC(''.$toemail.'', ''.$name.'');
//Content
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = 'Here is the subject';
$mail->Body = 'Here is the message';
$mail->send();
echo 'Message has been sent';
$upd_query = "update subscribers set flag_email = 1 where customer_id = ".$id."";
$result= mysqli_query($link, $upd_query);
}
} catch (Exception $e) {
echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
}
}
This is happening because you're not clearing the to addresses each time around your loop - notice the method is called addAddress, not setAddress, and it does what that suggests. You need to call $mail->clearAddresses(); at the end of your loop to reset it. You're doing things mostly right otherwise, but there are a few things you can do to improve efficiency, mainly using keep alive and setting all the properties that are common to all messages before your loop.
You can see all this working in the mailing list example provided with PHPMailer.
The issue i'm seeing is, the replacement of result variable with update output inside while loop. Please use different variable name inside while loop
$upd_query = "update subscribers set flag_email = 1 where customer_id = ".$id."";
$updateResult= mysqli_query($link, $upd_query);
mailer object creation can be move inside to while loop
$query = "select customer_id, customer_name, customer_email from subscribers where flag_email = 0";
$result = mysqli_query($link, $query) or die("No customer in the table");;
while($values = mysqli_fetch_assoc($result)){
$mail = new PHPMailer(true);
....
}
Related
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.
this code send emails to all employees from mail table where the cols are Id, Name, Email. Now for example,
if there are 3 employees, then the first employee gets all the 3 payslips as attachment, the 2 employee gets the 2nd and 3rd payslip and the 3rd employee gets the 3rd payslip only. I think that the stored email values are creating the problem and so I want to clear the data stored in a $sendto, $pdf and $filename variable. I have tried apc_delete(), unset(), and also $var = "" but none is working.
this is the code :
<?php
//require 'mailerClass/PHPMailerAutoload.php';
require_once '../connect.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
//Load composer's autoloader
require 'vendor/autoload.php';
$mail = new PHPMailer;
ini_set('max_execution_time', 300); //300 seconds = 5 minutes
$cin =1;
$sql = "SELECT * FROM mail";
$query = mysqli_query($con, $sql);
$count = mysqli_num_rows($query);
try {
while($row = mysqli_fetch_array($query, MYSQL_ASSOC)){
//
$sendto = $row['Email'];
//attachment pdf files path array
$path = "C:/Reports/";
$file = $row['Name'];
$extension = ".pdf";
$filename = $file.$extension;
$pdf = $path. $file. $extension ;
//Server settings
$mail->SMTPDebug = 2; // Enable verbose debug output
//$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.mail.yahoo.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'username#yahoo.com'; // SMTP username
$mail->Password = '#password'; // SMTP password
$mail->SMTPSecure = 'ssl'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 465; //587; // TCP port to connect to
//Recipients
$mail->setFrom('mediakraft64#yahoo.com');
$mail->addAddress($sendto); // Add a recipient
$mail->addReplyTo('mediakraft64#yahoo.com');
$mail->addCC('mediakraft64#yahoo.com');
//$mail->addBCC('bcc#example.com');
//Content
$mail->isHTML(true); // Set email format to HTML
$mail->Subject = 'Payslip';
$mail->Body = 'Payslip from Mediakraft Check file attachment';
$mail->AltBody = 'Payslip from Mediakraft Check file attachment';
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->addAttachment($pdf, $filename); //= $_POST['mypdf'];
$mail->send();
echo 'Message has been sent';
//unset($sendto);
$sendto = " ";
$pdf = " ";
$mail->clearAttachments();
sleep(5);
} //end while
echo"<script>alert('Email Sent... Successfully'); document.close();</script>";
} catch (Exception $e) {
echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
}
?>
any help ??
Thanks
If you want to reuse the variable, just reassign a new value to it:
$a = 1;
$a = "a string";
$a = new MyClass();
The old value is automatically deleted (if needed) by PHP's engine. You don't have to worry about that.
unset will remove the variable and the contents. Referencing $a after calling unset($a) will throw an error. Also, you have to think about the scope of the variable you are unsetting, and if it was passed by reference or by copy inside a function.
To remove only the contents, there is no specific built-in PHP instruction. Just assign null or false to it if you have to signal that the variable does not hold valid data anymore or to free the memory it currently uses.
There is not such thing as "clearing a variable". You can use empty() to see if PHP considers that a variable is empty.
I'm using PHPMailer to send a forgot password email to users and when the user submits their email to the code below the page takes a few minutes to load!! Im not expecting the email to be send instantly but is there a way to make it so it will redirect the user while still sending the email?
I'm fairly new to php so there could be something simple I'm missing out.
Here is my code:
<?php
define('DB_NAME', '#######');
define('DB_USER', '#######');
define('DB_PASSWORD', '#######');
define('DB_HOST', 'localhost');
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if (!$conn) {
die('Could not connect: ' . mysqli_connect_error());
}
$recovery = $_POST['recovery'];
$sql = "SELECT forgotpass FROM members WHERE username = '$recovery'";
$result=mysqli_query($conn, $sql);
while($row = mysqli_fetch_array($result, MYSQLI_ASSOC))
{
echo "Please wait... ";
$mailto = $recovery;
$mailSub = "Here's your password!";
$mailMsg = $row['forgotpass'];
require 'PHPMailer-master/PHPMailerAutoload.php';
$mail = new PHPMailer();
$mail ->IsSmtp();
$mail ->SMTPDebug = 0;
$mail ->SMTPAuth = true;
$mail ->SMTPSecure = 'ssl';
$mail ->Host = "bemoresocial.co.uk";
$mail ->Port = 465; // or 587
$mail ->IsHTML(true);
$mail ->Username = "info#bemoresocial.co.uk";
$mail ->Password = "#########";
$mail ->SetFrom("info#bemoresocial.co.uk");
$mail ->Subject = $mailSub;
$mail ->Body = $mailMsg;
$mail ->AddAddress($mailto);
if(!$mail->Send())
{
echo "Something went wrong :(";
}
else
{
header('Location: ./success.php');
}
}
mysqli_close($conn);
SMTP can be slow, often deliberately so; it's really not well suited to sending email to remote servers during page submission processing, as PHP is typically used for.
The best way to fix this is to install a local mail server such as Postfix. Submitting messages to that (using SMTP to localhost) will be very fast, and then the mail server can deal with the slower onward delivery. This is effectively a queuing system, but is much easier than using something like beanstalkd, redis, or rabbitmq to build your own queue.
In your code, the require inside the while loop will cause a fatal error if it goes round more than once, because you will be requiring the same class file repeatedly. To send to multiple recipients efficiently, reuse the PHPMailer instance - you don't need to start from scratch every time around the loop - if you can use keepalive, this will also be much faster - see the mailing list example provided with PHPMailer for how to do that.
Though it's not part of the problem you're seeing, I can see you've based your code on an obsolete example, and you're using an old version of PHPMailer - upgrade to 6.0.
I ran into the same problem with VERY SLOW running PHP Mailer, like 3 minutes to send 15 emails using the Website Host SMTP server.
What I did to make it finish within seconds is to switch to gmail smtp. When you do this you'll need to go to Google and generate an app password Sign In Using App Password. So in your code use your normal email address with the generated password (note your normal password still works for your normal email access) and use tls security.
When I moved the program from testing to production the email failed. I found I had to go to google and just sign in and everything worked again.
Also note if you turn on SMTPDebug = 2; it will show you each step and the time it finished.
include ('class.PHPMailer.php');
$mail = new PHPMailer(true); // Passing `true` enables exceptions
try {
//Server settings
$mail->SMTPDebug = 0; // 0=no output, 1=Commands, 2=Data & Commands, 3=2+connection status 4=Low-Level data output
$mail->isSMTP(); // Set mailer to use SMTP
$mail->Host = 'smtp.gmail.com'; // Specify main and backup SMTP servers
$mail->SMTPAuth = true; // Enable SMTP authentication
$mail->Username = 'mickeymouse#gmail.com'; // SMTP username
$mail->Password = 'aaaabbbbccccdddd'; // SMTP password (used google Generated app password)
$mail->SMTPSecure = 'tls'; // Enable TLS encryption, `ssl` also accepted
$mail->Port = 587; // TCP port to connect to
// Recipients
$i = 0; $address="";
foreach($recipients as $i => $address)
{
$mail->addAddress($address);
}
$mail->addReplyTo('mickeymouse#gmail.com', 'Mickey');
$mail->isHTML(true);
$mail->setFrom('mickeymouse#gmail.com', 'Mickey');// Set email format to HTML
$mail->Subject = $subject;
$mail->Body = $body;
$mail->send();
$message = 'Message has been sent';
}
catch (Exception $e) {
$message = 'Message could not be sent.'."<br>".
'Mailer Error: ' . $mail->ErrorInfo;
}
I'm using PHPMailer to send an email from a form to all the emails in a MySQL database.
I've managed to get the form to send an email to each email but I'm now trying to use their name in the email sent to them meaning each email woud begin with "Hello, [their-name],".
I'm using an array to loop through the results of my database query and adding each email and name as $mail->addAddress('email#email.com, 'name');
I've tried saving the variable $name = $row['name'}; inside the while loop but it inputs the last name from the database into all the emails.
My question is: Is it possible to access the 'name' part of the function and use it each email. Or is it better to take their name from the database itself? And if so how would I go about doing that?
Also, when the email sends to each recipient, it shows all of the recipients in the "To:" field. This would be pretty bad for privacy. Is there a way to prevent this?
I'm fairly new to PHP and MySQL still so explanatory answers would be greatly appreciated.
Please find my code below.
<?php
// Take message value from form
$message = preg_replace("/\r\n|\r/", "<br />", $_REQUEST['message']);
$message = trim($message);
// Error reporting on
error_reporting(E_STRICT | E_ALL);
date_default_timezone_set('Etc/UTC');
// Require PHPMailer
require 'PHPMailer/PHPMailerAutoload.php';
$mail = new PHPMailer;
$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 = 587;
$mail->Username = 'my#email.com';
$mail->Password = 'mypassword';
$mail->setFrom('myemail#email.com', 'Me');
$mail->addReplyTo('myemail#email.com', 'Me');
$mail->Subject = "Newsletter";
/* ----- Add address function below ----- */
// Require Database connection
require 'dbcon.php';
// SELECT email and name values from database
$query = "SELECT email, name FROM customers";
// Result = the database query
$result = $con->query($query);
// Get array of users email addresses and names.
while($row = $result->fetch_array())
{
// Add recipients from values found in database
$mail->addAddress($row["email"], $row["name"]);
}
/* ----- Add address function above ----- */
$mail->Body ="Hello, <br> <br> $message";
$mail->AltBody = $message;
if(!$mail->Send())
{
echo "Message could not be sent.";
echo "Mailer Error: " . $mail->ErrorInfo;
exit;
}
print 'Sent!';
?>
You are currently setting up to send a single email with several recipients. For security and etiquette, you should address the email to yourself (outside the loop):
$mail->addAddress('myemail#email.com', 'Me');
...and then add all of the recipients as BCC (inside the loop):
$mail->addBCC($row["email"], $row["name"]);
As for the name field. Add a debug line inside your loop and take a look at your output. Is the name field the same each time?
print "Adding recipient: {$row['email']} {$row['name']}";
If you want to sent several emails, one to each recipient, you need to instantiate a new email inside the loop each time.
[UPDATE]
If you want to send a single message to each recipient, then instantiate a new email each time (inside your loop):
<?php
// Take message value from form
$message = preg_replace("/\r\n|\r/", "<br />", $_REQUEST['message']);
$message = trim($message);
// Error reporting on
error_reporting(E_STRICT | E_ALL);
date_default_timezone_set('Etc/UTC');
// Require PHPMailer
require 'PHPMailer/PHPMailerAutoload.php';
// Require Database connection
require 'dbcon.php';
// SELECT email and name values from database
$query = "SELECT email, name FROM customers";
// Result = the database query
$result = $con->query($query);
// Get array of users email addresses and names.
while($row = $result->fetch_array())
{
// Instantiate a NEW email
$mail = new PHPMailer;
// Set the email settings
$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 = 587;
$mail->Username = 'my#email.com';
$mail->Password = 'mypassword';
$mail->setFrom('myemail#email.com', 'Me');
$mail->addReplyTo('myemail#email.com', 'Me');
$mail->Subject = "Newsletter";
// Add recipient from values found in database
$mail->addAddress($row["email"], $row["name"]);
$mail->Body ="Hello, <br> <br> $message";
$mail->AltBody = $message;
if(!$mail->Send())
{
echo "Message could not be sent.";
echo "Mailer Error: " . $mail->ErrorInfo;
exit;
}
print "Sent mail to: {$row["email"]}, {$row["name"]}";
}
?>
First time posting so any help greatly appreciated...
I'm happy the SQL query works and returns two 2 rows
I'm happy to leave message as "hello" for now
I've looked through many answers and none solve my problem
This function will send the mail to only the first (of 2) recipient but not the second.
If anyone could tell me where I am going wrong I would be very grateful...
public function sendOrderEmail($customer_order_id)
{
$sql3=" SELECT DISTINCT w.user_id AS supplier_id,
s.trading_name AS supplier_name,
s.contact_email AS supplier_email
FROM supplier_info AS s
JOIN wine AS w ON w.user_id = s.id
JOIN order_detail AS o ON o.wine_id = w.id
WHERE o.order_no_id = :customer_order_id ORDER BY supplier_id DESC;";
$query3 = $this->db->prepare($sql3);
$query3->execute(array(':customer_order_id' => intval($customer_order_id)));
//$result3 = $query3->fetchAll();
while($row3 = $query3->fetch(PDO::FETCH_ASSOC)){
// while ($row3 = $query3->fetch()) {
$supplier_name = $row3['supplier_name'];
$supplier_email = $row3['supplier_email'];
// foreach($result3 as $key => $output){
//$supplier_id = $output->supplier_id;
// $supplier_email = $output->email; */
$mail = new PHPMailer;
if (EMAIL_USE_SMTP) {
$mail->IsSMTP();
$mail->SMTPDebug = PHPMAILER_DEBUG_MODE;
$mail->SMTPAuth = EMAIL_SMTP_AUTH;
if (defined('EMAIL_SMTP_ENCRYPTION')) {
$mail->SMTPSecure = EMAIL_SMTP_ENCRYPTION;
}
$mail->Host = EMAIL_SMTP_HOST;
$mail->Username = EMAIL_SMTP_USERNAME;
$mail->Password = EMAIL_SMTP_PASSWORD;
$mail->Port = EMAIL_SMTP_PORT;
} else {
$mail->IsMail();
}
// Build email body
$message = "<p>hello</p>";
// fill mail with data
$mail->isHTML(true);
$mail->From = "billyjlennon#gmail.com";
$mail->FromName = "Me";
$mail->AddAddress($supplier_email);
$mail->Subject = "Your request ";
$mail->Body = $message;
// final sending and check
if($mail->Send()) {
$_SESSION["feedback_positive"][] = FEEDBACK_CONTACT_MAIL_SENDING_SUCCESSFUL;
return true;
} else {
$_SESSION["feedback_negative"][] = FEEDBACK_CONTACT_MAIL_SENDING_ERROR . $mail- >ErrorInfo;
return false;
}
}
First up, you should really check the PHPMailer documentation first since there is an example that does exactly this. You can't have been looking too hard.
In your code, don't create a new PHPMailer instance every time around the loop - all the things that remain the same (isSmtp, Host, Port, etc) should be set once, before the loop. Inside the loop you should do these things:
Set the message body
Set the recipient
Send the message
Store any errors
Clear the recipient list (use clearAllRecipients())
You've also got return statements after your send check - these will exit your function completely, so it will never send more than one message. You should gather the errors as you go through your list and present (or return) them at the end.
I used a while loop just around the addAddress as suggested by #synchro above. Foreach iterator syntax wouldn't work as I'm using PHP 5.3 (as per comment in the link to PHPmailer above).
Then I took the return outside the while loop and everything worked just fine.
This is the code that worked:
(Any improvements on it gratefully accepted)
public function sendOrderEmail($customer_order_id)
{
$sql3 = " SELECT DISTINCT w.user_id AS supplier_id,
s.trading_name AS supplier_name,
s.contact_email AS supplier_email
FROM supplier_info AS s
JOIN wine AS w ON w.user_id = s.id
JOIN order_detail AS o ON o.wine_id = w.id
WHERE o.order_no_id = :customer_order_id ORDER BY supplier_id DESC;";
$query3 = $this->db->prepare($sql3);
$query3->execute(array(':customer_order_id' => intval($customer_order_id)));
$mail = new PHPMailer;
if (EMAIL_USE_SMTP) {
$mail->IsSMTP();
$mail->Host = EMAIL_SMTP_HOST;
$mail->Username = EMAIL_SMTP_USERNAME;
$mail->Password = EMAIL_SMTP_PASSWORD;
$mail->Port = EMAIL_SMTP_PORT;
$mail->SMTPDebug = PHPMAILER_DEBUG_MODE;
$mail->SMTPAuth = EMAIL_SMTP_AUTH;
$mail->SMTPKeepAlive = true;
if (defined('EMAIL_SMTP_ENCRYPTION')) {
$mail->SMTPSecure = EMAIL_SMTP_ENCRYPTION;
}
} else {
$mail->IsMail();
}
$mail->isHTML(true);
$mail->From = "billyjlennon#gmail.com";
$mail->FromName = "Me";
while ($row3 = $query3->fetch(PDO::FETCH_ASSOC)) {
// Build email body
$message = "<p>hello</p>";
// fill mail with data
$mail->AddAddress($row3['supplier_email']);
$mail->Subject = "Your request to " . $row3['supplier_name'];
$mail->Body = $message;
// final sending and check
if ($mail->Send()) {
// $_SESSION["feedback_positive"][] = FEEDBACK_CONTACT_MAIL_SENDING_SUCCESSFUL;
//return true;
} else {
$_SESSION["feedback_negative"][] = FEEDBACK_CONTACT_MAIL_SENDING_ERROR . $mail->ErrorInfo;
// return false;
break;
}
$mail->ClearAddresses();
}
return true;
}