各位观众老爷,今天老夫就来给大家扒一扒 WordPress 里那个神神秘秘的 wp_mail()
函数的底裤,看看它到底是怎么把 PHP 原生的 mail()
函数给包装起来,又是怎么处理那些让人头大的邮件头的。保证让大家听得懂,看得明白,还能笑出声。
开场白:PHP 原生 mail()
的尴尬
话说 PHP 自带的 mail()
函数,简直就是个“直男癌晚期患者”。它简单粗暴,功能单一,用起来让人抓狂。
举个例子,你想发一封 HTML 格式的邮件,还得自己吭哧吭哧地拼凑邮件头,一不小心就出错,导致邮件乱码、无法显示等等问题。而且,安全性也是个大问题,很容易被垃圾邮件发送者利用。
所以,WordPress 为了方便开发者,就封装了一个 wp_mail()
函数,让大家可以更轻松、更安全地发送邮件。
wp_mail()
函数的庐山真面目
wp_mail()
函数的代码藏在 wp-includes/pluggable.php
文件里。咱们先来看看它的基本结构:
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
// 1. 参数处理与过滤
// 2. 构建邮件头
// 3. 处理附件
// 4. 发送邮件
// 5. 返回结果
}
可以看到,wp_mail()
函数接收五个参数:
参数 | 类型 | 描述 |
---|---|---|
$to |
string/array | 收件人邮箱地址,可以是字符串或数组。 |
$subject |
string | 邮件主题。 |
$message |
string | 邮件正文内容。 |
$headers |
string/array | 邮件头信息,可以是字符串或数组。 |
$attachments |
array | 附件列表,是一个包含附件文件路径的数组。 |
接下来,咱们就一步一步地深入分析 wp_mail()
函数的源码。
第一步:参数处理与过滤
wp_mail()
函数的第一步就是对传入的参数进行处理和过滤,确保数据的有效性和安全性。
-
处理收件人地址 (
$to
):wp_mail()
函数会检查$to
参数的类型,如果它是数组,则将其转换为逗号分隔的字符串。这样做是为了兼容不同的使用场景。if ( is_array( $to ) ) { $to = implode( ',', $to ); }
同时,
wp_mail()
还会使用wp_strip_all_tags()
函数过滤收件人地址,防止 XSS 攻击。 -
应用过滤器 (
wp_mail
):wp_mail()
函数会应用wp_mail
过滤器,允许开发者在邮件发送之前修改所有参数。$atts = array( 'to' => $to, 'subject' => $subject, 'message' => $message, 'headers' => $headers, 'attachments' => $attachments ); /** * Filters the array of email arguments used when sending mail. * * @since 2.2.0 * * @param array $atts { * Array of email arguments. * * @type string|array $to Array or comma-separated string of email addresses to send message. * @type string $subject Email subject. * @type string $message Email message. * @type string|array $headers Optional. Additional headers. Use an empty array to set none. * @type string|array $attachments Optional. Files to attach. Use an empty array to set none. * } */ $atts = apply_filters( 'wp_mail', $atts ); $to = $atts['to']; $subject = $atts['subject']; $message = $atts['message']; $headers = $atts['headers']; $attachments = $atts['attachments'];
这个过滤器非常有用,开发者可以通过它来实现自定义的邮件处理逻辑,比如添加额外的邮件头、修改邮件内容等等。
第二步:构建邮件头
邮件头是邮件的重要组成部分,它包含了邮件的各种元数据,比如发件人、收件人、邮件类型等等。wp_mail()
函数会根据传入的 $headers
参数,构建最终的邮件头。
-
标准化邮件头:
wp_mail()
函数首先会将$headers
参数标准化为一个数组,方便后续处理。如果$headers
是字符串,则将其拆分成数组。if ( ! is_array( $headers ) ) { $headers = explode( "n", str_replace( "rn", "n", $headers ) ); }
-
设置默认邮件头:
wp_mail()
函数会设置一些默认的邮件头,比如Content-Type
和From
。Content-Type
决定了邮件的类型,通常设置为text/plain
或text/html
。From
决定了发件人的邮箱地址。// Set Content-Type if not already set. $content_type = 'Content-Type: text/plain; charset=' . get_bloginfo( 'charset' ); $found = false; foreach ( (array) $headers as $header ) { if ( stripos( $header, 'Content-Type:' ) !== false ) { $found = true; break; } } if ( ! $found ) { $headers[] = $content_type; } // Set From: header // Get the site domain and get rid of www. $sitename = strtolower( $_SERVER['SERVER_NAME'] ); if ( substr( $sitename, 0, 4 ) == 'www.' ) { $sitename = substr( $sitename, 4 ); } $from_email = 'wordpress@' . $sitename; $switched_locale = switch_to_locale( get_locale(), SWT_FILTER ); $from_name = get_option( 'blogname' ); if ( '[email protected]' === $from_email ) { $from_email = sanitize_email( get_option( 'admin_email' ) ); } $from = "From: "" . wp_specialchars_decode( $from_name, ENT_QUOTES ) . "" <$from_email>"; if ( $switched_locale ) { restore_previous_locale(); } // Add it to the headers if it isn't already there. $found = false; foreach ( (array) $headers as $header ) { if ( strpos( $header, 'From:' ) !== false ) { $found = true; break; } } if ( ! $found ) { $headers[] = $from; }
这里需要注意的是,
wp_mail()
会尝试获取站点的域名和管理员邮箱地址,作为默认的发件人信息。如果管理员邮箱地址未设置,则使用[email protected]
作为默认值。 -
应用过滤器 (
wp_mail_from
,wp_mail_from_name
,wp_mail_content_type
,wp_mail_charset
):wp_mail()
函数还提供了一系列的过滤器,允许开发者自定义邮件头中的各个字段。wp_mail_from
: 用于修改发件人邮箱地址。wp_mail_from_name
: 用于修改发件人姓名。wp_mail_content_type
: 用于修改邮件类型。wp_mail_charset
: 用于修改邮件字符集。
/** * Filters the email address to send from. * * @since 2.2.0 * * @param string $from_email Email address to send from. */ $from_email = apply_filters( 'wp_mail_from', $from_email ); /** * Filters the name to associate with the "from" email address. * * @since 2.3.0 * * @param string $from_name Name to associate with the "from" email address. */ $from_name = apply_filters( 'wp_mail_from_name', $from_name ); $from = "From: "" . wp_specialchars_decode( $from_name, ENT_QUOTES ) . "" <$from_email>"; /** * Filters the content type of the email. * * @since 2.3.0 * * @param string $content_type Content type of the email. */ $content_type = apply_filters( 'wp_mail_content_type', $content_type ); /** * Filters the charset of the email. * * @since 2.3.0 * * @param string $charset Email charset. */ $charset = apply_filters( 'wp_mail_charset', get_bloginfo( 'charset' ) );
这些过滤器为开发者提供了极大的灵活性,可以根据实际需求定制邮件的各个方面。
-
构建最终邮件头:
最后,
wp_mail()
函数将所有邮件头信息拼接成一个字符串,作为mail()
函数的参数。$headers = implode( "n", $headers );
第三步:处理附件
如果 $attachments
参数不为空,wp_mail()
函数会处理附件,并将它们添加到邮件中。
-
检查附件文件是否存在:
wp_mail()
函数会遍历$attachments
数组,检查每个附件文件是否存在。如果文件不存在,则跳过该附件。if ( ! empty( $attachments ) ) { foreach ( $attachments as $attachment ) { if ( ! file_exists( $attachment ) ) { continue; } } }
-
使用 PHPMailer 处理附件:
wp_mail()
函数使用 PHPMailer 类来处理附件。PHPMailer 是一个强大的邮件发送类库,可以方便地添加附件、设置邮件格式等等。// (Re)create it, if it's gone missing. if ( ! ( $phpmailer instanceof PHPMailer ) ) { require_once ABSPATH . WPINC . '/class-phpmailer.php'; require_once ABSPATH . WPINC . '/class-smtp.php'; $phpmailer = new PHPMailer( true ); } // Clear previous email data. $phpmailer->ClearAddresses(); $phpmailer->ClearAttachments(); $phpmailer->ClearCustomHeaders(); $phpmailer->ClearReplyTos(); // Set the From address. $phpmailer->From = $from_email; $phpmailer->FromName = $from_name; if ( version_compare( phpversion(), '5.5', '>=' ) ) { $phpmailer->CharSet = $charset; } // Set destination address(es). foreach ( explode( ',', $to ) as $recipient ) { try { $phpmailer->addAddress( trim( $recipient ) ); } catch ( phpmailerException $e ) { continue; } } // Set the subject. $phpmailer->Subject = $subject; // Set the message body. $phpmailer->Body = $message; // Set Content-Type and charset. foreach ( (array) $headers as $header ) { if ( stripos( $header, 'Content-Type:' ) !== false ) { if ( stripos( $header, 'text/html' ) !== false ) { $phpmailer->isHTML( true ); } } else { // Add custom headers. $name = substr( $header, 0, strpos( $header, ':' ) ); $value = trim( substr( $header, strpos( $header, ':' ) + 1 ) ); $phpmailer->addCustomHeader( $name, $value ); } } if ( ! $phpmailer->isHTML() ) { $phpmailer->Body = wp_strip_all_tags( $phpmailer->Body ); } // Add attachments. foreach ( $attachments as $attachment ) { try { $phpmailer->addAttachment( $attachment ); } catch ( phpmailerException $e ) { continue; } }
wp_mail()
函数会先创建一个 PHPMailer 实例,然后设置发件人、收件人、主题、正文等信息。最后,遍历$attachments
数组,将每个附件添加到 PHPMailer 实例中。
第四步:发送邮件
wp_mail()
函数最终会调用 PHPMailer 的 send()
方法来发送邮件。
try {
$sent = $phpmailer->send();
} catch ( phpmailerException $e ) {
$sent = false;
}
如果发送成功,send()
方法会返回 true
,否则返回 false
。
第五步:返回结果
wp_mail()
函数会将邮件发送的结果返回。
return $sent;
总结:wp_mail()
的价值
wp_mail()
函数是对 PHP 原生 mail()
函数的封装和增强,它提供了以下价值:
- 易用性:
wp_mail()
函数提供了更简洁的 API,方便开发者发送邮件。 - 安全性:
wp_mail()
函数对输入参数进行过滤,防止 XSS 攻击。 - 灵活性:
wp_mail()
函数提供了丰富的过滤器,允许开发者自定义邮件的各个方面。 - 可扩展性:
wp_mail()
函数使用 PHPMailer 类来处理附件,支持各种邮件格式和附件类型。
wp_mail()
函数源码分析流程图
graph TD
A[开始] --> B(参数处理与过滤);
B --> C(构建邮件头);
C --> D{是否有附件?};
D -- 是 --> E(处理附件);
E --> F(发送邮件);
D -- 否 --> F;
F --> G(返回结果);
G --> H[结束];
彩蛋:如何修改 wp_mail()
的默认发件人地址
有时候,我们希望修改 wp_mail()
函数的默认发件人地址,比如将其设置为站点的名称或管理员邮箱地址。
可以通过以下代码实现:
add_filter( 'wp_mail_from', 'my_custom_mail_from' );
function my_custom_mail_from( $original_email_address ) {
return '[email protected]'; // 修改为你想要的发件人邮箱地址
}
add_filter( 'wp_mail_from_name', 'my_custom_mail_from_name' );
function my_custom_mail_from_name( $original_email_from ) {
return 'Your Website Name'; // 修改为你想要的发件人姓名
}
将以上代码添加到主题的 functions.php
文件中即可。
好了,今天的讲座就到这里。希望大家通过这次学习,对 wp_mail()
函数有了更深入的了解。记住,掌握了 wp_mail()
函数,你就掌握了 WordPress 邮件发送的钥匙!如果还有什么疑问,欢迎在评论区留言,老夫会尽力解答。 咱们下期再见!