[wp-trac] [WordPress Trac] #15448: wp_mail() sets Content-Type header twice for multipart emails
WordPress Trac
noreply at wordpress.org
Thu Jun 26 01:21:46 UTC 2025
#15448: wp_mail() sets Content-Type header twice for multipart emails
-------------------------------------------------+-------------------------
Reporter: rmccue | Owner:
| SergeyBiryukov
Type: feature request | Status: reviewing
Priority: normal | Milestone: Future
| Release
Component: Mail | Version:
Severity: normal | Resolution:
Keywords: has-patch has-unit-tests has-test- | Focuses:
info |
-------------------------------------------------+-------------------------
Comment (by SirLouen):
Replying to [comment:70 rmccue]:
> Note that due to the `if ( ! empty( $headers ) )` guard around it, this
will only be triggered if ''another'' header is added, and one which
doesn't have special handling (from, content-type, cc, bcc, reply-to). The
example code on the linked SE thread should do that via the MIME-Version
header, but maybe it's not triggering properly. (You may also need to test
with a "real" sendmail rather than MailHog.)
My test report was my initial, very superfluous review.
I've tested again, with Mailhog and your example, and I can't still see
the double `multipart`. Maybe some more testing with `sendmail` to some
other clients (other than basic Mailhog catchall client) is required.
> Note that this patch goes beyond just fixing that bug to actually
introduce multipart handling as a core capability.
>
> Aside: We're now so far down the track with this function as a pluggable
one that retrofitting the ecosystem is going to be difficult. At the time
I wrote the ticket, WP's market share was closer to 10% and there were
many fewer drop-in replacements for `wp_mail()`. At this point it might be
worth considering a new `wp_mail_multi()` instead.
I've been doing further code review both in WP, PHPMailer, and the patch
submitted, here are my conclusions:
There are two main functions in PHPMailer that build the
`multipart/alternative`
[https://github.com/PHPMailer/PHPMailer/blob/master/src/PHPMailer.php#L3269-L3286
SetMessageType] and
[https://github.com/PHPMailer/PHPMailer/blob/mater/src/PHPMailer.php#L2834-L2875
GetMailMIME]
The first one decides if the message will be sent as a
`multipart/alternative` if an `AltBody` exists. This was already covered
in tests like [35617] and [54529]. So using `phpmailer_init` to set
`$phpmailer` attributes has not been alien to WordPress in the past 10
years (although, it's true, that this post hast 15 years in total). So
[https://core.trac.wordpress.org/ticket/15448?replyto=70#comment:60 for
some people who have been moaning] because this is not fully supported
have no full reason for this, it's completely possible to achieve this
with a simple workaround. Obviously, a full support is always more
comfortable, but using the workaround is not the end of the world.
== For anyone coming up new, a quick patch recap
What the current patch is simply doing, is the following:
First, is taking advantage of the fact that `wp_mail` already has access
to `$phpmailer`, and setting `AltBody` accordingly for `multipart/alt` or
`attachments` for `mixed` or `related` (which is set in PHPMailer)
Then, it checks if the content of the email is a string or an array. If
it's a string ([https://core.trac.wordpress.org/ticket/15448#comment:69
like the test code in my example above]) or the code provided by @rmccue
in the previous message, it will simply set it as text/plain and will
probably not work (for now, check in the conclusion, maybe it could get
fixed, but I believe this should be done in the PHPMailer side)
But the real interesting thing that this patch does, is adding the
possibility of passing an array to `wp_mail` `$message` which adds a new
level of comfortability because building multipart emails this way is very
convenient.
== The PHPMailer dilemma
If something could be sorted in PHPMailer, is the default `switch`
condition
[https://github.com/PHPMailer/PHPMailer/blob/0ff2d3c8ead628df2493537d9f91db962aa47718/src/PHPMailer.php#L2855-L2859
at PHPMailer] seems to be the fallback reason of why a possible "double
Content-Type" is appearing: The problem is that once set the Header, it
will be sent as-is but Mailhog will add, again, another broken header,
with the `default` condition, which is the multipart, without the
boundary.
This double-content type, could be sorted in PHPMailer, as @dd32 was
suggesting for sure, if instead of using that default condition, it
allowed some logic for headers completely defined by the user (considering
that we are not passing the `AltBody` by default, in the current code).
But it seems that PHPMailer, for some reason as a default behaviour, is
expecting, the AltBody to be present, if we want to send a multipart.
== Final Conclusion
PHPMailer works perfectly for anything multipart. Proof of this is that
the thing is working with `phpmailer_init` action hook. So at this point,
I won't fully blame PHPMailer considering we have a very valid solution in
the table.
I have added a report upstream, to
[https://github.com/PHPMailer/PHPMailer/issues/3173 support the string
multipart]. But the current array implementation adds a extra degree of
correctness. We could obviously, always parse the string content and set
the AltBody accordingly, but I truly believe that this adds an extra layer
of unnecesary complexity to the function. As @rmccue this could be content
for a very specific `wp_mail_multi` function, which I think is an overkill
(specially if we can happen to find a solution upstream).
The thing, here, is considering whether this want to be supported natively
or not with the array alternative. We have the patch, we have the testing
(further testing will be ridden), we have multiple code reviews, and we
have good unit tests and everything is passing. I can't really think of a
reason on why not supporting this at this point for 6.9 with a nice `dev-
note` as a new feature. I will put this on my reviewing list and let's see
if we can onboard it again, for the next Milestone.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/15448#comment:71>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list