loading...
Cover image for How to make the PHP mail() function awesome

How to make the PHP mail() function awesome

lutvit profile image Vitali Lutz ・5 min read

All PHP developers know that PHP has a built-in mail() function for sending emails. No question, one can use this function to send messages through the contact form, send plain text email notifications with the PHP app and so on.

Unfortunately, working on projects in the past which did require advanced mail functionality, I've faced four main problems with the PHP's built-in mail() function:

  • It lacks UTF-8 support
  • It lacks HTML support
  • You can't define the name of the sender
  • You can't define/change the email address of the sender

In my special case, emails that have been sent often contained German Umlauts like ö, ä, ü, and ß. If you send an email with mail() containing one of those special characters, you will get ugly results like this:

PHP mail wrong content encoding

If you look into the email headers, you will notice that there is either any information about the Content-Type nor the Content-Transfer-Encoding, so it shouldn't be surprising seeing wrong displayed characters in the email.

PHP mail wrong encoding for subject

Notice how broken the subject line looks like in the email source code. I'm pretty sure a lot of email clients will have problems to display the email subject correctly.

What can we do to solve the encoding problems?

Additional headers – let's make PHP mail() awesome!

Now the fun part starts!

For our rescue, the mail() function comes with a fourth parameter, named $additional_headers. This parameter allows us to define custom email headers and pass them through as string (or as an associative array, since PHP 7.2.0) to the function.

This means we can define our own headers for the email and fix practically the most known drawbacks of the PHP's mail() function.

Let's get into coding, I can't hold my hands from the awesome Atom Editor any longer, and extend the mail() function for UTF-8 support. We also will be using Base64 encoding for the email text body to ensure a robust character encoding. And we will also encode the subject line and some other header parts.

// Force PHP to use the UTF-8 charset
header('Content-Type: text/html; charset=utf-8'); 

// Define and Base64 encode the subject line
$subject_text = 'Test email with German Umlauts öäüß';
$subject = '=?UTF-8?B?' . base64_encode($subject_text) . '?=';

// Add custom headers
$headers = 'Content-Type: text/plain; charset=utf-8' . "\r\n";
$headers .= 'Content-Transfer-Encoding: base64';

// Define and Base64 the email body text
$message = base64_encode('This email contains German Umlauts öäüß.');

// Send mail with custom headers
if (mail('recipient@domain.com', $subject, $message, $headers)) {
    echo 'Email has been sent!';
} else {
    echo 'Oops, something gone wrong!';
}

Let's send a test email and take a look into the email headers:

PHP mail encoding fixed

As you can see, the subject line is encoded with the combination of UTF-8 and Base64. This ensures that the email subject will be displayed on most devices and clients correctly.

The PHP's mail() function has properly applied our custom headers for Content-Type and Content-Transfer-Encoding, which is really great so far. Now the email body text is encoded with the Base64 algorithm and we can use all characters supported by UTF-8 inside the email body.

One important task is finished. We have fixed the encoding part.

Next, we can go further and add some more headers to get full control over the email's recipient and sender information. If you like you can even use the Reply-To header to define freely the contact information for email replies.


...

// Define the sender
$sender = 'sender@domain.com';
// or:
// $sender = '=?UTF-8?B?' . base64_encode('John Döe') . '?= <sender@domain.com>';

// Define the recipient
$recipient = 'recipient@domain.com';
// or:
// $recipient = '=?UTF-8?B?' . base64_encode('Margret Müller') . '?= <recipient@domain.com>';

// Add custom headers
$headers = 'Content-Type: text/plain; charset=utf-8' . "\r\n";
$headers .= 'Content-Transfer-Encoding: base64' . "\r\n";
$headers .= 'From: ' . $sender;

...

mail($recipient, $subject, $message, $headers);

Important: You must always pass the recipient value as the first parameter to the mail() function!

As soon as you check the email headers again you will notice that the new headers have been applied to the email too. If you define custom contact information make sure you use the right string format for the pairs name and email address.

Please notice in the code example above that we have encoded the sender's name with Base64. This needs to be done only for the name but not for the email address.

You have to agree with me that we have extended the mail() function in the right direction. The last thing one can wish is HTML support. This part is very easy to do, just modify the header corresponding for the Content-Type:

$headers = 'Content-Type: text/html; charset=utf-8' . "\r\n";

You don't need to do anything else.

As email body you can now use text and HTML, which, by the way, will be Base64 encoded. The result is a modern-looking email containing all sorts of fancy HTML formatting.

For best practice and most email client support, I would recommend using inline CSS styles instead of defining them in the <head> section of the email wrapped inside the <style> block.

Conclusion

I appreciate that the PHP developers gave us the mail() function and enabled us to extend it with custom headers. By using the $additional_headers parameter of the mail() function we got great power over the email headers.

But, with great power comes great responsibility, as we all know! Modify the headers only if you really need to. In the worst case, you can break the whole mailing functionality of your app – you don't want it to happen.

Workarounds and examples used in this article I use in a slightly modified version on my website NewsletterMarketer and some of my other websites for the contact form, transaction emails and to send bulk email newsletters.

I also maintain a Github repository for a custom PHP function, that is basically a wrapper for the mail() function and has already implemented all fixes shown in this article and some additional great features.

For all small email tasks like contact forms and all kinds of transaction emails I always use my customized mail() function. But, if I need to use external mail servers and send emails through SMTP I mostly use the mature, production-ready PHP class called PHPMailer.

I hope you can now enjoy the PHP's mail() function as I do :)

Discussion

pic
Editor guide