$phpmailer->CharSet = apply_filters( 'wp_mail_charset', $charset );
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 ) ) {
$phpmailer->addCustomHeader( sprintf( '%1$s: %2$s', $name, $content ) );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
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 : '';
$phpmailer->addAttachment( $attachment, $filename );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
* Fires after PHPMailer is initialized.
* @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 = $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.
* @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 );
} catch ( PHPMailer\PHPMailer\Exception $e ) {
$mail_data['phpmailer_exception_code'] = $e->getCode();
* Fires after a PHPMailer\PHPMailer\Exception is caught.
* @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 ) );
if ( ! function_exists( 'wp_authenticate' ) ) :
* Authenticates a user, confirming the login credentials are valid.
* @since 4.5.0 `$username` now accepts an email address.
* @param string $username User's username or email address.
* @param string $password User's password.
* @return WP_User|WP_Error WP_User object if the credentials are valid,
function wp_authenticate(
$username = sanitize_user( $username );
$password = trim( $password );
* Filters whether a set of user login credentials are valid.
* A WP_User object is returned if the credentials authenticate a user.
* WP_Error or null otherwise.
* @since 4.5.0 `$username` now accepts an email address.
* @param null|WP_User|WP_Error $user WP_User if the user is authenticated.
* WP_Error or null otherwise.
* @param string $username Username or email address.
* @param string $password User password.
$user = apply_filters( 'authenticate', null, $username, $password );
if ( null === $user || false === $user ) {
* TODO: What should the error message be? (Or would these even happen?)
* Only needed if all authentication handlers fail to return anything.
$user = new WP_Error( 'authentication_failed', __( '<strong>Error:</strong> Invalid username, email address or incorrect password.' ) );
$ignore_codes = array( 'empty_username', 'empty_password' );
if ( is_wp_error( $user ) && ! in_array( $user->get_error_code(), $ignore_codes, true ) ) {
* Fires after a user login has failed.
* @since 4.5.0 The value of `$username` can now be an email address.
* @since 5.4.0 The `$error` parameter was added.
* @param string $username Username or email address.
* @param WP_Error $error A WP_Error object with the authentication failure details.
do_action( 'wp_login_failed', $username, $error );
if ( ! function_exists( 'wp_logout' ) ) :
* Logs the current user out.
$user_id = get_current_user_id();
wp_destroy_current_session();
wp_set_current_user( 0 );
* Fires after a user is logged out.
* @since 5.5.0 Added the `$user_id` parameter.
* @param int $user_id ID of the user that was logged out.
do_action( 'wp_logout', $user_id );
if ( ! function_exists( 'wp_validate_auth_cookie' ) ) :
* Validates authentication cookie.
* The checks include making sure that the authentication cookie is set and
* pulling in the contents (if $cookie is not used).
* Makes sure the cookie is not expired. Verifies the hash in cookie is what is
* should be and compares the two.
* @global int $login_grace_period
* @param string $cookie Optional. If used, will validate contents instead of cookie's.
* @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
* Note: This does *not* default to 'auth' like other cookie functions.
* @return int|false User ID if valid cookie, false if invalid.
function wp_validate_auth_cookie( $cookie = '', $scheme = '' ) {
$cookie_elements = wp_parse_auth_cookie( $cookie, $scheme );
if ( ! $cookie_elements ) {
* Fires if an authentication cookie is malformed.
* @param string $cookie Malformed auth cookie.
* @param string $scheme Authentication scheme. Values include 'auth', 'secure_auth',
do_action( 'auth_cookie_malformed', $cookie, $scheme );
$scheme = $cookie_elements['scheme'];
$username = $cookie_elements['username'];
$hmac = $cookie_elements['hmac'];
$token = $cookie_elements['token'];
$expiration = $cookie_elements['expiration'];
$expired = (int) $expiration;
// Allow a grace period for POST and Ajax requests.
if ( wp_doing_ajax() || 'POST' === $_SERVER['REQUEST_METHOD'] ) {
$expired += HOUR_IN_SECONDS;
// Quick check to see if an honest cookie has expired.
if ( $expired < time() ) {
* Fires once an authentication cookie has expired.
* @param string[] $cookie_elements {
* Authentication cookie components. None of the components should be assumed
* to be valid as they come directly from a client-provided cookie value.
* @type string $username User's username.
* @type string $expiration The time the cookie expires as a UNIX timestamp.
* @type string $token User's session token used.
* @type string $hmac The security hash for the cookie.
* @type string $scheme The cookie scheme to use.
do_action( 'auth_cookie_expired', $cookie_elements );
$user = get_user_by( 'login', $username );
* Fires if a bad username is entered in the user authentication process.
* @param string[] $cookie_elements {
* Authentication cookie components. None of the components should be assumed
* to be valid as they come directly from a client-provided cookie value.
* @type string $username User's username.
* @type string $expiration The time the cookie expires as a UNIX timestamp.
* @type string $token User's session token used.
* @type string $hmac The security hash for the cookie.
* @type string $scheme The cookie scheme to use.
do_action( 'auth_cookie_bad_username', $cookie_elements );
if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
// Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
$pass_frag = substr( $user->user_pass, 8, 4 );
// Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
$pass_frag = substr( $user->user_pass, -4 );
$key = wp_hash( $username . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
$hash = hash_hmac( 'sha256', $username . '|' . $expiration . '|' . $token, $key );
if ( ! hash_equals( $hash, $hmac ) ) {
* Fires if a bad authentication cookie hash is encountered.
* @param string[] $cookie_elements {
* Authentication cookie components. None of the components should be assumed
* to be valid as they come directly from a client-provided cookie value.
* @type string $username User's username.
* @type string $expiration The time the cookie expires as a UNIX timestamp.
* @type string $token User's session token used.
* @type string $hmac The security hash for the cookie.
* @type string $scheme The cookie scheme to use.
do_action( 'auth_cookie_bad_hash', $cookie_elements );
$manager = WP_Session_Tokens::get_instance( $user->ID );
if ( ! $manager->verify( $token ) ) {
* Fires if a bad session token is encountered.
* @param string[] $cookie_elements {
* Authentication cookie components. None of the components should be assumed
* to be valid as they come directly from a client-provided cookie value.
* @type string $username User's username.
* @type string $expiration The time the cookie expires as a UNIX timestamp.
* @type string $token User's session token used.
* @type string $hmac The security hash for the cookie.
* @type string $scheme The cookie scheme to use.
do_action( 'auth_cookie_bad_session_token', $cookie_elements );
// Ajax/POST grace period set above.
if ( $expiration < time() ) {
$GLOBALS['login_grace_period'] = 1;
* Fires once an authentication cookie has been validated.
* @param string[] $cookie_elements {
* Authentication cookie components.
* @type string $username User's username.
* @type string $expiration The time the cookie expires as a UNIX timestamp.
* @type string $token User's session token used.
* @type string $hmac The security hash for the cookie.
* @type string $scheme The cookie scheme to use.
* @param WP_User $user User object.
do_action( 'auth_cookie_valid', $cookie_elements, $user );
if ( ! function_exists( 'wp_generate_auth_cookie' ) ) :
* Generates authentication cookie contents.
* @since 4.0.0 The `$token` parameter was added.
* @param int $user_id User ID.
* @param int $expiration The time the cookie expires as a UNIX timestamp.
* @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
* @param string $token User's session token to use for this cookie.
* @return string Authentication cookie contents. Empty string if user does not exist.
function wp_generate_auth_cookie( $user_id, $expiration, $scheme = 'auth', $token = '' ) {
$user = get_userdata( $user_id );
$manager = WP_Session_Tokens::get_instance( $user_id );
$token = $manager->create( $expiration );
if ( str_starts_with( $user->user_pass, '$P$' ) || str_starts_with( $user->user_pass, '$2y$' ) ) {
// Retain previous behaviour of phpass or vanilla bcrypt hashed passwords.
$pass_frag = substr( $user->user_pass, 8, 4 );
// Otherwise, use a substring from the end of the hash to avoid dealing with potentially long hash prefixes.
$pass_frag = substr( $user->user_pass, -4 );
$key = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $expiration . '|' . $token, $scheme );
$hash = hash_hmac( 'sha256', $user->user_login . '|' . $expiration . '|' . $token, $key );
$cookie = $user->user_login . '|' . $expiration . '|' . $token . '|' . $hash;
* Filters the authentication cookie.
* @since 4.0.0 The `$token` parameter was added.
* @param string $cookie Authentication cookie.
* @param int $user_id User ID.
* @param int $expiration The time the cookie expires as a UNIX timestamp.
* @param string $scheme Cookie scheme used. Accepts 'auth', 'secure_auth', or 'logged_in'.
* @param string $token User's session token used.
return apply_filters( 'auth_cookie', $cookie, $user_id, $expiration, $scheme, $token );
if ( ! function_exists( 'wp_parse_auth_cookie' ) ) :
* Parses a cookie into its components.
* @since 4.0.0 The `$token` element was added to the return value.
* @param string $cookie Authentication cookie.
* @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'.
* @return string[]|false {
* Authentication cookie components. None of the components should be assumed
* to be valid as they come directly from a client-provided cookie value. If
* the cookie value is malformed, false is returned.
* @type string $username User's username.
* @type string $expiration The time the cookie expires as a UNIX timestamp.
* @type string $token User's session token used.
* @type string $hmac The security hash for the cookie.
* @type string $scheme The cookie scheme to use.
function wp_parse_auth_cookie( $cookie = '', $scheme = '' ) {
if ( empty( $cookie ) ) {
$cookie_name = AUTH_COOKIE;
$cookie_name = SECURE_AUTH_COOKIE;
$cookie_name = LOGGED_IN_COOKIE;
$cookie_name = SECURE_AUTH_COOKIE;
$cookie_name = AUTH_COOKIE;
if ( empty( $_COOKIE[ $cookie_name ] ) ) {
$cookie = $_COOKIE[ $cookie_name ];
$cookie_elements = explode( '|', $cookie );
if ( count( $cookie_elements ) !== 4 ) {
list( $username, $expiration, $token, $hmac ) = $cookie_elements;
return compact( 'username', 'expiration', 'token', 'hmac', 'scheme' );
if ( ! function_exists( 'wp_set_auth_cookie' ) ) :
* Sets the authentication cookies based on user ID.
* The $remember parameter increases the time that the cookie will be kept. The
* default the cookie is kept without remembering is two days. When $remember is
* set, the cookies will be kept for 14 days or two weeks.
* @since 4.3.0 Added the `$token` parameter.
* @param int $user_id User ID.
* @param bool $remember Whether to remember the user.
* @param bool|string $secure Whether the auth cookie should only be sent over HTTPS. Default is an empty
* string which means the value of `is_ssl()` will be used.
* @param string $token Optional. User's session token to use for this cookie.
function wp_set_auth_cookie( $user_id, $remember = false, $secure = '', $token = '' ) {
* Filters the duration of the authentication cookie expiration period.
* @param int $length Duration of the expiration period in seconds.
* @param int $user_id User ID.
* @param bool $remember Whether to remember the user login. Default false.
$expiration = time() + apply_filters( 'auth_cookie_expiration', 14 * DAY_IN_SECONDS, $user_id, $remember );
* Ensure the browser will continue to send the cookie after the expiration time is reached.