When is wp_mail NOT pluggable?




Screencasts – WP Theme Tutorial show

Summary: Have you heard about <a href="http://codex.wordpress.org/Pluggable_Functions">Pluggable Functions</a> in WordPress? A while ago I wrote a <a href="http://wpthemetutorial.com/2013/05/20/logging-emails-in-wordpress-with-wp_logging/">tutorial about how to log email by plugging wp_mail</a> which is a pluggable function. Then I went off skipping on my way in to a land of bliss knowing I'd never again email clients by accident. Cue the dark clounds and ominous music. wp_mail is not plugged when... So there I was skipping along through a field of flowers and troubleshooting some password reset issues on a staging site only to find that the password reset emails were not getting logged. I had actually just sent a password reset email to a user by accident which made me frown and my client frowned. Lucky for us the user was a developer of a different stripe (not a WordPress developer) so when I emailed him about the mistake he understood. We still looked silly. wp_mail is not pluggable during a password reset it will send your emails so my previous plugin didn't work. We were also running in to issues with plugins like [wpMandril][mandril] which also 'plug' wp_mail. You can't plug a function twice though so all we got were errors. Logging all wp_mail without plugging it Now let's look at how I log wp_mail without using the pluggable function pattern. We'll start by taking a look at the full wp_mail code. if ( !function_exists( 'wp_mail' ) ) : /** * Send mail, similar to PHP's mail * * A true return value does not automatically mean that the user received the * email successfully. It just only means that the method used was able to * process the request without any errors. * * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from * creating a from address like 'Name ' when both are set. If * just 'wp_mail_from' is set, then just the email address will be used with no * name. * * The default content type is 'text/plain' which does not allow using HTML. * However, you can set the content type of the email by using the * 'wp_mail_content_type' filter. * * The default charset is based on the charset used on the blog. The charset can * be set using the 'wp_mail_charset' filter. * * @since 1.2.1 * @uses apply_filters() Calls 'wp_mail' hook on an array of all of the parameters. * @uses apply_filters() Calls 'wp_mail_from' hook to get the from email address. * @uses apply_filters() Calls 'wp_mail_from_name' hook to get the from address name. * @uses apply_filters() Calls 'wp_mail_content_type' hook to get the email content type. * @uses apply_filters() Calls 'wp_mail_charset' hook to get the email charset * @uses do_action_ref_array() Calls 'phpmailer_init' hook on the reference to * phpmailer object. * @uses PHPMailer * * @param string|array $to Array or comma-separated list of email addresses to send message. * @param string $subject Email subject * @param string $message Message contents * @param string|array $headers Optional. Additional headers. * @param string|array $attachments Optional. Files to attach. * @return bool Whether the email contents were sent successfully. */ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ) { // Compact the input, apply the filters, and extract them back out extract( apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', '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 ( !is_object( $phpmailer ) || !is_a( $phpmailer, 'PHPMailer' ) ) { require_once ABSPATH . WPINC . '/class-phpmailer.php'; require_once ABSPATH . WPINC . '/class-smtp.php'; $phpmailer = new PHPMailer( true ); } // Headers if ( empty( $headers ) ) { $headers = array(); } else { if ( !