Problem
I have a script to send newsletters to my contacts. The script works fine, if I have no attachement (proven with 2,000 emails).
Now, if I use attachments, the script works fine too. But only by sending about 30 emails.
Test-Setup
- group of 100 emails to be performed (loop).
- attachments: 2 files (total 4,6 MB) --> the script terminates (but without error message) after 50 seconds and 34 emails sent. (~ 156 MB).
Test-Variations
- changing in php.ini
memory_limit
from 100M to 500M --> has no effect. Still error after 34 emails. - putting a
sleep(5)
after each loop --> has no effect. Still error after 34 emails. - no attachments: all 100 emails are sent (about 30 seconds).
- no attachements on 2000 emails: all 2000 emails are sent (about 6 minutes).
- no effect of changing
max_execution_time
in php.ini.
Assumtions
Due to the behaviour I expect to have a memory-problem and not a time-problem.
But a test of the memory (memory_get_usage()
) in each loop showed that the memory of the first loop is 1.1 MB
and of the 34th loop 1.2 MB
.
Question
Please find below my code, but I guess it sould be ok. Does anybody has an idea what is causing the issue? Many thanks!
myMailer.class
class myMailer extends PHPMailer {
public function __construct(?bool $exceptions = true) {
$config = parse_ini_file('../../ini/config.ini', true);
parent::__construct($exceptions);
try {
// Language of Errors
$this->setLanguage('en', dirname(__FILE__) . '/../external/PHPMailer/language/');
// Server settings
$this->SMTPDebug = 0; // Enable verbose debug output
$this->isSMTP(); // Set mailer to use SMTP
$this->SMTPAuth = true; // Enable SMTP authentication
$this->SMTPSecure = 'ssl'; // Enable TLS encryption, `ssl` also accepted
$this->Port = 465; // TCP port to connect to
$this->Host = $config['smtp_server']['host']; // SMTP server
$this->Username = $config['smtp_server']['username']; // SMTP username
$this->Password = $config['smtp_server']['password']; // SMTP password
$this->CharSet ='UTF-8'; // Set Character Set
$this->isHTML(true); // Set email format to HTML
$this->setFrom("office@superman.com", "superman OFFICE");
} catch (Exception $e) {
// echo 'Message could not be sent. Mailer Error: ', $mail->ErrorInfo;
trigger_error("myMailer(): " . $this->ErrorInfo,E_USER_ERROR);
}
}
public function setBody(string $salutation, string $receiver, string $message) : void {
// Linebreak to <BR>
$message = nl2br($message);
// Create Body
$body = file_get_contents('../../ini/email_template.html');
$body = str_replace("{Receiver}", $receiver, $body);
$body = str_replace("{Salutation}", $salutation, $body);
$body = str_replace("{Message}", $message, $body);
$this->Body = $body;
}
}
send_emails.php
<?php
$groupID = $_POST['id'];
$subject = $_POST['Subject'];
$message = $_POST['Message'];
$attachments = (key_exists('Files', $_POST)) ? $_POST['Files'] : array();
$group = new Group($groupID);
// Prepare and get HTML
$htmlGenerator = HtmlGenerator::getInstance();
$htmlGenerator->setTitle("sending emails");
$header = $htmlGenerator->getWebbaseHtmlHeader();
$footer = $htmlGenerator->getWebbaseHtmlFooter();
echo $header;
?>
<div class="container">
<h1>Emailing</h1>
<h3>Group: <?php echo $group->getTitle(); ?></h3>
<h4>Number of members: <?php echo $group->getNumberOfMembers(); ?></h4>
<?php
$strScreenOutput = "
<table class='table table-sm table-hover table-responsive-md'>
<thead class='thead-dark'>
<th scope='col'>Nr.</th>
<th scope='col'>Date</th>
<th scope='col'>Company</th>
<th scope='col'>Lastname</th>
<th scope='col'>Firstname</th>
<th scope='col'>Email</th>
<th scope='col'>Newsletter</th>
<th scope='col'>Result</th>
</thead>
<tbody>";
$protocol = $strScreenOutput;
echo $strScreenOutput;
$i = 0;
$members = Contact::getAllOfGroup($groupID);
foreach ($members as $contact) {
$i++;
$datRun = date('d.m.Y (H:i:s)');
if ($contact->getNewsletter() === false) {
$result = "no sub.";
} elseif ($contact->getEmail() === "" || $contact->getEmail() === null) {
$result = "no email";
} else {
$return = $contact->sendEmail($subject, $message, $attachments);
$result = ($return===true) ? "ok" : $return;
}
// create feedback for browser
$strScreenOutput = "<tr>";
$strScreenOutput .= "<th scope='row'>".str_pad($i, 4 ,'0', STR_PAD_LEFT)."</th>";
$strScreenOutput .= "<td>{$datRun}</td>";
$strScreenOutput .= "<td>{$contact->getCompany()}</td>";
$strScreenOutput .= "<td>{$contact->getLastName()}</td>";
$strScreenOutput .= "<td>{$contact->getFirstName()}</td>";
$strScreenOutput .= "<td>{$contact->getEmail()}</td>";
$strScreenOutput .= "<td>".(($contact->getNewsletter()) ? "yes" : "no")."</td>";
$strScreenOutput .= "<td>{$result}</td>";
$strScreenOutput .= "</tr>";
echo str_pad($strScreenOutput,4096)."
"; // Add some additional blanks to enable flushing (as some browsers suppress flushing)
// create internal protocol
$protocol .= $strScreenOutput . "
";
// Send to browser
flush();
ob_flush();
// add some execution time
set_time_limit(30);
}
$strScreenOutput = "</tbody>
</table>";
$protocol .= $strScreenOutput;
echo $strScreenOutput;
?>
<h3>Emails successfully transmitted.</h3>
</div>
<?php
// send protocols
$protocol = "<h3>Group: {$group->getTitle()}</h3>".$protocol;
$protocol = "<div style='margin-top: 30px'>$protocol</div>";
$internal = new Contact(1);
$internal->sendEmail("PROTOCOL: ".$subject, $message . $protocol, $attachments);
echo $footer;
Contact::sendEmail()
public function sendEmail(string $subject, string $message, array $attachments = array()) {
$mail = new myMailer();
if ( is_null($this->getEmail() || $this->getEmail() == "") ) {
return false;
} else {
try {
// Compose Email
$mail->addAddress($this->getEmail(), $this->getFirstName() . " " . $this->getLastName());
$mail->Subject = $subject;
$mail->setBody($this->getSalutationText(), $this->getAddress(), $message);
foreach ($attachments as $file) {
$uploadPath = $_SERVER['DOCUMENT_ROOT'] . "/../../files/email_attachments/";
$file_url = $uploadPath.$file;
if (! is_dir($uploadPath)) { die("Folder \"$uploadPath\" does not exist.");}
if (! file_exists($file_url)) { die("File \"$file_url\" does not exist."); }
$mail->addAttachment($file_url);
}
$return = $mail->send();
// clean up
$mail->clearAddresses();
$mail->clearAttachments();
} catch (Exception $error) {
$return = $error->getMessage();
}
return $return;
}
unset($mail);
}