diff --git src/wp-includes/class-wpmailer-factory.php src/wp-includes/class-wpmailer-factory.php
new file mode 100644
index 0000000..8f4917c
--- /dev/null
+++ src/wp-includes/class-wpmailer-factory.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * Creates a WPMailer instance, and sets the default values of
+ * the content type, charset and from name/email.
+ *
+ * @see WPMailer
+ * @see wp_mail()
+ */
+class WPMailerFactory {
+
+	static function getMailer(){
+
+		require_once ABSPATH . WPINC . '/class-phpmailer.php';
+		require_once ABSPATH . WPINC . '/class-wpmailer.php';
+		require_once ABSPATH . WPINC . '/class-smtp.php';
+
+		$wpmailer = new WPMailer( true );
+		$wpmailer = self::reset( $wpmailer );
+
+		return $wpmailer;
+	}
+
+	static function reset( $wpmailer ){
+
+		$wpmailer->ClearAllRecipients();
+		$wpmailer->ClearAttachments();
+		$wpmailer->ClearCustomHeaders();
+		$wpmailer->ClearReplyTos();
+
+		$sitename = strtolower( $_SERVER['SERVER_NAME'] );
+		if ( substr( $sitename, 0, 4 ) == 'www.' ) {
+			$sitename = substr( $sitename, 4 );
+		}
+
+        $wpmailer->ContentType = 'text/plain';
+		$wpmailer->CharSet     = get_bloginfo( 'charset' );
+
+		$wpmailer->setFrom( 'wordpress@' . $sitename, 'WordPress' );
+
+		return $wpmailer;
+	}
+
+}
+?>
diff --git src/wp-includes/class-wpmailer.php src/wp-includes/class-wpmailer.php
new file mode 100644
index 0000000..9d7c136
--- /dev/null
+++ src/wp-includes/class-wpmailer.php
@@ -0,0 +1,278 @@
+<?php
+/**
+ * Email creation and transport class. (A child class of PHPMailer)
+ *
+ * This class adds a few 'helper' methods to the PHPMailer class and
+ * overrides the send method to trigger e-mail related filters.
+ *
+ */
+class WPMailer extends PHPMailer{
+
+	/**
+	 * Adds the passed e-mails as recipients
+	 *
+	 * @param string|array $recipients Array or comma-separated list of email addresses to send message.
+	 * @type string One of 'to', 'cc', 'bcc', or 'ReplyTo'
+	 */
+	function addRecipients( $addresses, $type )  {
+
+		//Set destination addresses
+		if ( !is_array( $addresses ) ){
+			$addresses = explode( ',', $addresses );
+		}
+
+		foreach ( (array) $addresses as $address ) {
+			try {
+				// Break $recipient into name and address parts if in the format "Foo <bar@baz.com>"
+				$recipient_name = '';
+
+				if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) {
+					if ( count( $matches ) == 3 ) {
+						$recipient_name = $matches[1];
+						$address        = $matches[2];
+					}
+				}
+
+				switch ( $type ) {
+					case 'to':
+						$this->addAddress( $address, $recipient_name );
+						break;
+					case 'cc':
+						$this->addCc( $address, $recipient_name );
+						break;
+					case 'bcc':
+						$this->addBcc( $address, $recipient_name );
+						break;
+					case 'reply_to':
+						$this->addReplyTo( $address, $recipient_name );
+						break;
+				}
+			} catch ( phpmailerException $e ) {
+				continue;
+			}
+		}
+	}
+
+	/**
+	 * Adds the provided headers to the e-mail.
+	 *
+	 * Will set the content type, charset and CC/BCC recipients where appropriate
+	 *
+	 * @param string|array $headers Additional headers.
+	 */
+	function setHeaders( $headers = array() ){
+
+		$tempheaders = array();
+
+		// Headers
+		if ( $headers ){
+			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;
+			}
+		}
+
+		if ( empty( $tempheaders ) ) {
+			return;
+		}
+
+		$headers = array();
+		$cc = $bcc = $reply_to = array();
+
+
+		// Iterate through the raw headers
+		foreach ( (array) $tempheaders as $header ) {
+			if ( strpos($header, ':') === false ) {
+				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 ( $bracket_pos !== false ) {
+
+						// Text before the bracketed email is the "From" name.
+						if ( $bracket_pos > 0 ) {
+							$from_name = substr( $content, 0, $bracket_pos - 1 );
+							$from_name = str_replace( '"', '', $from_name );
+							$this->FromName = trim( $from_name );
+						}
+						$from_email = substr( $content, $bracket_pos + 1 );
+						$from_email = str_replace( '>', '', $from_email );
+						$this->From = trim( $from_email );
+
+						// Avoid setting an empty $from_email.
+					} elseif ( '' !== trim( $content ) ) {
+						$this->From = trim( $content );
+					}
+					break;
+
+				case 'content-type':
+
+					if ( strpos( $content, ';' ) !== false ) {
+
+						list( $type, $charset_content ) = explode( ';', $content );
+						$this->ContentType = trim( $type );
+
+						if ( false !== stripos( $charset_content, 'charset=' ) ) {
+							$this->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 ) ) {
+						$this->ContentType = trim( $content );
+					}
+
+					if ( false !== stripos( $this->ContentType, 'multipart' ) && ! empty( $boundary ) ){
+						$this->AddCustomHeader( sprintf( "Content-Type: %s;\n\t boundary=\"%s\"", $this->ContentType, $boundary ) );
+					}
+					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
+					$this->AddCustomHeader( sprintf( '%1$s: %2$s', trim( $name ), trim( $content ) ) );
+					break;
+			endswitch;
+		}
+
+		// Use appropriate methods for handling addresses, rather than treating them as generic headers
+		$address_headers = compact( 'cc', 'bcc', 'reply_to' );
+
+		foreach ( $address_headers as $address_header => $addresses ) {
+			if ( empty( $addresses ) ) {
+				continue;
+			}
+
+			$this->addRecipients( $addresses, $address_header );
+		}
+	}
+
+	/**
+	 * Helper function for adding attachments.
+	 *
+	 * @param string|array $attachments Files to attach.
+	 */
+	function addAttachments( $attachments ){
+
+		if ( !is_array($attachments) )
+			$attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
+
+
+		foreach ( (array) $attachments as $attachment ) {
+			try {
+				$this->AddAttachment( $attachment );
+			} catch ( phpmailerException $e ) {
+				continue;
+			}
+		}
+	}
+
+	/**
+	 * Pre-send routine, for "last minute" filtering of e-mail properties.
+	 *
+	 * Using the two 'wp_mail_from' and 'wp_mail_from_name' hooks allow from
+	 * creating a from address like 'Name <email@address.com>' 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.
+	 *
+	 * @see PHPMailer::preSend();
+	 * @param string|array $attachments Files to attach.
+	 */
+	function preSend(){
+
+		/**
+		 * 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', $this->From );
+
+		/**
+		 * 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', $this->FromName );
+
+		$this->setFrom( $from_email, $from_name );
+
+		// Set to use PHP's mail()
+		$this->IsMail();
+
+		/**
+	 	* Filter the wp_mail() content type.
+	 	*
+	 	* @since 2.3.0
+	 	*
+	 	* @param string $content_type Default wp_mail() content type.
+	 	*/
+		$this->ContentType = apply_filters( 'wp_mail_content_type', $this->ContentType );
+
+		// Set whether it's plaintext, depending on $content_type
+		if ( 'text/html' == $this->ContentType ){
+			$this->IsHTML( true );
+		}
+
+		/**
+	 	* Filter the default wp_mail() charset.
+	 	*
+	 	* @since 2.3.0
+	 	*
+	 	* @param string $charset Default email charset.
+	 	*/
+		$this->CharSet = apply_filters( 'wp_mail_charset', $this->CharSet );
+
+		/**
+	 	* Fires after PHPMailer is initialized.
+	 	*
+	 	* @since 2.2.0
+	 	*
+	 	* @param PHPMailer &$this The PHPMailer instance, passed by reference.
+	 	*/
+		do_action_ref_array( 'phpmailer_init', array( &$this ) );
+
+		return parent::preSend();
+
+	}
+
+}
diff --git src/wp-includes/pluggable.php src/wp-includes/pluggable.php
index 511e6ac..3935efa 100644
--- src/wp-includes/pluggable.php
+++ src/wp-includes/pluggable.php
@@ -147,21 +147,9 @@ if ( !function_exists( 'wp_mail' ) ) :
  * 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 <email@address.com>' 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
