Building a Multipart Email Plugin for WordPress

This article is part of a three part series exploring how to configure the WordPress content management system to send multipart emails. In part 3, I go through setting up a basic test plugin to control the sending of multipart emails.

Plugin Setup

We’re going to be setting up a PHP class that contains all the functions and filters necessary to send multipart email with WordPress. Lets begin by setting up a plugin environment to contain the plugin code.

Go to your website’s plugins folder, which can be found under wp-content. Create a new folder within the plugins folder and call it “my_multipart_email”. If you would like to call it something else, feel free to do so. Be careful to avoid overly generic names like “email” or “email_plugin” to avoid conflicts with other plugins.

Create a file inside your newly created folder and call it “my_multipart_email.php”. The name of the file, without it’s .php extension must match that of the folder.

Open your “my_multipart_email.php” file in your text editor of choice and copy the following:

<?php
/**
 * Plugin Name: My Multipart Email
 * Plugin URI: http://mywebsite.com
 * Description: A plugin that provides functions and filters that enable the WordPress system to send multipart/alternative mime type emails.
 * Version: 1.0.0
 * Author: Me
 * Author URI: http://myself.me
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

The above provides details to WordPress about the plugin and registers the plugin with the system. We also include a check for the 'ABSPATH' constant to make sure that this file cannot be accessed from outside of the WordPress environment.

Now let’s go ahead and start building our multipart email class.

Class Properties

Beneath the comment code that registers our plugin, we’re going to start our multipart email class and set up a few empty properties that will apply to our class.

/**
 * My Multipart Email Class.
 *
 * @package    WordPress
 * @subpackage My Multipart Email
 * @version 1.0.0
 */
class my_multipart_email_class{
    /**
     * Email content type
     *
     * @since 1.0.0
     */
    public $email_content_type;
	
    /**
     * Plain Text
     *
     * @since 1.0.0
     */
    public $plain_text;
	
    /**
     * HTML
     *
     * @since 1.0.0
     */
    public $html;
 
    /**
     * Sending
     *
     * @since 1.0.0
     */
    public $sending;
  • $email_content_type – this property will be used to store the type of email to be sent. Possible values will be plain, html and multipart.
  • $plain_text – this property will be used to store the plain text version of our message body.
  • $html – this property will be used to store the HTML version of our message body.
  • $sending – this boolean property (true or false) will be set when using the class’ sending function. This will be used inside our filter functions to determine whether our class functions are being used to send email or if WordPress’ core wp_mail() function is being used unmodified.

Next, let’s go ahead and construct some helper functions to use during the sending of our email.

Helper Functions

/**
 * Get From Address.
 *
 * @return string The email address to send the email from.
 */
public function get_from_address(){
    return 'website@mywebsite.com';
}
 
/**
 * Get From Name
 *
 * @return string The name from which to send the current email.
 */
public function get_from_name(){
    return 'MyWebsite';
}
 
/**
 * Get Content Type
 *
 * @return string The email mime type.
 */
public function get_content_type(){
    switch( $this->email_content_type ){
        case 'html':
            return 'text/html';
        case 'multipart':
            return 'multipart/alternative';
        default :
            return 'text/plain';
    }
}

Let’s break down what each of the above functions do:

