wp_mail() │ WP 1.2.1
Send mail, similar to PHP's mail
The default sender name is WordPress
, and email is [email protected]
. You can change them by changing the header:
From: Example User <[email protected] >
Optional filters wp_mail_from and wp_mail_from_name are run on the sender email address and name. The return values are reassembled into a from
address like Example User
If only wp_mail_from
returns a value, then just the email address will be used with no name.
The default content type is text/plain
which does not allow using HTML. You can set the content type of the email either by using the wp_mail_content_type filter, or by including a header like Content-type: text/html
.
The default charset is based on the charset used on the blog (usually utf-8). The charset can be set using the wp_mail_charset filter.
For this function to work webserver should support SMTP
and smtp_port
should be set in php.ini.
Pluggable function — this function can be replaced from a plugin. It means that this function is defined (works) only after all plugins are loaded (included), but before this moment this function has not defined. Therefore, you cannot call this and all functions depended on this function directly from a plugin code. They need to be called on plugins_loaded hook or later, for example on init hook.
Function replacement (override) — in must-use or regular plugin you can create a function with the same name, then it will replace this function.
Return
true|false
. Whether the email contents were sent successfully.
true — does not mean that the user received the email. It only means that the used method was able to process the request without any errors.
Usage
wp_mail( $to, $subject, $message, $headers, $attachments );
$to(string/array) (required)
Array or comma-separated list of email addresses to send message.
$subject(string) (required)
Email subject
$message(string) (required)
Message contents
$headers(string/array)
Additional headers. Can be:
from
— Example: [email protected]
content-type
— text/html , text/plain
cc
— [email protected] — carbon copy — secondary recipients that will receive a copy of the email. They see each other.
bcc
— [email protected] — blind carbon copy — secondary recipients that will receive a copy of the email. They don't see each other.
reply-to
— [email protected]
any other custom headers.
Default: ''
$attachments(string/array)
Files to attach. Specify the full path to the file. If you need to attach several files, specify their names in the array or in a string separated by a newline.
Default: array()
Examples
#1 An example of sending an email
Sending an email from My Name <[email protected] >
with an attachment attach.zip
:
Some servers (hostings) require the FROM field to contain a domain of the website, otherwise, for example, Yandex mail will not receive an email. Tested on Beget hosting.
In this case it is more convenient to change only the part of the header (only the name) through the filter, and not to specify the From:
header:
add_filter( 'wp_mail_from_name', function($from_name){
return 'My name, not WordPress'; // you can specify your mail here: [email protected]
} );
// Remove filters that can change $headers
// remove_all_filters( 'wp_mail_from' );
// remove_all_filters( 'wp_mail_from_name' );
$attachments = [ WP_CONTENT_DIR . '/uploads/attach.zip' ];
$headers = 'From: My Name <[email protected] >' . "\r\n";
wp_mail( '[email protected] ', 'Subject', 'Content', $headers, $attachments );
// [email protected] - is a recepient.
#2 Using an array to specify headers
$to = '[email protected] ';
$subject = 'The subject';
$message = 'The message.';
// Remove filters that can change $headers
// remove_all_filters( 'wp_mail_from' );
// remove_all_filters( 'wp_mail_from_name' );
$headers = array(
'From: Me Myself <[email protected] >',
'content-type: text/html',
'Cc: John Q Codex <[email protected] >',
'Cc: [email protected] ', // you can specify here only a email address
);
wp_mail( $to, $subject, $message, $headers );
#3 Send an email to multiple recipients and set the content type
$multiple_to_recipients = [
'[email protected] ',
'[email protected] '
];
add_filter( 'wp_mail_content_type', 'set_html_content_type' );
wp_mail( $multiple_to_recipients, 'The subject', '<p>The <em>HTML</em> message</p>' );
// Remove content-type filter to avoid futher possible change of the content type.
remove_filter( 'wp_mail_content_type', 'set_html_content_type' );
function set_html_content_type(){
return 'text/html';
}
#4 Change the content type of the email
This example demonstrates how to change the content type of the email to html using wp_mail_content_type
filter
add_filter( 'wp_mail_content_type', function($content_type){
return "text/html";
});
wp_mail( '[email protected] ', 'The subject', '<p>The <em>HTML</em> message</p>');
#5 Specify the email address of FROM header
add_filter( 'wp_mail_from', 'vortal_wp_mail_from' );
function vortal_wp_mail_from( $email_address ){
return '[email protected] ';
}
// Will get the header: WordPress <[email protected] >
#6 Specify the name of FROM header
add_filter( 'wp_mail_from_name', 'vortal_wp_mail_from_name' );
function vortal_wp_mail_from_name( $email_from ){
return 'XXX';
}
// Will get the header: XXX <[email protected] >
#7 Debugging wp_mail: How to intercept errors that occur when sending an email
To intercept errors and view useful messages that may be sent by the SMTP server or WordPress itself, you can use the following hook:
add_action( 'wp_mail_failed', 'mail_failed_error_log' );
function mail_failed_error_log( \WP_Error $wp_error ) {
global $phpmailer;
$err_data = json_encode( $wp_error->get_error_data(), JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE );
$msg = <<<MSG
wp_email() Error:
- PHPMailer ErrorInfo: {$phpmailer->ErrorInfo}
- WP_Error Message: {$wp_error->get_error_message()}
- WP_Error Error Data: {$err_data}
MSG;
error_log( $msg );
}
This hook should be added before the wp_mail() function is used.
Add Your Own Example
Notes
Global. PHPMailer\PHPMailer\PHPMailer. $phpmailer
Changelog
Since 1.2.1
Introduced.
Since 5.5.0
is_email() is used for email validation, instead of PHPMailer's default validator.
wp_mail() wp mail code
WP 6.7.2
function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) {
// Compact the input, apply the filters, and extract them back out.
/**
* Filters the wp_mail() arguments.
*
* @since 2.2.0
*
* @param array $args {
* Array of the `wp_mail()` arguments.
*
* @type string|string[] $to Array or comma-separated list of email addresses to send message.
* @type string $subject Email subject.
* @type string $message Message contents.
* @type string|string[] $headers Additional headers.
* @type string|string[] $attachments Paths to files to attach.
* }
*/
$atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments' ) );
/**
* Filters whether to preempt sending an email.
*
* Returning a non-null value will short-circuit {@see wp_mail()}, returning
* that value instead. A boolean return value should be used to indicate whether
* the email was successfully sent.
*
* @since 5.7.0
*
* @param null|bool $return Short-circuit return value.
* @param array $atts {
* Array of the `wp_mail()` arguments.
*
* @type string|string[] $to Array or comma-separated list of email addresses to send message.
* @type string $subject Email subject.
* @type string $message Message contents.
* @type string|string[] $headers Additional headers.
* @type string|string[] $attachments Paths to files to attach.
* }
*/
$pre_wp_mail = apply_filters( 'pre_wp_mail', null, $atts );
if ( null !== $pre_wp_mail ) {
return $pre_wp_mail;
}
if ( isset( $atts['to'] ) ) {
$to = $atts['to'];
}
if ( ! is_array( $to ) ) {
$to = explode( ',', $to );
}
if ( isset( $atts['subject'] ) ) {
$subject = $atts['subject'];
}
if ( isset( $atts['message'] ) ) {
$message = $atts['message'];
}
if ( isset( $atts['headers'] ) ) {
$headers = $atts['headers'];
}
if ( isset( $atts['attachments'] ) ) {
$attachments = $atts['attachments'];
}
if ( ! is_array( $attachments ) ) {
$attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
}
global $phpmailer;
// (Re)create it, if it's gone missing.
if ( ! ( $phpmailer instanceof PHPMailer\PHPMailer\PHPMailer ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
$phpmailer = new PHPMailer\PHPMailer\PHPMailer( true );
$phpmailer::$validator = static function ( $email ) {
return (bool) is_email( $email );
};
}
// Headers.
$cc = array();
$bcc = array();
$reply_to = array();
if ( empty( $headers ) ) {
$headers = array();
} else {
if ( ! is_array( $headers ) ) {
/*
* Explode the headers out, so this function can take
* both string headers and an array of headers.
*/
$tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) );
} else {
$tempheaders = $headers;
}
$headers = array();
// If it's actually got contents.
if ( ! empty( $tempheaders ) ) {
// Iterate through the raw headers.
foreach ( (array) $tempheaders as $header ) {
if ( ! str_contains( $header, ':' ) ) {
if ( false !== stripos( $header, 'boundary=' ) ) {
$parts = preg_split( '/boundary=/i', trim( $header ) );
$boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) );
}
continue;
}
// Explode them out.
list( $name, $content ) = explode( ':', trim( $header ), 2 );
// Cleanup crew.
$name = trim( $name );
$content = trim( $content );
switch ( strtolower( $name ) ) {
// Mainly for legacy -- process a "From:" header if it's there.
case 'from':
$bracket_pos = strpos( $content, '<' );
if ( false !== $bracket_pos ) {
// Text before the bracketed email is the "From" name.
if ( $bracket_pos > 0 ) {
$from_name = substr( $content, 0, $bracket_pos );
$from_name = str_replace( '"', '', $from_name );
$from_name = trim( $from_name );
}
$from_email = substr( $content, $bracket_pos + 1 );
$from_email = str_replace( '>', '', $from_email );
$from_email = trim( $from_email );
// Avoid setting an empty $from_email.
} elseif ( '' !== trim( $content ) ) {
$from_email = trim( $content );
}
break;
case 'content-type':
if ( str_contains( $content, ';' ) ) {
list( $type, $charset_content ) = explode( ';', $content );
$content_type = trim( $type );
if ( false !== stripos( $charset_content, 'charset=' ) ) {
$charset = trim( str_replace( array( 'charset=', '"' ), '', $charset_content ) );
} elseif ( false !== stripos( $charset_content, 'boundary=' ) ) {
$boundary = trim( str_replace( array( 'BOUNDARY=', 'boundary=', '"' ), '', $charset_content ) );
$charset = '';
}
// Avoid setting an empty $content_type.
} elseif ( '' !== trim( $content ) ) {
$content_type = trim( $content );
}
break;
case 'cc':
$cc = array_merge( (array) $cc, explode( ',', $content ) );
break;
case 'bcc':
$bcc = array_merge( (array) $bcc, explode( ',', $content ) );
break;
case 'reply-to':
$reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
break;
default:
// Add it to our grand headers array.
$headers[ trim( $name ) ] = trim( $content );
break;
}
}
}
}
// Empty out the values that may be set.
$phpmailer->clearAllRecipients();
$phpmailer->clearAttachments();
$phpmailer->clearCustomHeaders();
$phpmailer->clearReplyTos();
$phpmailer->Body = '';
$phpmailer->AltBody = '';
// Set "From" name and email.
// If we don't have a name from the input headers.
if ( ! isset( $from_name ) ) {
$from_name = 'WordPress';
}
/*
* If we don't have an email from the input headers, default to wordpress@$sitename
* Some hosts will block outgoing mail from this address if it doesn't exist,
* but there's no easy alternative. Defaulting to admin_email might appear to be
* another option, but some hosts may refuse to relay mail from an unknown domain.
* See https://core.trac.wordpress.org/ticket/5007.
*/
if ( ! isset( $from_email ) ) {
// Get the site domain and get rid of www.
$sitename = wp_parse_url( network_home_url(), PHP_URL_HOST );
$from_email = 'wordpress@';
if ( null !== $sitename ) {
if ( str_starts_with( $sitename, 'www.' ) ) {
$sitename = substr( $sitename, 4 );
}
$from_email .= $sitename;
}
}
/**
* 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 associated with the "from" email address.
*/
$from_name = apply_filters( 'wp_mail_from_name', $from_name );
try {
$phpmailer->setFrom( $from_email, $from_name, false );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
$mail_error_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
$mail_error_data['phpmailer_exception_code'] = $e->getCode();
/** This filter is documented in wp-includes/pluggable.php */
do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_error_data ) );
return false;
}
// Set mail's subject and body.
$phpmailer->Subject = $subject;
$phpmailer->Body = $message;
// Set destination addresses, using appropriate methods for handling addresses.
$address_headers = compact( 'to', 'cc', 'bcc', 'reply_to' );
foreach ( $address_headers as $address_header => $addresses ) {
if ( empty( $addresses ) ) {
continue;
}
foreach ( (array) $addresses as $address ) {
try {
// Break $recipient into name and address parts if in the format "Foo <[email protected] >".
$recipient_name = '';
if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
if ( count( $matches ) === 3 ) {
$recipient_name = $matches[1];
$address = $matches[2];
}
}
switch ( $address_header ) {
case 'to':
$phpmailer->addAddress( $address, $recipient_name );
break;
case 'cc':
$phpmailer->addCc( $address, $recipient_name );
break;
case 'bcc':
$phpmailer->addBcc( $address, $recipient_name );
break;
case 'reply_to':
$phpmailer->addReplyTo( $address, $recipient_name );
break;
}
} catch ( PHPMailer\PHPMailer\Exception $e ) {
continue;
}
}
}
// Set to use PHP's mail().
$phpmailer->isMail();
// 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';
}
/**
* Filters 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' );
}
/**
* Filters 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 ) {
// Only add custom headers not added automatically by PHPMailer.
if ( ! in_array( $name, array( 'MIME-Version', 'X-Mailer' ), true ) ) {
try {
$phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
continue;
}
}
}
if ( false !== stripos( $content_type, 'multipart' ) && ! empty( $boundary ) ) {
$phpmailer->addCustomHeader( sprintf( 'Content-Type: %s; boundary="%s"', $content_type, $boundary ) );
}
}
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $filename => $attachment ) {
$filename = is_string( $filename ) ? $filename : '';
try {
$phpmailer->addAttachment( $attachment, $filename );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
continue;
}
}
}
/**
* Fires after PHPMailer is initialized.
*
* @since 2.2.0
*
* @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
*/
do_action_ref_array( 'phpmailer_init', array( &$phpmailer ) );
$mail_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
// Send!
try {
$send = $phpmailer->send();
/**
* Fires after PHPMailer has successfully sent an email.
*
* The firing of this action does not necessarily mean that the recipient(s) received the
* email successfully. It only means that the `send` method above was able to
* process the request without any errors.
*
* @since 5.9.0
*
* @param array $mail_data {
* An array containing the email recipient(s), subject, message, headers, and attachments.
*
* @type string[] $to Email addresses to send message.
* @type string $subject Email subject.
* @type string $message Message contents.
* @type string[] $headers Additional headers.
* @type string[] $attachments Paths to files to attach.
* }
*/
do_action( 'wp_mail_succeeded', $mail_data );
return $send;
} catch ( PHPMailer\PHPMailer\Exception $e ) {
$mail_data['phpmailer_exception_code'] = $e->getCode();
/**
* Fires after a PHPMailer\PHPMailer\Exception is caught.
*
* @since 4.4.0
*
* @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array
* containing the mail recipient, subject, message, headers, and attachments.
*/
do_action( 'wp_mail_failed', new WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_data ) );
return false;
}
}
Related Functions