- * {@see 'wp_mail_content_type'} filter.
- *
- * The default charset is based on the charset used on the blog. The charset can
- * be set using the {@see 'wp_mail_charset'} filter.
- *
  * @since 1.2.1
  *
- * @global PHPMailer $phpmailer
+ * @uses WPMailer
  *
  * @param string|array $to          Array or comma-separated list of email addresses to send message.
  * @param string       $subject     Email subject
@@ -203,268 +191,29 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array()
 		$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 ) ) {
-		require_once ABSPATH . WPINC . '/class-phpmailer.php';
-		require_once ABSPATH . WPINC . '/class-smtp.php';
-		$phpmailer = new PHPMailer( true );
-	}
-
-	// Headers
-	$cc = $bcc = $reply_to = array();
-
-	if ( empty( $headers ) ) {
-		$headers = array();
+	if ( ! is_object( $phpmailer ) || ! is_a( $phpmailer, 'WPMailer' ) ) {
+		$phpmailer = WPMailerFactory::getMailer();
 	} 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 ( strpos($header, ':') === false ) {
-					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 ( $bracket_pos !== false ) {
-							// Text before the bracketed email is the "From" name.
-							if ( $bracket_pos > 0 ) {
-								$from_name = substr( $content, 0, $bracket_pos - 1 );
-								$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 ( strpos( $content, ';' ) !== false ) {
-							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();
-
-	// From email and name
-	// 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 = strtolower( $_SERVER['SERVER_NAME'] );
-		if ( substr( $sitename, 0, 4 ) == 'www.' ) {
-			$sitename = substr( $sitename, 4 );
-		}
-
-		$from_email = 'wordpress@' . $sitename;
+		$phpmailer = WPMailerFactory::reset( $phpmailer );
 	}
 
-	/**
-	 * 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 );
-
-	$phpmailer->setFrom( $from_email, $from_name );
-
 	// Set destination addresses
-	if ( !is_array( $to ) )
-		$to = explode( ',', $to );
+	$phpmailer->addRecipients( $to, 'to' );
 
 	// Set mail's subject and body
 	$phpmailer->Subject = $subject;
 	$phpmailer->Body    = $message;
 
-	// Use appropriate methods for handling addresses, rather than treating them as generic headers
-	$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 <bar@baz.com>"
-				$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 ( phpmailerException $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' );
-
-	// Set the content-type and 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 ) {
-			$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 ) );
+		$phpmailer->setHeaders( $headers );
 	}
 
-	if ( !empty( $attachments ) ) {
-		foreach ( $attachments as $attachment ) {
-			try {
-				$phpmailer->AddAttachment($attachment);
-			} catch ( phpmailerException $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 ) );
+	//Set attachments
+	$phpmailer->addAttachments( $attachments );
 
 	// Send!
 	try {
diff --git src/wp-settings.php src/wp-settings.php
index 37a31c2..8eeb043 100644
--- src/wp-settings.php
+++ src/wp-settings.php
@@ -213,6 +213,7 @@ require( ABSPATH . WPINC . '/rest-api.php' );
 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-server.php' );
 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-response.php' );
 require( ABSPATH . WPINC . '/rest-api/class-wp-rest-request.php' );
+require( ABSPATH . WPINC . '/class-wpmailer-factory.php' );
 
 // Load multisite-specific files.
 if ( is_multisite() ) {
diff --git tests/phpunit/includes/mock-mailer.php tests/phpunit/includes/mock-mailer.php
index 7acfd57..940792c 100644
--- tests/phpunit/includes/mock-mailer.php
+++ tests/phpunit/includes/mock-mailer.php
@@ -1,7 +1,8 @@
 <?php
 require_once( ABSPATH . '/wp-includes/class-phpmailer.php' );
+require_once( ABSPATH . '/wp-includes/class-wpmailer.php' );
 
-class MockPHPMailer extends PHPMailer {
+class MockPHPMailer extends WPMailer {
 	var $mock_sent = array();
 
 	function preSend() {