  • get_from_address() – this function returns the email address which the current email should be sent from. We’ll use this with the wp_mail_from filter to set the from address.
  • get_from_name() – this function returns the name we would like the email to come from. We’ll use this with the wp_mail_from_name filter to set the from name.
  • get_content_type – this function contains a switch which checks our $email_content_type property and returns the correct mime type to use when sending the email. We’ll be setting the $email_content_type property when we construct our html and text message bodies prior to send. This function will be used with the wp_mail_content_type filter.

The Send Function

This is the function that will be responsible for sending out our emails. Essentially we’ll be able to use it exactly like we would use WordPress’ core wp_mail()function as it uses wp_mail() with extra functions attached to WordPress action and filter hooks in order to enable multipart email functionality.

/**
 * Send Email
 * 
 * Extended email send function.
 *
 * @uses wp_mail() To send the email message
 * @see https://developer.wordpress.org/reference/functions/wp_mail/ 
 *                 For documentation on the parameters used by wp_mail().
 * @return bool    Whether the email contents were sent successfully.
 * @since 1.0.0
 */
public function send_email( $to, $subject, $message, $headers = '', $attachments = array() ){
    // Set sending property to true at beginning of send
    $this->sending = true;
    
    // Add filters prior to send
    add_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
    add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
    add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
    
    // Send the message with wp_mail()
    $return = wp_mail( $to, $subject, $message, $headers, $attachments );
 
    /** 
     * Remove filters after send so they don't interfere with normal 
     * wp_mail() and can be set on a per message basis.
     */
    remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
    remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
    remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
    
    // Return result of wp_mail()
    return $return; 
}

Let’s go through what this sending function does:

