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) {