Opened 10 years ago
Last modified 7 weeks ago
#33972 assigned defect (bug)
static use of PHPMailer class results in stale state between calls to wp_mail()
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 6.9 | Priority: | normal |
| Severity: | critical | Version: | 4.3 |
| Component: | Keywords: | has-test-info good-first-bug has-patch has-unit-tests | |
| Focuses: | Cc: |
Description
I've just been chasing down a problem since upgrading to WordPress 4.3 with mails sent via wp_mail() being sent with:
Content-Transfer-Encoding: quoted-printable
Auto switching to quoted-printable was added in the PHPMailer class which was upgraded in 4.3. The issue is that the internal state in $phpmailer is not properly cleared between calls to wp_mail(). There is an attempt to do this via:
Empty out the values that may be set
$phpmailer->ClearAllRecipients();
$phpmailer->ClearAttachments();
$phpmailer->ClearCustomHeaders();
$phpmailer->ClearReplyTos();
But non of these methods reset the value of $this->Encoding. So if I make two calls to wp_mail() and the first one gets switched to quoted-printable the second one will be sent as quoted-printable even if it doesn't need to be.
Would it not be better to just create a new instance of PHPMailer for each invocation of wp_mail() and let the constructor take care of it?
Attachments (2)
Change History (35)
This ticket was mentioned in Slack in #core by stephenharris. View the logs.
10 years ago
#6
@
4 months ago
- Keywords has-test-info added
Reproduction Report
Description
✅ This report validates that the issue can be reproduced still 10 years after.
Environment
- WordPress: 6.9-alpha-60093-src
- PHP: 8.2.29
- Server: nginx/1.29.0
- Database: mysqli (Server: 8.4.5 / Client: mysqlnd 8.2.29)
- Browser: Chrome 138.0.0.0
- OS: Windows 10/11
- Theme: Twenty Twenty-Five 1.2
- MU Plugins: None activated
- Plugins:
- Micro Email Testing 1.0.0
Testing Instructions
- I've used the script I provide in the bottom
- 🐞 The second email is sent with
Content-Transfer-Encoding: quoted-printableheader
Actual Results
- ✅ Error condition occurs (reproduced).
Additional Notes
- I've noted that the unit test provided does the other way around. First test fails with
8bit
1) Tests_Pluggable_wpMail::test_wp_mail_encoding_does_not_bleed Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'quoted-printable' +'8bit'
- Probably this report has not progressed in all this time because a 1000 char word is very unlikely to happen.
Supplemental Artifacts
Testing script:
$to = 'test@example.com';
$subject = "Subject";
$body = str_repeat( 'A', 1000 );
if ( wp_mail( $to, $subject, $body) ) {
echo 'Message 1 successfully sent.';
} else {
echo '>Message 1 sending failed.';
}
$to = 'test@example.com';
$subject = "Subject B";
$body = "Hello";
if ( wp_mail( $to, $subject, $body) ) {
echo 'Message 2 successfully sent.';
} else {
echo 'Message 2 sending failed.';
}
#8
@
4 months ago
- Severity changed from normal to critical
Now I've realized that after reading this #59670 duplicate, it's obviously affecting all Content-Transfer-Encoding and as suggested in this report, it might lead into serious problem like SPAM tagging. Increasing severity to critical.
This ticket was mentioned in PR #9417 on WordPress/wordpress-develop by @rishabhwp.
3 months ago
#10
- Keywords has-patch added; needs-patch removed
@SirLouen commented on PR #9417:
3 months ago
#11
@R1shabh-Gupta thanks for the patch
I will start reviewing it. I just one to comment one thing that would be great for the future
When you send a PR, also send a message in Trac informing about the patch. Problem with PRs is that it doesn't send notifications. Every time you sent a patch for any ticket I have in my follow list, I don't see it until I happen to be reviewing the old tickets by chance (and maybe there are more I have not already seen)).
Just write something: "Patch sent to GH"
Also, its easier to miss props with GH patches than Trac messages (specially when there are many PRs in a single ticket)
#12
@
3 months ago
- Keywords changes-requested added
@rishabhwp can you add the unit tests in 33972.test.diff to your PR?
(It needs a minimal tweaking to apply, adding the visibility and set it in the bottom of wpMail.php).
Test is not passing with your patch.
@rishabhwp commented on PR #9417:
3 months ago
#13
I’ve added a test for this and noticed a few points worth highlighting:
- PHPMailer encoding downgrade As mentioned by @SirLouen, the original test assertion:
$this->assertEquals( '8bit', $mailer->Encoding );
fails because PHPMailer automatically downgrades the encoding from
8bitto7bitwhen the message body contains no 8-bit characters (seePHPMailer.php, line 2875). In our case, we were sending'Short email'as the body, which is pure ASCII, so the encoding was downgraded to7bit, leading to the test failure.
To address this, I’ve added an 8-bit character to the message body —'Short email –'— ensuring PHPMailer keeps the encoding as8bitand allowing the test to pass.
- MockPHPMailer behavior difference
In
mock-mailer.php, the `preSend()` method explicitly sets:
$this->Encoding = '8bit';
This step does not occur in the real PHPMailer implementation. As a result, the mock partially hides the encoding downgrade behavior, though the issue still appears in the actual email headers.
Let me know if anything else should be addressed.
#16
@
2 months ago
- Keywords changes-requested added; dev-feedback removed
Hey @rishabhwp
Thanks for the second edit, but we need some extra work.
The first problem I clearly see is this line
https://github.com/WordPress/wordpress-develop/blob/3ea6be9fa8a4973c2b01c094f062de4fd33c0aba/tests/phpunit/includes/mock-mailer.php#L23
This is provoking that the test don't match my manual testing. It didn't make any sense that 8-bit was being enforced even when I did not add any default value to the Encoding as you did in pluggable.php. So I had to dig a little further into the tests code to find that line. Removing that line is necessary, as its not providing absolutely any value and its breaking the test.
With this one sorted out, I believe that just by clearing up Encoding (no need to default to anything, like 8Bit as you did), just the same as all the other cleared values, will be enough to sort this problem. With this done the Unit Test should result in an empty value for the Content-Transfer-Encoding (like in my manual testing) as we are not explicitly setting up anything in particular (you can keep the message short, don't need to add 8 bit characters or anything like that)
If anyone wants to explicitely set-up the charset, they must set it with wp_mail_charset filter hook.
To prove that we don't really need any stunts or 8-bit defaulting, the best way is to test everything directly with a clean PHPMailer object by itself to observe the default returned values.
The first test, should obviously a very long line, to display the quoted-printable in an email
Example with long test:
<?php
require_once 'src/PHPMailer.php';
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer();
$mail->setFrom('test@test.com', 'Test');
$mail->addAddress('test@test.com', 'Test');
$mail->Subject = 'Test';
$mail->Body = str_repeat('A', 1000);;
$mail->send();
echo 'Email sent';
Result:
Return-Path: <test@test.com>
Received: from sendmailpit:1025 (phpmailer_appserver_1.phpmailer_default. [172.23.0.8])
by 9ac33a333c0b (Mailpit) with SMTP
for <test@test.com>; Tue, 19 Aug 2025 22:53:10 +0000 (UTC)
To: Test <test@test.com>
Subject: Test
Date: Tue, 19 Aug 2025 22:53:10 +0000
From: Test <test@test.com>
Message-ID: <mS0SO0Ld0KfwS6k63Ier90w7hZC0TzptyhgMX4vETY@phpmailer.lndo.site>
X-Mailer: PHPMailer 6.10.0 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAA
Example with very short text:
<?php
require_once 'src/PHPMailer.php';
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer();
$mail->setFrom('test@test.com', 'Test');
$mail->addAddress('test@test.com', 'Test');
$mail->Subject = 'Test';
$mail->Body = "Short Text";
$mail->send();
echo 'Email sent';
Result
Return-Path: <test@test.com>
Received: from sendmailpit:1025 (phpmailer_appserver_1.phpmailer_default. [172.23.0.8])
by 9ac33a333c0b (Mailpit) with SMTP
for <test@test.com>; Tue, 19 Aug 2025 22:54:40 +0000 (UTC)
To: Test <test@test.com>
Subject: Test
Date: Tue, 19 Aug 2025 22:54:40 +0000
From: Test <test@test.com>
Message-ID: <oLVepBtfeRiHDrB7rIQDxIu6MmaamAcffeafldWLY@phpmailer.lndo.site>
X-Mailer: PHPMailer 6.10.0 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Short Text
As we can see, Content-Transfer-Encoding by default, is not a thing, so with a very simple email, we should be expecting such (emptiness for $mailer->Encoding)
I think with this you have everything to get the patch done.
#17
follow-up:
↓ 20
@
2 months ago
- Keywords changes-requested removed
Hi @SirLouen,
I've updated the PR per your feedback in comment #16:
- Removed the line in
mock-mailer.phpthat was forcing$this->Encoding = '8bit' - Changed
wp_mail()to clear the encoding property ($phpmailer->Encoding = '') instead of defaulting to 8bit - Updated the unit test to expect empty encoding for short messages, matching PHPMailer's natural behavior
The test now passes. Thanks for the detailed guidance!
#20
in reply to:
↑ 17
@
2 months ago
- Keywords needs-testing added
- Milestone set to Future Release
The test now passes. Thanks for the detailed guidance!
Looks good to me now. Time for some manual testing.
This ticket was mentioned in Slack in #core-test by sirlouen. View the logs.
7 weeks ago
#22
@
7 weeks ago
Failed Test Report
When testing email sending in WordPress with small email bodies (length < 1000 characters) using MailPit/MailHog, email sending fails with the error:
PHPMailer Error: Unknown encoding:
This occurs after applying the patch from [PR #9417](https://github.com/WordPress/wordpress-develop/pull/9417).
Environment
- WordPress: 6.9-alpha-60093-src
- PHP: 8.2.29
- Server: nginx/1.29.1
- Database: MySQL 8.4.6 (mysqlnd 8.2.29)
- Browser: Chrome 139.0.0.0
- OS: Windows 10/11
- Theme: Twenty Twenty-Five 1.3
- Plugins Active:
- Email Testing 1.0.0 (custom test plugin)
- Test Reports 1.2.0
Test Plugin Used
Email Testing Plugin (custom):
add_action( 'phpmailer_init', 'configure_mailhog_for_testing' );
function configure_mailhog_for_testing( $phpmailer ) {
$phpmailer->isSMTP();
$phpmailer->SMTPAuth = false;
$phpmailer->SMTPSecure = '';
$phpmailer->SMTPAutoTLS = false;
$phpmailer->Host = 'mailpit';
$phpmailer->Port = '1025';
}
add_action( 'wp_mail_failed', 'log_mailer_errors' );
function log_mailer_errors( $wp_error ) {
if ( is_wp_error( $wp_error ) ) {
error_log( 'PHPMailer Error: ' . $wp_error->get_error_message() );
}
}
add_filter( 'wp_mail_from', 'my_custom_mail_from' );
function my_custom_mail_from( $email ) {
return 'from@example.com';
}
add_action( 'admin_menu', 'mail_test_menu' );
function mail_test_menu() {
add_management_page(
'Mail Test',
'Mail Test',
'manage_options',
'mail-test',
'mail_test_page'
);
}
function mail_test_page() {
?>
<div class="wrap">
<h1>MailHog Email Test</h1>
<?php
if ( isset( $_POST['send_test'] ) && check_admin_referer( 'mail_test_email' ) ) {
$to = 'test@example.com';
$subject = "Test Subject";
$body = 'Small Body'; // it doesn't work.
// $body = str_repeat( 'A', 1000 ); // it works only, body length greater or equal to 1000 works only.
for ( $i = 1; $i <= 1005; $i++ ) {
$body = str_repeat( 'A', $i );
$sent = wp_mail( $to, $subject, $body );
if ( $sent ) {
?>
<div class="notice notice-success">
<p>Test email sent successfully! Length: <?php echo strlen( $body ); ?></p>
</div>
<?php
} else {
?>
<div class="notice notice-error">
<p>Error sending email, Body : Length: <?php echo strlen( $body ); ?></p>
</div>
<?php
}
}
}
?>
<form method="post">
<?php wp_nonce_field( 'mail_test_email' ); ?>
<p class="submit">
<input type="submit" name="send_test" class="button button-primary" value="Send Test Email">
</p>
</form>
</div>
<?php
}
✅ Expected Result
- All test emails should be sent successfully, regardless of body length.
❌ Actual Result
- Emails with body length < 1000 characters fail to send.
- Error logged:
PHPMailer Error: Unknown encoding:
- Emails with body length ≥ 1000 characters succeed and are received in MailPit.
Steps to Reproduce
- Install and activate the provided Email Testing plugin.
- Navigate to Tools → Mail Test in wp-admin.
- Click Send Test Email.
- Observe results:
- Messages with body length
< 1000fail. - Messages with body length
≥ 1000succeed.
- Messages with body length
Notes
- Issue persists after applying [PR #9417](https://github.com/WordPress/wordpress-develop/pull/9417).
- Suggests underlying issue with body encoding/handling in PHPMailer integration when body length is below threshold.
This ticket was mentioned in Slack in #core-test by sajjad67. View the logs.
7 weeks ago
#25
@
7 weeks ago
- Keywords changes-requested removed
Thanks @sajjad67 for the test report.
I’ve updated my PR to address the issue mentioned in the test report.
#27
@
7 weeks ago
Success Test Report
After applying the latest patch from [PR #9417](https://github.com/WordPress/wordpress-develop/pull/9417) it now works perfectly. Sending any length of body text goes into inbox.
Environment
- WordPress: 6.9-alpha-60093-src
- PHP: 8.2.29
- Server: nginx/1.29.1
- Database: MySQL 8.4.6 (mysqlnd 8.2.29)
- Browser: Chrome 139.0.0.0
- OS: Windows 10/11
- Theme: Twenty Twenty-Five 1.3
- Plugins Active:
- Email Testing 1.0.0 (custom test plugin)
- Test Reports 1.2.0
✅ Expected Result
- All test emails should be sent successfully, regardless of body length.
✅ Actual Result
- Emails with any body length works now.
Return-Path: <from@example.com>
Received: from localhost (wordpress-develop-php-1.wordpress-develop_wpdevnet. [172.18.0.3])
by eb84c58a3dd4 (Mailpit) with SMTP
for <test@example.com>; Tue, 9 Sep 2025 16:30:57 +0000 (UTC)
Date: Tue, 9 Sep 2025 16:30:57 +0000
To: test@example.com
From: WordPress <from@example.com>
Subject: Test Subject
Message-ID: <ZBdIPJ9HDcyMmsqahX8mHRUc8iLmI7xrEAJwkvWLh8@localhost>
X-Mailer: PHPMailer 6.10.0 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
A
Return-Path: <from@example.com>
Received: from localhost (wordpress-develop-php-1.wordpress-develop_wpdevnet. [172.18.0.3])
by eb84c58a3dd4 (Mailpit) with SMTP
for <test@example.com>; Tue, 9 Sep 2025 16:31:48 +0000 (UTC)
Date: Tue, 9 Sep 2025 16:31:48 +0000
To: test@example.com
From: WordPress <from@example.com>
Subject: Test Subject
Message-ID: <aSKIP0ZJHQ9cGxaGd3XqWlCJ5iWBNl0LiaY84jekTo@localhost>
X-Mailer: PHPMailer 6.10.0 (https://github.com/PHPMailer/PHPMailer)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Steps to Reproduce
- Install and activate the provided Email Testing plugin.
- Navigate to Tools → Mail Test in wp-admin.
- Click Send Test Email.
- Observe results:
- Messages with any body length succeed.
Notes
- Issue resolved after applying [PR #9417](https://github.com/WordPress/wordpress-develop/pull/9417).
This ticket was mentioned in Slack in #core-test by sajjad67. View the logs.
7 weeks ago
This ticket was mentioned in Slack in #core-test by sirlouen. View the logs.
7 weeks ago
This ticket was mentioned in Slack in #core-test by nikunj8866. View the logs.
7 weeks ago
#31
@
7 weeks ago
- Keywords has-unit-tests has-dev-note added
Test Report
Description
This report validates that the fix for #33972 works as expected on the current trunk.
Patch tested: trunk @ becfe55789
Environment
- WordPress: 6.8.2
- PHP: 8.2.23
- Server: nginx/1.26.1
- Database: mysqli (Server: 8.0.35 / Client: mysqlnd 8.2.23)
- Browser: Chrome 140.0.0.0
- OS: Windows 10/11
- Theme: Twenty Twenty-One 2.5
- MU Plugins: None activated
- Plugins:
- Test Reports 1.2.0
Steps to Reproduce (Before)
- On trunk, executed unit tests for wp_mail:
npm run test:php -- tests/phpunit/tests/pluggable/wpMail.php - Optional manual: Sent two consecutive
wp_mail()calls (first forces base64, second default) and inspected PHPMailerEncodingviaphpmailer_init.
Actual Results (After)
- ✅ Unit tests:
OK (27 tests, 66 assertions) - ✅ No Encoding bleed between consecutive
wp_mail()calls; headers/content behave as expected.
Additional Notes
- Could not reproduce the issue on the current trunk; behavior appears correct.
- ✅ Tested on trunk @ becfe55789
Unit tests: OK (27 tests, 66 assertions)
Observation: Encoding bleed not reproducible on trunk.
#32
follow-up:
↓ 33
@
7 weeks ago
- Keywords needs-testing has-dev-note removed
- Milestone changed from Future Release to 6.9
Thanks @dilip2615 and @sajjad67 for manual testing, time to move this forward.



I've written a test which (almost) replicates this. The test unexpectedly passes because of changes made to the mock PHPMailer class explicitly designed to suppress this behaviour (see #28909).
In my view we should be creating a new PHPMailer instance for each email, not trying to reset the instance for each e-mail (evidently PHPMailer doesn't allow us to do that).