  1. It set’s the $sending property to true at the beginning of the send. This is checked later in an action hook to determine that our class’ function is being used to send multipart email.
  2. It adds necessary filters to the wp_mail_from, wp_mail_from_name and wp_mail_content_type filter hooks so we can set the email address, name and content type for our email prior to send.
  3. It sends the email using wp_mail() and stores the result of the send in the $return variable.
  4. It removes the previously set filters so that they won’t affect any emails still set up to use the normail wp_mail() function. This will help avoid conflict with other plugins and functions that may use the wp_mail() function to send email. It also opens up the opportunity to allow us to set the from address and from name on a per message basis.
  5. It returns the boolean result of the wp_mail() function so it can be used for error handling.

Adding a Shortcode

We’re going to be registering a shortcode with the WordPress system which will act as a way for us to test our email sending functionality. Using the shortcode, we’ll construct an email message that can be triggered on page load using attributes and content provided to the shortcode in the WordPress admin.

To do this we’ll need two functions. One that can be registered with the add_shortcode() action hook and controls what happens when WordPress encounters our shortcode in our post or page content. The second function will be used to provide basic sanitisation of the data we’ll be passing to our shortcode.

Let’s go ahead and setup our sanitisation function first.

/**
 * Sanitize Shortcode Atts
 * 
 * Provides basic shortcode attribute Sanitization. Meant for test purposes only, more in-depth
 * sanitization may be necessary depending on use case.
 *
 * @param array  $atts {
 *		Arguments provided by shortcode.
 *		
 *		@see my_multipart_email_class::multipart_shortcode();  
 * }
 * @return array $args The sanitized argument array.
 * @since 1.0.0
 */
private function sanitize_shortcode_atts( $atts ){
	// Loop through array and run values through sanitization switch
	if( is_array( $atts ) ){
		foreach( $atts as $key => $value ){
			switch( $key ){
				case 'to':
					$atts[ $key ] = sanitize_email( $value );
				break;
				default:
					$atts[ $key ] = sanitize_text_field( $value );
				break;
			}
		}
	}
	
	return $atts;
}

This basic sanitisation function loops through the data provided via the shortcode and uses WordPress’ sanitisation functions to make sure that all data is safe before being used by our plugin. I’ve only setup two kinds of sanitisation in this function as I’m only accepting simple text data and a single email address via the shortcode’s attributes.

The switch checks if the current shortcode array key ($key) is ‘to‘. If so, we know that this attribute is meant to contain an email address so we use the sanitize_email() WordPress function to make sure that the data provided doesn’t contain any malicious content or characters which aren’t allowed in an email address. In all of our other attributes we’re expecting text strings only so we’ll use sanitize_text_field() on any data that has been provided in any other shortcode attribute.

Tip: It’s important when giving a user the ability to input data that you always carry out data validation and sanitisation procedures in your code. This will help prevent the unwitting creation of security vulnerabilities in the WordPress system. See this post in the WordPress codex for further details on validation and sanitisation.

Now that we have our sanitisation function, we’ll create our shortcode function which accepts our shortcode attributes and passes them on for processing after sanitisation.

/**
 * Multipart Shortcode
 * 
 * Controls the test sending of a multipart email. This functionality is meant for 
 * test purposes only to show the capability of WordPress to send multipart emails.
 * This method should not be used in a production environment.
 *
 * @param array $atts {
 *	 @param string $to           The email address to send an email to.
 *	 @param string $subject      The subject of the email.
 * 	 @param string $type         The type of email to send. Accepted types are plain,
 *                               html and multipart.
 * }
 * @param string  $message_body  The message to send in the body of the email.
 * @return string $output        The output to show on the page on success or failure of the
 *                               shortcode function.
 * @since 1.0.0
 */
public function multipart_shortcode( $atts, $message_body ){
	// Get shortcode attributes
	$args = shortcode_atts( array(
		'to'      => 'me@myemail.com',
		'subject' => 'My Default Email Subject',
		'type'    => 'plain'
	), $atts );
	
	// Sanitize the shortcode attributes
	$args = $this->sanitize_shortcode_atts( $args );
	
	// Sanitize $message_body
	$message_body = wp_kses( $message_body, array( 'br' => array() ) );
	
	/** 
	 * Set the property for email content type based on the value provided in the shortcode
	 * and construct the required message bodies accordingly.
	 */
	switch ( $args['type'] ){
		case 'html':
			$this->email_content_type = 'html';
		break;
		case 'multipart':
			$this->email_content_type = 'multipart';
		break;
		default:
			$this->email_content_type = 'plain';
		break;
	}
	
	// Construct message bodies
	$check = $this->construct_message_bodies( $args, $message_body );
	
	// Check that message bodies have been constructed successfully and, if so, send email
	if( $check ){
		// Send relevant message body
		switch ( $args['type'] ){
			case 'html':
				$send = $this->send_email( $args['to'], $args['subject'], $this->html );
			break;
			case 'multipart':
				/**
				 * In the case of multipart we still send the HTML body to the send function 
				 * as the plain text body will be added via the phpmailer_init action hook.
				 *
				 * @see my_multipart_email::enable_multipart()
				 */ 
				$send = $this->send_email( $args['to'], $args['subject'], $this->html );
			break;
			default:
				$send = $this->send_email( $args['to'], $args['subject'], $this->plain_text );
			break;
		}
		
		// Check whether the send was sucessful and set output text accordingly
		if( false !== $send ){
			$output = '<p class = "success">Your email message sent sucessfully.</p>';
		}else{
			$output = '<p class = "error">Your email failed to send. Please try again.</p>';
		}
		
	}else{
		// Set output error message
		$output = '<p class = "error">Message failed to send due to a problem with the email templates. Please check and try again.</p>';
	}
	
	echo $output;
}

First, we use the shortcode_atts() WordPress function to compare the attributes provided against an array of expected values. We can set default attribute values in this array to act as fallbacks should the shortcode not contain all the values we are expecting. The result is a new array containing the attributes that were provided via the shortcode combined with our defaults if any attributes were missing.

Second, we pass the resulting array of arguments to the sanitize_shortcode_atts() function we created. This will run through the attributes and make sure they are safe for us to continue processing.

The $message_body isn’t passed as an attribute value in a shortcode. For those not familiar with WordPress’ shortcode structure, here’s an example to show you how the data would be provded via shortcode:

[shortcode_tag attribute="$value"]$message_body[/shortcode_tag]

As you can see, $message_body is made up of any content that falls between the opening and closing shortcode tags. As such, it’s possible to pass HTML markup via the WordPress admin interface which could then be used in our HTML email.

I’ve decided that for this example, I only want to retain line breaks (<br> or <br />) in my message body and ignore all other HTML. Accordingly, I use the wp_kses() function for sanitisation stripping out any malicious content and retaining only the line break tags in our message body before adding it to our email.

Next, a switch is used to set the $email_content_type class property. We’ve provided the type of email we want to send in our shortcode’s ‘type’ attribute. Our functions can now check this when deciding which message bodies need to be constructed and the settings necessary for sending that type of email.

With our content type set, we can pass the shortcode data through to our $construct_message_bodies() function to handle the construction of our email message. We check that this has completed successfully by setting the outcome of the function to a $check variable. This will give a true or false value to our $check variable allowing us to construct an $output message to show to the user accordingly should the message bodies fail to construct for any reason.

If the message bodies construct successfully, a second switch passes the required message bodies and arguments to our email send function for sending. The outcome of the send is set to a $send variable to check the success or failure of the send and construct our $output message to the user accordingly.

Finally, depending on the results of the shortcode function and our email send, our $output value is echoed to the page to notify us of success or failure.

Constructing the Different Message Bodies

Now we have the helper and send functions set up, we can add our message body construction functions to the multipart email class. We’re going to add three new functions responsible for the construction of our email message bodies.

/**
 * Construct message bodies.
 * 
 * Checks the email content type for the current email being sent and 
 * constructs the message bodies accordingly.
 *
 * @param array  $args {
 *		Arguments provided by shortcode.
 *		
 *		@see my_multipart_email_class::multipart_shortcode();  
 * }
 * @param string $message_body The message to be sent.
 * @return bool success True on successful construction of message bodies.
 * @since 1.0.0
 */
private function construct_message_bodies( $args, $message_body ){
	// Setup message bodies based on email content type
	switch ( $this->email_content_type ) {
		case 'html':
			// Check html body constructs ok
			$check = $this->construct_html_body( $args, $message_body );
		break;
		case 'multipart':
			// Check html body constructs ok
			$check = $this->construct_html_body( $args, $message_body );
			
			// If html body constructed ok, construct plain body
			if( $check ){
				// Check plain body constructs ok
				$check = $this->construct_plain_body( $args, $message_body );
			}
		break;
		default:
			// Check plain body constructed ok
			$check = $this->construct_plain_body( $args, $message_body );
		break;
	}
	
	return $check;
}

/**
 * Construct html body.
 *
 * Checks for the existence of the required html template for the
 * current email being sent and constructs the html message body.
 *
 * @param array $args {
 *		Arguments provided by construct_message_bodies() function.
 *		
 *		@see my_multipart_email_class::construct_message_bodies();  
 * }
 * @param string $message_body The message to be sent.
 * @return bool success True on successful construction of html message body.
 * @since 1.0.0
 */
private function construct_html_body( $args, $message_body ){
	// Check that our theme has an HTML email template
	$template = locate_template( 'template-parts/emails/html/body.php' );
	
	if( empty( $template ) ){
		// Return false back to requesting function
		return false;
	}
	
	// Construct the email subject from the arguments provided
	$subject = $args['subject'];
	
	// We have a template and our variables set, start output buffer
	ob_start();		
					
	// Get template file to use from theme
	include( $template );
	
	// Save template output to class property  
	$this->html = ob_get_contents();
	
	// Clean output buffer
	ob_end_clean();
	
	// Return success
	return true;
}

/**
 * Construct plain body.
 *
 * Checks for the existence of the required plain text template for the
 * current email being sent and constructs the plain text message body.
 *
 * @param array $args {
 *		Arguments provided by construct_message_bodies().
 *		
 *		@see my_multipart_email_class::construct_message_bodies();  
 * }
 * @param string $message_body The message to send.
 * @return bool success True on successful construction of plain text message body.
 * @since 1.0.0
 */
private function construct_plain_body( $args, $message_body ){
	// Check that our theme has a plain text email template
	$template = locate_template( 'template-parts/emails/plain/body.php' );
	
	// Strip any tags found in message body
	$message_body = wp_strip_all_tags( $message_body );
	
	if( empty( $template ) ){
		// Return false back to requesting function
		return false;
	}
	
	// Construct the email subject from the arguments provided
	$subject = $args['subject'];
			
	// We have a template and our variables set, start output buffer
	ob_start();		
					
	// Get template file to use from theme
	include( $template );
	
	// Save template output to class property 
	$this->plain_text = ob_get_contents();
	
	// Clean output buffer
	ob_end_clean();
	
	// Return success
	return true;
}

Here’s what each of the above functions do:

