diff --git a/examples/benchmark_reduce_mime_encoding.phps b/examples/benchmark_reduce_mime_encoding.phps
new file mode 100644
index 0000000000..38489f83c7
--- /dev/null
+++ b/examples/benchmark_reduce_mime_encoding.phps
@@ -0,0 +1,230 @@
+ /tmp/1mb.jpg
+//
+$image_files = array(
+ '1.8kb' => 'phpmailer_mini.png',
+ '5.7kb' => 'images/phpmailer.png',
+ '50kb' => '/tmp/50kb.jpg',
+ '100kb' => '/tmp/100kb.jpg',
+ '200kb' => '/tmp/200kb.jpg',
+ '500kb' => '/tmp/500kb.jpg',
+);
+
+$receiver_count = 200;
+
+$receiver_list = [];
+for ($i=0 ; $i<$receiver_count ; ++$i)
+ $receiver_list[] = 'whoto'.$i.'@example.com';
+
+echo "Genrate $receiver_count receivers: " . count($receiver_list) . "\n";
+
+$results = [];
+$test_count_per_run = 10;
+
+echo "Sending $receiver_count mails with creating a new PHPMailer instance:\n";
+{
+ foreach ($image_files as $filesize => $image_file) {
+ echo "\tWith $filesize attachment, path = $image_file\n";
+ $total_time = 0;
+ for ($i=1; $i<=$test_count_per_run ; ++$i) {
+ $start = microtime(true);
+ foreach ($receiver_list as $receiver) {
+ $mail = new PHPMailer;
+ $mail->isSMTP();
+ $mail->SMTPDebug = 0;
+ $mail->Host = 'mail.example.com';
+ $mail->Port = 25;
+ $mail->SMTPAuth = true;
+ $mail->Username = 'yourname@example.com';
+ $mail->Password = 'yourpassword';
+ $mail->setFrom('from@example.com', 'First Last');
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+ $mail->addAddress($receiver);
+ $mail->Subject = 'Hi '.$receiver.'!';
+ $mail->msgHTML(str_replace( '
This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+
+ //Attach an image file
+ $mail->addAttachment($image_file);
+
+ //Build the mail content
+ $mail->preSend();
+ unset($mail);
+ }
+ $cost = microtime(true) - $start;
+ $total_time += $cost;
+ }
+ $avg_time = $total_time / floatval($test_count_per_run);
+ echo sprintf("\t\ttest $test_count_per_run runs, avg. time: %.5f seconds\n", $avg_time );
+
+ if (!isset($results[$filesize]))
+ $results[$filesize] = [];
+ $results[$filesize]['method1'] = $avg_time;
+ }
+}
+
+echo "Sending $receiver_count mails with only one PHPMailer instance: \n";
+{
+ foreach ($image_files as $filesize => $image_file) {
+ echo "\tWith $filesize attachment, path = $image_file\n";
+ $total_time = 0;
+ for ($i=1; $i<=$test_count_per_run ; ++$i) {
+ //echo "\t\tRun $i: ";
+ $start = microtime(true);
+
+ $mail = new PHPMailer;
+ $mail->isSMTP();
+ $mail->SMTPDebug = 0;
+ $mail->Host = 'mail.example.com';
+ $mail->Port = 25;
+ $mail->SMTPAuth = true;
+ $mail->Username = 'yourname@example.com';
+ $mail->Password = 'yourpassword';
+ $mail->setFrom('from@example.com', 'First Last');
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+
+ foreach ($receiver_list as $receiver) {
+ $mail->clearAddresses();
+ $mail->addAddress($receiver);
+ $mail->Subject = 'Hi '.$receiver.'!';
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+
+ //Attach an image file
+ $mail->addAttachment($image_file);
+
+ //Build the mail content
+ $mail->preSend();
+ }
+ $cost = microtime(true) - $start;
+ //echo "$cost sec\n";
+ $total_time += $cost;
+ }
+ $avg_time = $total_time / floatval($test_count_per_run);
+ echo sprintf("\t\ttest $test_count_per_run runs, avg. time: %.5f seconds\n", $avg_time );
+
+ if (!isset($results[$filesize]))
+ $results[$filesize] = [];
+ $results[$filesize]['method2'] = $avg_time;
+ }
+}
+
+echo "Sending $receiver_count mails with creating a new PHPMailer instance and ReduceMIMEEncodeCacheStore: \n";
+{
+ foreach ($image_files as $filesize => $image_file) {
+ echo "\tWith $filesize attachment, path = $image_file\n";
+ $total_time = 0;
+ for ($i=1; $i<=$test_count_per_run ; ++$i) {
+ //echo "\t\tRun $i: ";
+ $start = microtime(true);
+ $cacheLookupTable = [];
+ foreach ($receiver_list as $receiver) {
+ $mail = new PHPMailer;
+ $mail->isSMTP();
+ $mail->SMTPDebug = 0;
+ $mail->Host = 'mail.example.com';
+ $mail->Port = 25;
+ $mail->SMTPAuth = true;
+ $mail->Username = 'yourname@example.com';
+ $mail->Password = 'yourpassword';
+ $mail->setFrom('from@example.com', 'First Last');
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+ $mail->addAddress($receiver);
+ $mail->Subject = 'Hi '.$receiver.'!';
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+
+ //Attach an image file
+ $mail->addAttachment($image_file);
+
+ $mail->ReduceMIMEEncodeCacheStore = &$cacheLookupTable;
+
+ //Build the mail content
+ $mail->preSend();
+ unset($mail);
+ }
+ $cost = microtime(true) - $start;
+ //echo "$cost sec\n";
+ $total_time += $cost;
+ }
+ $avg_time = $total_time / floatval($test_count_per_run);
+ echo sprintf("\t\ttest $test_count_per_run runs, avg. time: %.5f seconds\n", $avg_time );
+
+ if (!isset($results[$filesize]))
+ $results[$filesize] = [];
+ $results[$filesize]['method3'] = $avg_time;
+ }
+
+}
+echo "Sending $receiver_count mails with only one PHPMailer instance and ReduceMIMEEncodeCacheStore: \n";
+{
+ foreach ($image_files as $filesize => $image_file) {
+ echo "\tWith $filesize attachment, path = $image_file\n";
+ $total_time = 0;
+ for ($i=1; $i<=$test_count_per_run ; ++$i) {
+ //echo "\t\tRun $i: ";
+ $start = microtime(true);
+ $cacheLookupTable = [];
+
+ $mail = new PHPMailer;
+ $mail->isSMTP();
+ $mail->SMTPDebug = 0;
+ $mail->Host = 'mail.example.com';
+ $mail->Port = 25;
+ $mail->SMTPAuth = true;
+ $mail->Username = 'yourname@example.com';
+ $mail->Password = 'yourpassword';
+ $mail->setFrom('from@example.com', 'First Last');
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+
+ foreach ($receiver_list as $receiver) {
+ $mail->clearAddresses();
+ $mail->addAddress($receiver);
+ $mail->Subject = 'Hi '.$receiver.'!';
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+
+ //Attach an image file
+ $mail->addAttachment($image_file);
+
+ $mail->ReduceMIMEEncodeCacheStore = &$cacheLookupTable;
+
+ //Build the mail content
+ $mail->preSend();
+ }
+ $cost = microtime(true) - $start;
+ //echo "$cost sec\n";
+ $total_time += $cost;
+ }
+ $avg_time = $total_time / floatval($test_count_per_run);
+ echo sprintf("\t\ttest $test_count_per_run runs, avg. time: %.5f seconds\n", $avg_time );
+
+ if (!isset($results[$filesize]))
+ $results[$filesize] = [];
+ $results[$filesize]['method4'] = $avg_time;
+ }
+}
+//
+// Print results
+echo "\n";
+echo "Size\tMethod1\tMethod2\tMethod3\tMethod4\n";
+foreach($results as $filesize => $test_method_result) {
+ echo "$filesize";
+ foreach($test_method_result as $value)
+ echo sprintf("\t%3.4f", $value);
+ echo "\n";
+}
diff --git a/examples/smtp_reduce_mime_encoding.phps b/examples/smtp_reduce_mime_encoding.phps
new file mode 100644
index 0000000000..f49aa48864
--- /dev/null
+++ b/examples/smtp_reduce_mime_encoding.phps
@@ -0,0 +1,232 @@
+isSMTP();
+ //Enable SMTP debugging
+ // 0 = off (for production use)
+ // 1 = client messages
+ // 2 = client and server messages
+ $mail->SMTPDebug = 0;
+ //Set the hostname of the mail server
+ $mail->Host = 'mail.example.com';
+ //Set the SMTP port number - likely to be 25, 465 or 587
+ $mail->Port = 25;
+ //Whether to use SMTP authentication
+ $mail->SMTPAuth = true;
+ //Username to use for SMTP authentication
+ $mail->Username = 'yourname@example.com';
+ //Password to use for SMTP authentication
+ $mail->Password = 'yourpassword';
+ //Set who the message is to be sent from
+ $mail->setFrom('from@example.com', 'First Last');
+ //Set an alternative reply-to address
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+ //Set who the message is to be sent to
+ $mail->addAddress($receiver);
+ //Set the subject line
+ $mail->Subject = 'Hi '.$receiver.'!';
+ //Read an HTML message body from an external file, convert referenced images to embedded,
+ //convert HTML into a basic plain-text alternative body
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ //Replace the plain text body with one created manually
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+ //Attach an image file
+ $mail->addAttachment('images/phpmailer_mini.png');
+
+ //Build the mail content
+ $mail->preSend();
+ unset($mail);
+}
+$cost = microtime(true) - $start;
+
+echo $cost . " sec\n";
+
+echo "Sending $receiver_count mails with only one PHPMailer instance: ";
+
+$start = microtime(true);
+{
+ //Create a new PHPMailer instance
+ $mail = new PHPMailer;
+ //Tell PHPMailer to use SMTP
+ $mail->isSMTP();
+ //Enable SMTP debugging
+ // 0 = off (for production use)
+ // 1 = client messages
+ // 2 = client and server messages
+ $mail->SMTPDebug = 0;
+ //Set the hostname of the mail server
+ $mail->Host = 'mail.example.com';
+ //Set the SMTP port number - likely to be 25, 465 or 587
+ $mail->Port = 25;
+ //Whether to use SMTP authentication
+ $mail->SMTPAuth = true;
+ //Username to use for SMTP authentication
+ $mail->Username = 'yourname@example.com';
+ //Password to use for SMTP authentication
+ $mail->Password = 'yourpassword';
+ //Set who the message is to be sent from
+ $mail->setFrom('from@example.com', 'First Last');
+ //Set an alternative reply-to address
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+
+ //Attach an image file
+ $mail->addAttachment('images/phpmailer_mini.png');
+
+ foreach ($receiver_list as $receiver) {
+ //Reset addresses
+ $mail->clearAddresses();
+ //Set who the message is to be sent to
+ $mail->addAddress($receiver);
+ //Set the subject line
+ $mail->Subject = 'Hi '.$receiver.'!';
+ //Read an HTML message body from an external file, convert referenced images to embedded,
+ //convert HTML into a basic plain-text alternative body
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ //Replace the plain text body with one created manually
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+
+ //Build the mail content
+ $mail->preSend();
+ }
+}
+$cost = microtime(true) - $start;
+
+echo $cost . " sec\n";
+//
+// ---
+//
+echo "Sending $receiver_count mails with creating a new PHPMailer instance and ReduceMIMEEncodeCacheStore: ";
+
+$start = microtime(true);
+$cacheLookupTable = [];
+foreach ($receiver_list as $receiver) {
+ //Create a new PHPMailer instance
+ $mail = new PHPMailer;
+ //Tell PHPMailer to use SMTP
+ $mail->isSMTP();
+ //Enable SMTP debugging
+ // 0 = off (for production use)
+ // 1 = client messages
+ // 2 = client and server messages
+ $mail->SMTPDebug = 0;
+ //Set the hostname of the mail server
+ $mail->Host = 'mail.example.com';
+ //Set the SMTP port number - likely to be 25, 465 or 587
+ $mail->Port = 25;
+ //Whether to use SMTP authentication
+ $mail->SMTPAuth = true;
+ //Username to use for SMTP authentication
+ $mail->Username = 'yourname@example.com';
+ //Password to use for SMTP authentication
+ $mail->Password = 'yourpassword';
+ //Set who the message is to be sent from
+ $mail->setFrom('from@example.com', 'First Last');
+ //Set an alternative reply-to address
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+ //Set who the message is to be sent to
+ $mail->addAddress($receiver);
+ //Set the subject line
+ $mail->Subject = 'Hi '.$receiver.'!';
+ //Read an HTML message body from an external file, convert referenced images to embedded,
+ //convert HTML into a basic plain-text alternative body
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ //Replace the plain text body with one created manually
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+ //Attach an image file
+ $mail->addAttachment('images/phpmailer_mini.png');
+
+ //Set Reduce MIME Encode Cache Store
+ $mail->ReduceMIMEEncodeCacheStore = &$cacheLookupTable;
+
+ //Build the mail content
+ $mail->preSend();
+ unset($mail);
+}
+$cost = microtime(true) - $start;
+
+echo $cost . " sec\n";
+//
+// ---
+//
+echo "Sending $receiver_count mails with only one PHPMailer instance and ReduceMIMEEncodeCacheStore: ";
+
+$start = microtime(true);
+$cacheLookupTable = [];
+{
+ //Create a new PHPMailer instance
+ $mail = new PHPMailer;
+ //Tell PHPMailer to use SMTP
+ $mail->isSMTP();
+ //Enable SMTP debugging
+ // 0 = off (for production use)
+ // 1 = client messages
+ // 2 = client and server messages
+ $mail->SMTPDebug = 0;
+ //Set the hostname of the mail server
+ $mail->Host = 'mail.example.com';
+ //Set the SMTP port number - likely to be 25, 465 or 587
+ $mail->Port = 25;
+ //Whether to use SMTP authentication
+ $mail->SMTPAuth = true;
+ //Username to use for SMTP authentication
+ $mail->Username = 'yourname@example.com';
+ //Password to use for SMTP authentication
+ $mail->Password = 'yourpassword';
+ //Set who the message is to be sent from
+ $mail->setFrom('from@example.com', 'First Last');
+ //Set an alternative reply-to address
+ $mail->addReplyTo('replyto@example.com', 'First Last');
+ //Attach an image file
+ $mail->addAttachment('images/phpmailer_mini.png');
+
+ //Set Reduce MIME Encode Cache Store
+ $mail->ReduceMIMEEncodeCacheStore = &$cacheLookupTable;
+
+ foreach ($receiver_list as $receiver) {
+ //Reset addresses
+ $mail->clearAddresses();
+ //Set who the message is to be sent to
+ $mail->addAddress($receiver);
+ //Set the subject line
+ $mail->Subject = 'Hi '.$receiver.'!';
+ //Read an HTML message body from an external file, convert referenced images to embedded,
+ //convert HTML into a basic plain-text alternative body
+ $mail->msgHTML(str_replace( 'This is a test of PHPMailer.
', 'Hi '.$receiver.'
', file_get_contents('contents.html')), __DIR__);
+ //Replace the plain text body with one created manually
+ $mail->AltBody = 'This is a plain-text message body for '.$receiver;
+
+ //Build the mail content
+ $mail->preSend();
+ }
+}
+$cost = microtime(true) - $start;
+
+echo $cost . " sec\n";
diff --git a/src/PHPMailer.php b/src/PHPMailer.php
index 16359fabe5..641082dfb2 100644
--- a/src/PHPMailer.php
+++ b/src/PHPMailer.php
@@ -420,6 +420,17 @@ class PHPMailer
*/
protected $SingleToArray = [];
+ /**
+ * Whether to send the same message to each receiver
+ * with small changes in mail content.
+ * Use a lookup table to reduce attachment encoding.
+ *
+ * Storage for attachment encoding.
+ *
+ * @var array
+ */
+ public $ReduceMIMEEncodeCacheStore = false;
+
/**
* Whether to generate VERP addresses on send.
* Only applicable when sending via SMTP.
@@ -3028,14 +3039,34 @@ protected function attachAll($disposition_type, $boundary)
protected function encodeFile($path, $encoding = self::ENCODING_BASE64)
{
try {
- if (!static::isPermittedPath($path) || !file_exists($path)) {
- throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
- }
- $file_buffer = file_get_contents($path);
- if (false === $file_buffer) {
- throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ $file_buffer = '';
+ if ($this->ReduceMIMEEncodeCacheStore !== false) {
+ $cache_key = $path . "\t" . $encoding;
+ if (array_key_exists($cache_key, $this->ReduceMIMEEncodeCacheStore)) {
+ $file_buffer = &$this->ReduceMIMEEncodeCacheStore[$cache_key];
+ $this->edebug('encodeFile: use cache [' . $path . '][' . $encoding . ']');
+ } else {
+ if (!static::isPermittedPath($path) || !file_exists($path)) {
+ throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ }
+ $file_buffer = file_get_contents($path);
+ if (false === $file_buffer) {
+ throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ }
+ $file_buffer = $this->encodeString($file_buffer, $encoding);
+ $this->ReduceMIMEEncodeCacheStore[$cache_key] = $file_buffer;
+ $this->edebug('encodeFile: set cache [' . $path . '][' . $encoding . ']');
+ }
+ } else {
+ if (!static::isPermittedPath($path) || !file_exists($path)) {
+ throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ }
+ $file_buffer = file_get_contents($path);
+ if (false === $file_buffer) {
+ throw new Exception($this->lang('file_open') . $path, self::STOP_CONTINUE);
+ }
+ $file_buffer = $this->encodeString($file_buffer, $encoding);
}
- $file_buffer = $this->encodeString($file_buffer, $encoding);
return $file_buffer;
} catch (Exception $exc) {