摘要
由于WP Core中存在一个错误,具有讽刺意味的是,您的域被Hotmail(和其他Microsoft电子邮件)阻止了,因此使用wp_mail()发送多部分电子邮件(html /文本)(以减少电子邮件最终进入垃圾邮件文件夹的可能性)。
这是一个复杂的问题,我将详细分解以帮助某人找到可行的解决方案,并最终在核心中实现。
这将是一个有益的阅读。让我们开始...
错误
避免使时事通讯电子邮件最终进入垃圾邮件文件夹的最常见建议是发送多部分邮件。
多部分(MIME)是指在一封电子邮件中同时发送电子邮件的HTML和TEXT部分。客户端收到多部分消息时,如果可以呈现HTML,则接受HTML版本,否则将呈现纯文本版本。
事实证明这是可行的。当发送到gmail时,我们所有的电子邮件都将放入垃圾邮件文件夹,直到当它们到达主收件箱时我们将邮件更改为多部分。好东西。
现在,当通过wp_mail()发送多部分消息时,它将两次输出内容类型(multipart / *),一次是带有边界(如果是自定义设置的),一次是没有边界的。此行为导致电子邮件被显示为原始消息,而不是包括某些所有 Microsoft(Hotmail,Outlook等)在内的某些电子邮件的一部分。
Microsoft将将此邮件标记为垃圾邮件,而接收到的少量邮件将由收件人手动标记。不幸的是,Microsoft电子邮件地址被广泛使用。我们有40%的订户使用它。
微软通过我们最近进行的电子邮件交换确认了这一点。
标记消息将导致域被完全阻止。这意味着该邮件将不会发送到垃圾邮件文件夹,甚至根本不会发送给收件人。
到目前为止,我们已经屏蔽了3次主域名。
因为这是WP核心中的错误,所以发送多部分消息的每个域都被阻止。问题是大多数网站管理员都不知道为什么。我在进行研究时发现这一点,并看到其他用户在论坛等上进行讨论。这需要深入研究原始代码,并对这些电子邮件的工作方式有充分的了解,我们将继续进行下一步...
让我们将其分解为代码
创建一个hotmail / outlook帐户。然后,运行以下代码:
// Set $to to an hotmail.com or outlook.com email
$to = "YourEmail@hotmail.com";
$subject = 'wp_mail testing multipart';
$message = '------=_Part_18243133_1346573420.1408991447668
Content-Type: text/plain; charset=UTF-8
Hello world! This is plain text...
------=_Part_18243133_1346573420.1408991447668
Content-Type: text/html; charset=UTF-8
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<p>Hello World! This is HTML...</p>
</body>
</html>
------=_Part_18243133_1346573420.1408991447668--';
$headers = "MIME-Version: 1.0\r\n";
$headers .= "From: Foo <foo@bar.com>\r\n";
$headers .= 'Content-Type: multipart/alternative;boundary="----=_Part_18243133_1346573420.1408991447668"';
// send email
wp_mail( $to, $subject, $message, $headers );
如果要更改默认的内容类型,请使用:
add_filter( 'wp_mail_content_type', 'set_content_type' );
function set_content_type( $content_type ) {
return 'multipart/alternative';
}
这将发送多部分消息。
因此,如果检查消息的完整原始资源,您会注意到内容类型被添加了两次,一次没有边界:
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="====f230673f9d7c359a81ffebccb88e5d61=="
MIME-Version: 1.0
Content-Type: multipart/alternative; charset=
这就是问题。
问题的根源在于pluggable.php
-如果我们在这里查看:
// Set Content-Type and charset
// If we don't have a content-type from the input headers
if ( !isset( $content_type ) )
$content_type = 'text/plain';
/**
* Filter the wp_mail() content type.
*
* @since 2.3.0
*
* @param string $content_type Default wp_mail() content type.
*/
$content_type = apply_filters( 'wp_mail_content_type', $content_type );
$phpmailer->ContentType = $content_type;
// Set whether it's plaintext, depending on $content_type
if ( 'text/html' == $content_type )
$phpmailer->IsHTML( true );
// If we don't have a charset from the input headers
if ( !isset( $charset ) )
$charset = get_bloginfo( 'charset' );
// Set the content-type and charset
/**
* Filter the default wp_mail() charset.
*
* @since 2.3.0
*
* @param string $charset Default email charset.
*/
$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
// Set custom headers
if ( !empty( $headers ) ) {
foreach( (array) $headers as $name => $content ) {
$phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
}
if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) )
$phpmailer->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $content_type, $boundary ) );
}
if ( !empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
try {
$phpmailer->AddAttachment($attachment);
} catch ( phpmailerException $e ) {
continue;
}
}
}
潜在解决方案
所以您想知道,为什么不在Trac上报告此情况?我已经有。令我惊讶的是,五年前创建了另一张票证,概述了相同的问题。
面对现实,已经过去了五年。在互联网时代,大约是30岁。这个问题显然已经被放弃了,并且基本上永远不会得到解决(...除非我们在这里解决,否则)。
我在这里找到了一个提供解决方案的好线程,但是尽管他的解决方案有效,但它破坏了没有自定义$headers
设置的电子邮件。
那就是我们每次崩溃的地方。多部分版本可以正常工作,而普通未设置的$headers
消息则行不通,或者不推荐使用。
我们想到的解决方案是:
if ( false !== stripos( $content_type, 'multipart' ) && ! empty($boundary) ) {
$phpmailer->ContentType = $content_type . "; boundary=" . $boundary;
}
else {
$content_type = apply_filters( 'wp_mail_content_type', $content_type );
$phpmailer->ContentType = $content_type;
// Set whether it's plaintext, depending on $content_type
if ( 'text/html' == $content_type )
$phpmailer->IsHTML( true );
// If we don't have a charset from the input headers
if ( !isset( $charset ) )
$charset = get_bloginfo( 'charset' );
}
// Set the content-type and charset
/**
* Filter the default wp_mail() charset.
*
* @since 2.3.0
*
* @param string $charset Default email charset.
*/
$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
// Set custom headers
if ( !empty( $headers ) ) {
foreach( (array) $headers as $name => $content ) {
$phpmailer->AddCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
}
}
是的,我知道,编辑核心文件是忌讳的,请坐下来……这是一个绝望的修复,并且是为内核提供修复的较差尝试。
我们的修复程序存在的问题是,默认电子邮件(如新注册,评论,密码重置等)将作为空白邮件发送。因此,我们有一个有效的wp_mail()脚本,该脚本将发送多部分消息,但没有其他内容。
该怎么办
这里的目的是找到一种使用核心wp_mail()函数(而不是自定义sendmail函数)发送常规(纯文本)消息和多部分消息的方法。
尝试解决此问题时,您将遇到的主要问题是花在发送虚假消息,检查是否收到虚假消息以及基本上打开一盒阿司匹林并在Microsoft受到诅咒的时间,因为您已经习惯了IE问题而不幸的是,这里的gremlin是WordPress。
更新资料
@bonger发布的解决方案允许$message
是一个包含内容类型键控替代项的数组。我已经确认它可以在所有情况下使用。
我们将允许悬而未决的问题悬而未决,直到赏金用完为止,以提高人们对该问题的认识,也许将其提高到可以解决核心问题的程度。随时发布一个替代解决方案,其中$message
可以是一个字符串。
wp_mail()
功能是可插拔的,不是将替代品定义为必须使用的插件(在wp-content / mu-plugins中)对您来说不是一个好的解决方案(以及其他所有人,都无法通过核心修复)?在哪种情况下,设置$phpmailer->ContentType = $content_type;
(而不是选择)后不能将多部分/边界检查移至?