  • construct_message_bodies() – This function checks the content type of the email we want to send and runs the required message body construction functions accordingly.
  • construct_html_body() – This function first checks for the existence of the required HTML email templates in our theme. If found, it passes the attributes and message provided via our shortcode to the templates and constructs the message using PHP’s output buffer to set the resulting message body to our $html class property.
  • construct_plain_body() – This function checks for the existence of the required plain text templates in our theme. If found, it passes the attributes and message provided via our shortcode to the templates. It strips any tags present in the message using the wp_strip_all_tags() WordPress function before constructing the plain text message, once again using the output buffer to set the message body to our $plain_text class property.

Enabling a Multipart Send

Now we have all construction functions and helper functions created for our class, it’s time to add the finishing pieces to the multipart puzzle.

You’ll have noticed that when setting ‘multipart’ as the email type we want to send in our shortcode, only our HTML message body is passed to our sending function – even though our plain text message body has also been created. This is because the wp_mail() function only expects one type of message body to be provided via the function. In order to add our second plain text message body, we need to modify our send using an action hook.

WordPress uses the PHPMailer email sending library to provide it’s email sending capabilities. We therefore need to use the phpmailer_init() action hook in order to pass our plain text message body directly to the phpmailer instance handling the sending of our email. We’ll also be adding a fix to our phpmailer instance in order to amend the return path header issue described here. This’ll further improve our email spam score when it comes to send.

Here’s the function to be run by phpmailer_init():

/**
 * Enable multipart email.
 *
 * @param object  $phpmailer The PHPMailer object.
 * @return object $phpmailer The original or modified PHPMailer object.
 * @since 1.0.0
 */
public function enable_multipart( $phpmailer )  {
	if ( $this-&gt;sending &amp;&amp; 'multipart' === $this-&gt;email_content_type ) {
		$phpmailer-&gt;AltBody = $this-&gt;plain_text;
		$this-&gt;sending   = false;
	}
	
	// Fix return path
	$phpmailer-&gt;Sender = $phpmailer-&gt;From;
	
	return $phpmailer;
}

The enable_multipart() function first checks if the current email being sent is using our plugin class by checking the $sending property. If so, it then checks whether the current email being sent is a multipart email and attaches our plain text message to the $phpmailer->AltBody instance property if so. Now when the email sends, we’ll have constructed a multipart email that contains both a HTML and plain text message body for the email client to choose from.

Lastly, the function sets the return path address to be the same as the sending address by changing the $phpmailer->Sender instance property to be the same as the $phpmailer->From property. This happens regardless of whether we’re using our class to send an email as it can improve the spam score of emails sent using wp_mail() to. If you don’t want this to happen with every email sent via WordPress you can move it inside the if( $this->sending check if you prefer.

Bringing it all Together

We’ve almost completed our plugin code now. All that’s left to do is to bind our functions to the correct hooks and create an instance of our class when the plugin is activated and WordPress loads.

In order to bind our functions to the correct hooks, I’m going to use the __construct() constructor method to bind functions to hooks as soon as an instance of our class is created. I usually include the constructor function for a class near the top of the class, underneath the class properties as it’s the function that will automatically be run as soon as a class instance is created.

Here’s our constructor function:

/**
 * Constructor
 *
 * @since 1.0.0
 */
 public function __construct(){
	 // Bind action hooks
	 add_action( 'phpmailer_init', array( $this, 'enable_multipart' ) );
	 
	 // Add Shortcode
	 add_shortcode( 'my_multipart_email', array( $this, 'multipart_shortcode' ) );
 }

As soon as an instance of our class is created, the constructor function will bind our enable_multipart() function to the phpmailer_init hook. It’ll also register a shortcode we can use to call our multipart_shortcode() function from within post or page content and send an email. The shortcode registered above would be: [my_multipart_email]. Feel free to call yours whatever you want.

Now we’ll create a function that sits outside of our class which creates an instance of our plugin when WordPress starts. We’ll run that function to start the ball rolling at the bottom of our plugin file.

}// End my_multipart_email_class
/**
 * Begins execution of the plugin.
 *
 * @since    1.0.0
 */
function run_my_multipart_email() {

	$plugin = new my_multipart_email_class();

}

run_my_multipart_email();

The run_my_multipart_email() function creates an instace of our class when the plugin is activated binding our hooks and enabling our functionality. We’ve now completed our plugin code!

Here is our my_multipart_email.php plugin code in full:

<?php
/**
 * Plugin Name: My Multipart Email
 * Plugin URI: http://mywebsite.com
 * Description: A plugin that provides functions and filters that enable the WordPress system to send multipart/alternative mime type emails.
 * Version: 1.0.0
 * Author: Me
 * Author URI: http://myself.me
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
 
/**
 * My Multipart Email Class.
 *
 * @package    WordPress
 * @subpackage My Multipart Email
 * @version 1.0.0
 */
class my_multipart_email_class{
    /**
     * Email content type
     *
     * @since 1.0.0
     */
    public $email_content_type;
	
