11
2014
Templated HTML Email with PHP using PHPMailer
Today we’re going to talk about sending amazing, templated, parsed HTML emails using PHP and the PHPMailer class. I know it sounds trivial- “Oh sure! Let’s send some messages!”, however, I do get a ton of requests for assistance when dealing with email templates and parsed content within those templates, so I figured this would be a good thing to write about!
Background
When I refer to email templates, I’m referring specifically to chunked, individual files that provide different portions of the content, much like in a MVC or normal web application. The main reasons that putting this into practice when sending HTML emails with PHP is:
- Executing PHP variables through the inclusion of files that are not parsed, but rather are simply included for use as a string can be problematic
- Creating consistency and enabling easy changes when containing emails to a single file without any separation is a huge pain in the ass, and even more-so after the styles have all be inlined!
- Providing support for multiple email types, where 99% of the time the only thing that changes is the inner message body or inner message body structure is redundant and prone to inconsistencies
So in this article, I’ll walk you through how to separate, tag, parse, and send some pretty spectacular HTML emails with PHP (using PHPMailer because I like it).
Sidenote: PHPMailer is not required for this to work, Chris Coyier from Css-Tricks has this great article, as well as from the official PHP documentation
Getting Started
For this demo, I’m using a template from the super-duper Zurb Ink (responsive HTML email templates), which you can see in /email-templates/basic-raw.html.
I’ve also included a fresh copy of PHPMailer’s core file (also available at https://github.com/PHPMailer/PHPMailer) in the /phpmailer/ directory.
So to get started, we’re going to create a directory structure that looks like the following (where anything ending or beginning in a “/” is a directory):
index.php
phpmailer-config.php
/phpmailer/
/email-templates/email-header.php
/email-templates/email-body.php
/email-templates/email-footer.php
And our email is going to end up looking like the pic below (I am aware that it isn’t the most beautiful email you’ve ever seen, this is just a demonstration ;))
Separating and templating the Emails
Since HTML email can get pretty nasty (thank you email clients from 1990), I personally prefer to use a NON-inlined HTML file to get the general structure in place first. Once you have something that you don’t hate, the next step is to plan out what content will need to change on a per-message basis.
In my message, the header, footer, and contact info are never ever going to change, so all I’m going to do is replace my message heading, and my message body with KNOWN tags in curly brackets (“{like_so}”).
Tip of the day: If you plan on using a lot of different tags, make a list of what they are, and where they go in your message to save yourself from the email monster!
I know that I’d like the subject of my message to also be the heading in the message body, so I’ll go ahead and replace the actual text from my template with the {message_subject} tag:
<h1>
{message_subject}
</h1>
And the message body is pretty simple since I’d like to be able to style that and manage it on a per-email basis, which leaves:
<p>
{message_body}
</p>
You can see this most easily in the basic-raw.html file provided in the download
The send_message() Function
Before I go into detail with the minutia of creating a basic HTML form that posts to index.php, and before we get much further, this is the really super duper important part. Our send_message function is the bread and butter of the operation here, and it handles quite a few things for us, making it 1,000X easier to use this reliably, and to change/adjust/debug as needed.
The send_message function performs the following operations:
- Includes PHPMailer
- Retrieves the contents of our email templates
- Replaces our curly tags with the actual content
- Makes a plain text version
- Sends our awesome message
I’ve added all of the necessary code to use SMTP instead of sendmail in this function between lines 34 and 47, and these are only activated if you define SMTP credentials
All of the PHPMailer config options are laid out on the github page here, so I’m not going to dive too deep into that.
We’re only going to pass a few options to the send_message function (though feel free to get all sorts of crazy with this- I usually do!)
send_message( $from, $to, $subject, $message_content, $logo )
You’ll notice that I haven’t set any of these parameters to be optional- though the $logo parameter could easily have a fallback and not be required, so when we actually call up our send_message function, we’ll just pass the direct parameters from our form $_POST values to it:
//Assigning a picture for {logo} replacement
$logo = 'https://www.phpdevtips.com/wp-content/uploads/2013/06/dev_tips-2.png';
//Send the message assigned to a var so we can output
$status = send_message( $_POST['sender'], $_POST['email'], $_POST['subject'], $_POST['message'], $logo );
Parsing the email templates
Within our crafty little function, before we actually process the message content that was sent, we’ll go ahead and grab the contents of our desired template parts, and assign the contents to a variable called “$message”:
//Add the message header
$message = file_get_contents( 'email-templates/email-header.php' );
//Add the message body
$message .= file_get_contents( 'email-templates/email-body.php' );
//Add the message footer content
$message .= file_get_contents( 'email-templates/email-footer.php' );
This next part has only 3 parameters to replace, and you’ll see they’re all encapsulated with curly brackets for a bit of reliability:
- {logo}
- {message_subject}
- {message_body}
We’ll run those through preg_replace using array_keys and array_values in order to make this as easily extendible as possible:
//Replace the codetags with the message contents
$replacements = array(
'({logo})' => $logo,
'({message_subject})' => $subject,
'({message_body})' => nl2br( stripslashes( $message_content ) )
);
$message = preg_replace( array_keys( $replacements ), array_values( $replacements ), $message );
Take all of our super fun HTML email business, and make a plain text version (for those of you who for some reason use a blackberry):
//Make the generic plaintext separately due to lots of css and tables
$plaintext = $message_content;
//Strip all the tags EXCEPT headings and paragraphs
$plaintext = strip_tags( stripslashes( $plaintext ), '<p><br><h2><h3><h1><h4>' );
//Replace all the beginnings of headings and paragraphs with newlines
$plaintext = str_replace( array( '<p>', '<br />', '<br>', '<h1>', '<h2>', '<h3>', '<h4>' ), PHP_EOL, $plaintext );
//Remove all the endings of headings and paragraphs
$plaintext = str_replace( array( '</p>', '</h1>', '</h2>', '</h3>', '</h4>' ), '', $plaintext );
//Decode all the HTML and remove any leftover slashes
$plaintext = html_entity_decode( stripslashes( $plaintext ) );
And then (plain-text conversions aside), set the $message variable to be explicit HTML…
$mail->MsgHTML( stripslashes( $message ) );
Usage
Most of the code below is well commented, so I won’t write too much here since you can see for yourself how this works:
if( isset( $_POST ) && !empty( $_POST ) )
{
//Handle some basic validation
$errors = array();
//Validate that we HAVE an email to send to, it isn't empty, and matches regex for email
if( !isset( $_POST['email'] ) || empty( $_POST['email'] ) || !filter_var( trim( $_POST['email'] ), FILTER_VALIDATE_EMAIL ) )
{
$errors[] = 'Please enter a valid email address';
}
//Validate that we HAVE an email to send FROM, it isn't empty, and matches regex for email
if( !isset( $_POST['sender'] ) || empty( $_POST['sender'] ) || !filter_var( trim( $_POST['sender'] ), FILTER_VALIDATE_EMAIL ) )
{
$errors[] = 'Please enter a valid sender email address';
}
//Validate that we have a subject line
if( !isset( $_POST['subject'] ) || empty( $_POST['subject'] ) )
{
$errors[] = 'Please enter a subject for your email';
}
//And validate that we actually have a message
if( !isset( $_POST['message'] ) || empty( $_POST['message'] ) )
{
$errors[] = 'A message is required to send anything';
}
//End basic validation
//If we have no errors, process the message
if( empty( $errors ) )
{
//Include the phpmailer functions
require_once( 'phpmailer-config.php' );
//Assigning a picture for {logo} replacement
$logo = 'https://www.phpdevtips.com/wp-content/uploads/2013/06/dev_tips-2.png';
//Send the message assigned to a var so we can output
$status = send_message( $_POST['sender'], $_POST['email'], $_POST['subject'], $_POST['message'], $logo );
}
//Otherwise return the errors below in the body
}
That’s it!
Extending how the templates are handled and parsed is as simple as cracking open our send_message function, adding or altering the $replacements key => value pairs to match anything you need, and/or allowing conditionals to dictate which parts of the template are included!
Just remember, all links and image sources must be full http:// style links
[dm]19[/dm]
Related Posts
5 Comments + Add Comment
Leave a comment
Recent Snippets
- htaccess : Only allow access to specific wordpress upload types if logged in
- MySQL : Query WordPress Database for Invalid Media Filenames
- PHP : Get Actual IP Address with PHP
- JavaScript : Allow Tab in Textarea
- PHP : Clean sanitized database contents on output
- htaccess : Force www. prefix in URIs
- PHP : Force File Download with Correct Content Type
- Wordpress : Disable upgrade notification
Thanks for this interesting read, have been using your db class for a couple of weeks now very nice job!
thanks a lot ,you’re tutorial really awesome and fix my problem for 2 weeks ~
Thanks for writing such a informative post , Now, I am going to make an HTML template to send bulk email to my clients for best wishes, reminders and so on …
Thank you. This works beautifully. I was worried about ({logo}), thinking that it should be {logo}. But it works fine just the way you have it.
Thank you
Thanks for sharing this. It was really useful!