This is going to be a continuation of the Website Change Request Form demo we’ve been using around here for a while. If you need to catch up, first I talked about it, then I built it, then I screencasted it, then I secured it. Throughout all of this, the end result has been a boring text-only email that gets sent to a single email address.
We’re going to improve that output, and make the email into a nicer looking HTML-formatted email.
It’s Not Much Different Than Text Email
Sending HTML Email through PHP uses the exact same mail function as text email:
mail($to, $subject, $message, $headers);
The last parameter, the headers, are optional for the function but required for sending HTML email, as this is where we are able to pass along the Content-Type declaration telling email clients to parse the email as HTML.
In fact, the headers area gives us the opportunity to do lots of important email functions. This is where we can set the From: and Reply To: settings if need be, as well as CC and BCC other recipients (Hey, a checkbox for CC’ing yourself would be a cool feature to add!). Here is the code used for the new and improved HTML-Sendin’ Website Change Request Form:
$to = '[email protected]';
$subject = 'Website Change Request';
$headers = "From: " . strip_tags($_POST['req-email']) . "\r\n";
$headers .= "Reply-To: ". strip_tags($_POST['req-email']) . "\r\n";
$headers .= "CC: [email protected]\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=ISO-8859-1\r\n";
Now We Can Use HTML Tags
The message parameter (a big string we pass to the mail function with the body of our email), can now have HTML tags in it. For example:
$message = '<html><body>';
$message .= '<h1>Hello, World!</h1>';
$message .= '</body></html>';
Here is whole shebang, to create the table of data as shown above:
$message = '<html><body>';
$message .= '<img src="//css-tricks.com/examples/WebsiteChangeRequestForm/images/wcrf-header.png" alt="Website Change Request" />';
$message .= '<table rules="all" style="border-color: #666;" cellpadding="10">';
$message .= "<tr style='background: #eee;'><td><strong>Name:</strong> </td><td>" . strip_tags($_POST['req-name']) . "</td></tr>";
$message .= "<tr><td><strong>Email:</strong> </td><td>" . strip_tags($_POST['req-email']) . "</td></tr>";
$message .= "<tr><td><strong>Type of Change:</strong> </td><td>" . strip_tags($_POST['typeOfChange']) . "</td></tr>";
$message .= "<tr><td><strong>Urgency:</strong> </td><td>" . strip_tags($_POST['urgency']) . "</td></tr>";
$message .= "<tr><td><strong>URL To Change (main):</strong> </td><td>" . $_POST['URL-main'] . "</td></tr>";
$addURLS = $_POST['addURLS'];
if (($addURLS) != '') {
$message .= "<tr><td><strong>URL To Change (additional):</strong> </td><td>" . strip_tags($addURLS) . "</td></tr>";
}
$curText = htmlentities($_POST['curText']);
if (($curText) != '') {
$message .= "<tr><td><strong>CURRENT Content:</strong> </td><td>" . $curText . "</td></tr>";
}
$message .= "<tr><td><strong>NEW Content:</strong> </td><td>" . htmlentities($_POST['newText']) . "</td></tr>";
$message .= "</table>";
$message .= "</body></html>";
I think that looks A LOT nicer. And since this email is (theoretically) coming directly to you, isn’t it nice to know that it will be formatted to be easy on the eyes?
Concerns
Some people just absolutely hate HTML email. For one, it can be a security risk as it’s possible to run JavaScript in them in some email clients which can be problematic. HTML emails also have a habit of being more easily caught in Spam filters. I think it’s less of a concern here as this email is essentially being created BY you FOR you.
Demo
I updated the demo and download to use the new HTML email format.
There is likely to be another one or two demos on this form yet to come. I would at least like to do one on writing the data to a database before sending the email. Email can be such a fragile thing, that saving the data to a DB first is surely a smart move.
With HTML E-Mails I go the extra route of having a template file with the HTML and necessary variable markers. This way my HTML is separate from my PHP, and it makes it that much easier to make simple changes to the e-mails look.
I cannot tell you how many times I’ve gone to edit HTML within a PHP variable or echo statement only to forget to add a slash before a double-quotation and then get an annoying error out of it all.
use a single quotation mark – always works fine for me
Me 2
heredoc Works even better, although it is nice to have it in a separate file, but then just set up some simple replace strings for variables.
http://php.net/manual/en/language.types.string.php
I was all excited about nowdoc, but it still isn’t out yet. At this rate, it won’t go stable until PHP 6.
i want to send an email through php but it does not work i dont know what to do please help.
Very good article, I was actually just asking someone on HTML e-mail tips last week. thanks!
Nice one Chris. Just one little addition: You really should look into the PHPMailer library: It’s better and easier than the “normal” mail() function.
You can simply create a new instance:
$var = new phpMailer();
Set it to HTML
$var->useHTML(true);
Than add the body:
$var->body(); // Mail stuff here
And send it!
$var->send();
(This is just from the top of my head, the function names could be different)
I used to use a script similar to Chris’ but now I use zend’s validation class to validate the email and the mail class to send the email, it cares of everything for me, error handling etc. You can even specify a text only version, it’s pretty simple.
This is how short it is.
require_once ‘Zend/Mail.php’;
$mail=new Zend_Mail();
require_once ‘Zend/Validate/EmailAddress.php’;
$validator=new Zend_Validate_EmailAddress();
if($validator->isValid($_POST[’email’]))
{
// text only version, so strip the tags
$mail->setBodyText(strip_tags($_POST[‘message’]));
// html version
$mail->setBodyHtml($_POST[‘message’]);
$mail->setFrom($_POST[’email’],’sender name’]);
$mail->addTo(‘[email protected]’,”receiver’s name”);
$mail->setSubject(‘Subject goe heere’);
// send email
$mail->send();
}
else
{
foreach($validator->getMessages() as $errorMessage)
{
echo “$errorMessage”;
}
}
The errors tell the user exactly what is wrong with the email if it’s not valid. You can strip tags you don’t want like javascript tags and/or links with the strip_tags function.
Robert I like your idea but am not sure exactly how to implement, if you have an email template in html how do you include it in the php file and pass variables to it?
Any links/guides that you could point me towards?
The function I use simply loads a file, and then replaces variables in that file (in my case, variables are encased in brackets like {TITLE} or {BODY} ) with the specified results (ex. {TITLE} => $foo or {TITLE} => “My Homepage”.
Since I tend to output as $variableName = callToMyFunction(“filename.tmpl”); $variableName already contains the entire bit of HTML… it’s that simple.
Kudos for using “its” vs. “it’s” as the focus of your demonstration.
It is the bane of so many otherwise-smart developers.
http://tr.im/itsits
Erm you get a lot of spam right?
strip_tags is suppose to sanitize the From: address etc right? Wrong! Remember how SMTP works, \n\r can inject headers, you need to filter those. A single dot on a line can end the DATA transaction. You need to filter those.
I suggest you read the notes on the manual page:-
http://uk.php.net/manual/en/function.mail.php
Awesome article. I’m just about to make a contact form, so this will be very helpful indeed.
I agree with Marco. People should consider using 3-rd party libs such as PHPMailer or Swift, rather then pure mail function from php, especially when you want to use SMTP Auth. But anyway good example for beginners.
Could of used this like 3 days ago. :)
Great article! I have a question though, how would you set it up to send both HTML email and plain-text in the same message. I know it can be done, just never figured it out.
Check out this tutorial http://www.sitepoint.com/print/advanced-email-php/ or simply use phpmailer.
You have to set the vars $mail->Body (html text) and $mail->AltBody (plain text) to send multipart mails.
God article. However, it is prone to email injection
Having strip_tags here:
$headers = “From: ” . strip_tags($_POST[‘req-email’])
does not help much.
It only strips html tags. not \r\n
You have to filter req-email for \r\n
If you have a bit of replacement code for that area, I’d be grateful, and I’ll update the example and download.
Like this:
$req-email = $_POST[‘req-email’];
$req-email = preg_replace(‘=((||0x0A/%0A|0x0D/%0D|\\n|\\r)\S).*=i’, null, $req-email);
$headers = “From: $req-email\r\n”;
$headers .= “Reply-To: $req-email\r\n”;
preg_replace(‘/[^a-zA-Z0-9\._\[email protected]]/’,”,$_POST[‘req-email’])
Would replace any characters not allowed in an e-mail address. However, I’d still recommend doing a more comprehensive preg_match to determine if the structure is of a proper email format, too.
I couldn’t get either of these to work. I copied and pasted and converted the stupid wordpress curly quotes to straight quotes, but no dice. Maybe something got messed up in the posting of them. I don’t know enough about RegEx to debug myself, but if I learn I’ll attempt to get this integrated.
I actually just posted something similar to this, except I covered sending a message with both HTML and plain text versions.
I also did email address validation to make sure nothing explodes. The regex there works.
LINK
Good article!
Excellent.
For the record, I stole this an implemented it so the From: business should be all nice and sanitized and safe now.
I concur with other comments and recommend phpMailer. MIME messages look nice but what if the recipient doesn’t have MIME, or has chosen to not to view MIME. I believe good practice is to use MIME & Text versions of an email, which phpMailer simplifies greatly.
However, Microsoft back-tracked on HTML email and now uses Word as a rendering vehicle for email which is far more restrictive. If a large number of email recipients are corporate people you’d be wise to read Campaign Monitor’s musings on the subject.
Personally, I’ve now dropped back to text-only emails as this was the original intention of the developers. The email is smaller, more likely to render properly and if I *really* need to impress somebody, I just link to a web version of the broadcast. So PHP’s mail function and a regex to replace templated variables works for me every time.
Don’t forget context here. In his specific example, he’s generating an HTML email to himself, so there is no reason to code for all receiving platforms.
I’ve been doing something that I think works well to prevent your email client from marking emails from your site as spam.
Simply set the “From” header to something like ‘[email protected]’ and then set the “Reply-To” header to the user’s email like normal.
Now you can just add ‘[email protected]’ to your contacts and you’ll always get your emails, but can still click reply and it will be sent to the correct email.
Never ever set the from-header to the users email adress! You may get blocked by spam filters due to SPF (http://en.wikipedia.org/wiki/Sender_Policy_Framework). The worst thing would be your ip blacklisted.
http://www.html-form-guide.com/email-form/php-script-not-sending-email.html
The article at the link above explains it very nicely. In short, yes, set “From” to an address on a domain the server is allowed to send for. Set “Reply-To” to whatever you want–like the address of the form submitter.
Haven’t even read this through yet but I know it’s going to be great. Thanks in advance Chris!
I only used plain version so far, but its time for change ;)
I’m sorry, but I don’t really see what’s new here? HTML emails have been around for ever…
Also, in your mail you have an image from your domain (“<img src=’https://css-tricks.com/ex…'”), you should note and warn people it will be blocked by most modern clients.
It’s rather worrying that people are writing tutorials and yet seem incapable of actually writing decent code. I had thought (or at least hoped) that all programmers with half a brain had a decent understanding of email headers by now… anyway, I suppose the actual tutorial will be quite useful for a lot of beginners – though showing them how to make spam vulnerable forms !== sensible.
Hey Chris,
Nice article but I think you should also mention that you shouldn’t use divs and floats in email templates and instead use tables and inline styles. I know that’s what you have used but I’m just saying that you should mention to your readers why you are doing this.. which is because all our email clients are still stuck in the stone age :)
Thanks for this comment. I’ve been struggling to format my e-mail with divs. And, Chris, thanks for the post.
I’m glad I could help :)
$message = '';
$message .= '';
$message .= '';
$message .= "Name: " . strip_tags($_POST['req-name']) . "";
$message .= "Email: " . strip_tags($_POST['req-email']) . "";
$message .= "Type of Change: " . strip_tags($_POST['typeOfChange']) . "";
$message .= "Urgency: " . strip_tags($_POST['urgency']) . "";
$message .= "URL To Change (main): " . $_POST['URL-main'] . "";
$addURLS = $_POST['addURLS'];
if (($addURLS) != '') {
$message .= "URL To Change (additional): " . strip_tags($addURLS) . "";
}
$curText = htmlentities($_POST['curText']);
if (($curText) != '') {
$message .= "CURRENT Content: " . $curText . "";
}
$message .= "NEW Content: " . htmlentities($_POST['newText']) . "";
$message .= "";
$message .= "";
insted of doing this…. how about some heredoc:
$name = strip_tags($_POST['req-name']) ;
$email = strip_tags($_POST['req-email']);
$typeChange = strip_tags($_POST['typeOfChange']);
$urgency = strip_tags($_POST['urgency']);
$urlMail = $_POST['URL-main'];
$addURLS = ($_POST['addURLS'] != NULL) : "URL To Change (additional): " . strip_tags($_POST['addURLS']) . "";
$curText = (htmlentities($_POST['curText']) == NULL) : "CURRENT Content: " . htmlentities($_POST['curText']) . "" ;
$newText = htmlentities($_POST['newText']);
$message = <<<EOD
Name: $name";
Email: $email
Type of Change: $typeChange
Urgency: $urgency
URL To Change (main): $urlMail
$addURLS
$curText
NEW Content: $newText";
EOD;
Save those bits and it is easier to read and use….
Hey Chris,
Nice Explanation. I have to test it out, Hope it works fine for me :)
Thanks
Sankar
To improve Web Accessibility you may consider adding the accesskey attribute to the label tag.
I use phpmailer for this and works great. I suggest to send email in combined format (plain text/HTML).
How did you get it to work I couldn’t.
I changed the contact information over and still nothing. Any ideas?
I think there’s a problem with the URL field. I know what an URL is and how to format it, but how many of our clients know that? I think the field is to restrictive as it requires http and www to validate that field.
Wow, I just had problems with HTML mails with PHP and then I got to this site. Nice work. You helped me out with some problems!
To those recommending PHPMailer, have a look at SwiftMailer. It provides the same sort of functionality but in (in my opinion) a much better designed set of classes.
Leeeeeeeerrrrrrrrrroooooyyyyyyyyyyyy Jeeeeennnnnnnkkkkkiiiiiiiinnnnnssssss
Nothing to do with the article, just had to say it!!!
Good post, its a very simple way of formatting html emails!
Another function to check email:
http://www.phpinform.com/2009/06/17/another-function-to-check-email/
Here is a little trick I learned. If you don’t want to break your HTML up into a bunch of variables you can do this…
$html = <<< newsletter
$message…
newsletter;
mail($email, $subject, $html, $headers);
Anyone have a PHP4 version? Mail is only sent successfully when running PHP5.
How can you add PHP content to the $body then ?
I will get the title of the message from a form in a different page and send it via POST in the “send_newsletter” page.
I will then need to have the Title i wrote in the Other page displayed on the E-mails which go out.
How can that be done?
Thanks
Hi Chris,
Excellent form you’re having here.
I’ve been following it from the first version and this is quite a cool end result here.
A small mistake I spotted during customization.
in “index.php” on line 135 you forgot a period (” . “) at the second line of the $message… this is causing not to be part of the sent message.
not that the second $message is also = instead of .=.
For the rest… awesome form, best thing I’ve seen to date.
—
TeMc
Excellent, good catch, thanks. I updated the article and download.
Hey CSS-Tricks,
I’ve used this form for a freelancer’s website the other day and found myself customizing the mailoutput and thought I’d share the way I did it.
In the html e-mail for all 1-line field (like names, addresses, etc.) this:
and for the text-area’s:
stripslashes get’s right of annoying slash/’es where quotes are, which, depending on your server and framework settings are being put there automaticly.
nl2br is handy to maintain linebreaks where entered in the form textarea’s.
For non text-area’s the trim-function get’s rid of linebreaks and spaces at begin and end.
—
My 2c,
Krinkle
I am using php pear to send a mail, In this way how can i send a html formatted mail, i tried lot but all are failed. Can you help me?
Hi im new in web development, im looking for a code through which i can send mail to email id, with a specific format.
when i use html tags into mail text, these tags show as a text in mail.
help me out and tell me how i send mail using format.
regards
vikas siwach
Workd perfect!
THX
Hey Chris,
Nice Tutorial. Can you confirm whether there is a limitation to no. of characters in mail body or limited no. of rows for table in mail body while sending mails using php mail function?
If it is, then how can I increase the limit?
Is it required to table the message? (Just started studying sending email.)
Thank you very much Chris you’re Doing Great job !! :)
Hi Chris,
I spotted two typo, not very important only visible to recipient developer or
the middle man between developer and website owner, who receives these requests.
They aren’t within comment tags, neither variable names.
Line 28
IP-Adress: {$ip} \n
// Address
Line 173
$subject = ‘Website Change Reqest’;
// Request
is completely missing everywhere at all. Not sure about you guys, but I need it and use it. Plus, my preference is phpmailer as well. There is no need to waste your hours and hours and hours to come up with a function when you can just spend 5 minutes to use a perfect set of classes in phpmailer. Just my 5 cents in edgeways :-) Time becomes a way too precious when you get older, so cannot afford to waste it any more.