    /**
     * Plain Text
     *
     * @since 1.0.0
     */
    public $plain_text;
	
    /**
     * HTML
     *
     * @since 1.0.0
     */
    public $html;
 
    /**
     * Sending
     *
     * @since 1.0.0
     */
    public $sending;
	
	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 */
	 public function __construct(){
		 // Bind action hooks
		 add_action( 'phpmailer_init', array( $this, 'enable_multipart' ) );
		 
		 // Add Shortcode
		 add_shortcode( 'my_multipart_email', array( $this, 'multipart_shortcode' ) );
	 }
	
	/**
	 * Get From Address.
	 *
	 * @return string address The email address to send the email from.
	 */
	public function get_from_address(){
		return 'website@mywebsite.com';
	}
 
	/**
	 * Get From Name
	 *
	 * @return string name The name from which to send the current email.
	 */
	public function get_from_name(){
		return 'MyWebsite';
	}
 
	/**
	 * Get Content Type
	 *
	 * @return string The email mime type.
	 */
	public function get_content_type(){
		switch( $this->email_content_type ){
			case 'html':
				return 'text/html';
			case 'multipart':
				return 'multipart/alternative';
			default :
				return 'text/plain';
		}
	}

	/**
	 * Send Email
	 * 
	 * Extended email send function.
	 *
	 * @uses wp_mail() To send the email message
	 * @see https://developer.wordpress.org/reference/functions/wp_mail/ 
	 *                 For documentation on the parameters used by wp_mail().
	 * @return bool    Whether the email contents were sent successfully.
	 * @since 1.0.0
	 */
	public function send_email( $to, $subject, $message, $headers = '', $attachments = array() ){
		// Set sending property to true at beginning of send
		$this->sending = true;
		
		// Add filters prior to send
		add_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
		add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
		add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
		
		// Send the message with wp_mail()
		$return = wp_mail( $to, $subject, $message, $headers, $attachments );
	 
		/** 
		 * Remove filters after send so they don't interfere with normal 
		 * wp_mail() and can be set on a per message basis.
		 */
		remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ) );
		remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
		remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
		
		// Return result of wp_mail()
		return $return; 
	}
	
	/**
	 * Construct message bodies.
	 * 
	 * Checks the email content type for the current email being sent and 
	 * constructs the message bodies accordingly.
	 *
	 * @param array  $args {
	 *		Arguments provided by shortcode.
	 *		
	 *		@see my_multipart_email_class::multipart_shortcode();  
	 * }
	 * @param string $message_body The message to be sent.
	 * @return bool success True on successful construction of message bodies.
	 * @since 1.0.0
	 */
	private function construct_message_bodies( $args, $message_body ){
		// Setup message bodies based on email content type
		switch ( $this->email_content_type ) {
			case 'html':
				// Check html body constructs ok
				$check = $this->construct_html_body( $args, $message_body );
			break;
			case 'multipart':
				// Check html body constructs ok
				$check = $this->construct_html_body( $args, $message_body );
				
				// If html body constructed ok, construct plain body
				if( $check ){
					// Check plain body constructs ok
					$check = $this->construct_plain_body( $args, $message_body );
				}
			break;
			default:
				// Check plain body constructed ok
				$check = $this->construct_plain_body( $args, $message_body );
			break;
		}
		
		return $check;
	}
	
	/**
	 * Construct html body.
	 *
	 * Checks for the existence of the required html template for the
	 * current email being sent and constructs the html message body.
	 *
	 * @param array $args {
	 *		Arguments provided by construct_message_bodies() function.
	 *		
	 *		@see my_multipart_email_class::construct_message_bodies();  
	 * }
	 * @param string $message_body The message to be sent.
	 * @return bool success True on successful construction of html message body.
	 * @since 1.0.0
	 */
	private function construct_html_body( $args, $message_body ){
		// Check that our theme has an HTML email template
		$template = locate_template( 'template-parts/emails/html/body.php' );
		
		if( empty( $template ) ){
			// Return false back to requesting function
			return false;
		}
		
		// Construct the email subject from the arguments provided
		$subject = $args['subject'];
		
		// We have a template and our variables set, start output buffer
		ob_start();		
						
		// Get template file to use from theme
		include( $template );
		
		// Save template output to class property  
		$this->html = ob_get_contents();
		
		// Clean output buffer
		ob_end_clean();
		
		// Return success
		return true;
	}
	
	/**
	 * Construct plain body.
	 *
	 * Checks for the existence of the required plain text template for the
	 * current email being sent and constructs the plain text message body.
	 *
	 * @param array $args {
	 *		Arguments provided by construct_message_bodies().
	 *		
	 *		@see my_multipart_email_class::construct_message_bodies();  
	 * }
	 * @param string $message_body The message to send.
	 * @return bool success True on successful construction of plain text message body.
	 * @since 1.0.0
	 */
	private function construct_plain_body( $args, $message_body ){
		// Check that our theme has a plain text email template
		$template = locate_template( 'template-parts/emails/plain/body.php' );
		
		// Strip any tags found in message body
		$message_body = wp_strip_all_tags( $message_body );
		
		if( empty( $template ) ){
			// Return false back to requesting function
			return false;
		}
		
		// Construct the email subject from the arguments provided
		$subject = $args['subject'];
				
		// We have a template and our variables set, start output buffer
		ob_start();		
						
		// Get template file to use from theme
		include( $template );
		
		// Save template output to class property 
		$this->plain_text = ob_get_contents();
		
		// Clean output buffer
		ob_end_clean();
		
		// Return success
		return true;
	}

	/**
	 * Sanitize Shortcode Atts
	 * 
	 * Provides basic shortcode attribute Sanitization. Meant for test purposes only, more in-depth
	 * sanitization may be necessary depending on use case.
	 *
	 * @param array  $atts {
	 *		Arguments provided by shortcode.
	 *		
	 *		@see my_multipart_email_class::multipart_shortcode();  
	 * }
	 * @return array $args The sanitized argument array.
	 * @since 1.0.0
	 */
	private function sanitize_shortcode_atts( $atts ){
		// Loop through array and run values through sanitization switch
		if( is_array( $atts ) ){
			foreach( $atts as $key => $value ){
				switch( $key ){
					case 'to':
						$atts[ $key ] = sanitize_email( $value );
					break;
					default:
						$atts[ $key ] = sanitize_text_field( $value );
					break;
				}
			}
		}
		
		return $atts;
	}

	/**
	 * Multipart Shortcode
	 * 
	 * Controls the test sending of a multipart email. This functionality is meant for 
	 * test purposes only to show the capability of WordPress to send multipart emails.
	 * This method should not be used in a production environment.
	 *
	 * @param array $atts {
	 *	 @param string $to           The email address to send an email to.
	 *	 @param string $subject      The subject of the email.
	 * 	 @param string $type         The type of email to send. Accepted types are plain,
	 *                               html and multipart.
	 * }
	 * @param string  $message_body  The message to send in the body of the email.
	 * @return string $output        The output to show on the page on success or failure of the
	 *                               shortcode function.
	 * @since 1.0.0
	 */
	public function multipart_shortcode( $atts, $message_body ){
		// Get shortcode attributes
		$args = shortcode_atts( array(
			'to'      => 'me@myemail.com',
			'subject' => 'My Default Email Subject',
			'type'    => 'plain'
		), $atts );
		
		// Sanitize the shortcode attributes
		$args = $this->sanitize_shortcode_atts( $args );
		
		// Sanitize $message_body
		$message_body = wp_kses( $message_body, array( 'br' => array() ) );
		
		/** 
		 * Set the property for email content type based on the value provided in the shortcode
		 * and construct the required message bodies accordingly.
		 */
		switch ( $args['type'] ){
			case 'html':
				$this->email_content_type = 'html';
			break;
			case 'multipart':
				$this->email_content_type = 'multipart';
			break;
			default:
				$this->email_content_type = 'plain';
			break;
		}
		
		// Construct message bodies
		$check = $this->construct_message_bodies( $args, $message_body );
		
		// Check that message bodies have been constructed successfully and, if so, send email
		if( $check ){
			// Send relevant message body
			switch ( $args['type'] ){
				case 'html':
					$send = $this->send_email( $args['to'], $args['subject'], $this->html );
				break;
				case 'multipart':
					/**
					 * In the case of multipart we still send the HTML body to the send function 
					 * as the plain text body will be added via the phpmailer_init action hook.
					 *
					 * @see my_multipart_email::enable_multipart()
					 */ 
					$send = $this->send_email( $args['to'], $args['subject'], $this->html );
				break;
				default:
					$send = $this->send_email( $args['to'], $args['subject'], $this->plain_text );
				break;
			}
			
			// Check whether the send was sucessful and set output text accordingly
			if( false !== $send ){
				$output = '<p class = "success">Your email message sent sucessfully.</p>';
			}else{
				$output = '<p class = "error">Your email failed to send. Please try again.</p>';
			}
			
		}else{
			// Set output error message
			$output = '<p class = "error">Message failed to send due to a problem with the email templates. Please check and try again.</p>';
		}
		
		echo $output;
	}

	/**
	 * Enable multipart email.
	 *
	 * @param object  $phpmailer The PHPMailer object.
	 * @return object $phpmailer The original or modified PHPMailer object.
	 * @since 1.0.0
	 */
	public function enable_multipart( $phpmailer )  {
		if ( $this->sending && 'multipart' === $this->email_content_type ) {
			$phpmailer->AltBody = $this->plain_text;
			$this->sending   = false;
		}
		
		// Fix return path
		$phpmailer->Sender = $phpmailer->From;
		
		return $phpmailer;
	}

}// End my_multipart_email_class 

/**
 * Begins execution of the plugin.
 *
 * @since    1.0.0
 */
function run_my_multipart_email() {

	$plugin = new my_multipart_email_class();

}

run_my_multipart_email();

Running a Test

In order to test your plugin with your theme email templates, activate the plugin and create a test page that contains your test shortcode with all the necessary attributes. Here’s an example:

[my_multipart_email to="me@myemail.com" subject="A test of the multipart email plugin" type="multipart"]This is my great multipart message[/my_multipart_email]

Please note: while this series explores a way in which we can configure WordPress to send multipart emails, the plugin it produces is meant for testing purposes only and not for use in a live environment.

Tags: php, plugin