Sending Emails with PHP

    Jason Pasnikowski
    Jason Pasnikowski

    We know why you’re here… you want to write a script to send emails to your friends with funny return addresses. I’m here to tell you you’re better than that. There’s so much more you could be doing with your life! But, what other reasons could you possibly have?

    Well, maybe you have a cron job that you want to inform you of issues, have a user-initiated script you’d like to be notified when run, have a “Contact Us” form that forwards messages to you, want to flex your PHP muscles and write your own web-based mail client, need to set up a confirmation-by-email script – there are plenty of other reasons to send email with PHP. Plus, it’s so easy!

    In most cases your installation of PHP will be capable of sending emails. If you are using a shared host, or if you installed PHP using a package management system like apt-get, more than likely you’re all set. You’ll really only need to worry about extra configuration if you’re compiling PHP from source or if you’re running it on Windows. In either case, there are plenty of resources available online to help you out. Because that’s all beyond the scope of this article, I’ll assume you’re set. If not, Google will be your friend.

    The Super-Basic Example

    To send just a really simple email, the code is:

    mail($address, $subject, $message);

    Really, that’s all there is to sending basic plain-text emails (if it doesn’t work for you, again check with Google to see how to configure PHP correctly).

    Now let’s see how this would look in a script. Let’s say, for example, you want the script to email you every time a query fails:

    $query = "SELECT left_arm AS arm_in, right_leg AS leg_in, front_head AS head_in FROM hokey_pokey WHERE its_about = 'all'";
    try {
        $result = $db->query($query);
        // ...
    catch (PDOException $e) {
        mail("", "Error in " . $_SERVER["SCRIPT_NAME"], $e->getMessage());

    If there’s some unforeseen error that happens with the execution of your query then you’ll get an email informing you what script had an error and what that error was.

    A Full-Blown HTML Mail Example

    Now, let’s check out a full-blown multipart mail() example that has an HTML body with a plain text alternative and a file attachment:

    $emailList = array("",
    $headers = "From: "Fluffy Mumsy" <>rn" .
       "Reply-To: weregonnaberich@shhhsecret.comrn" .
       "MIME-Version: 1.0rn" .
       "Content-Type: multipart/mixed; boundary="YaGottaKeepEmSeparated"rn";
    $subject = "Please donate all your moneys to us";
    $goodAttachment = chunk_split(base64_encode(file_get_contents( "")));
    $body = "--YaGottaKeepEmSeparatedrn" .
        "Content-Type: multipart/alternative; boundary="EachEmailAlternative"rn" .
         "--EachEmailAlternativern" .
          "Content-Type: text/plain; charset="iso-8859-1"rn" .
          "Content-Transfer-Encoding: 7bitrn" .
          "You have cheap text email you have no money. Please ignore.rn" .
         "--EachEmailAlternativern" .
          "Content-Type: text/html; charset="iso-8859-1"rn" .
      <title>We need money to give you</title>
      <p>We found some <span style='color:green'>money</span>. And we need you give us money to give some you. We is good people. You can trust. Please install contact information attachment</p>
    </html>rn" .
        "--YaGottaKeepEmSeparatedrn" .
         "Content-Type: application/zip; name=""rn" .
         "Content-Transfer-Encoding: base64rn" .
         "Content-Disposition: attachmentrn" .
         $goodAttachment . "rn" .
    foreach ($emailList AS $addy) {
       $success = mail($addy, $subject, $body, $headers);
       if (!$success) {
           echo "Mail to " . $addy . " is fail.";

    Certain aspects of more typical email scripts had been simplified to make the general concept easier to grasp, though I will touch on those in this breakdown.

    First, the $emailList array is populated with some email addresses I’d like to share my message with. The array is iterated through at the end of the script and each address will be sent a copy of my email.

    Next, the $headers string is built with various mail headers. Each header in the string is separated with a CRLF (rn) per RFC 2822, the standard that defines the format of email messages.

    "From: "Fluffy Mumsy" <>rn"
    The From header specifies the email address the recipient will see the message as having come from.

    "Reply-To: weregonnaberich@shhhsecret.comrn"
    The Reply-To header is the email address to which an email reply should be sent. By making it different than the “From:” header, the chances of this email being identified as spam increases (though if this were the only thing that caused an email client to raise a red flag this message is spam then it would probably get through).

    "MIME-Version: 1.0rn"
    The MIME-Version header tells the server to expect Multipurpose Internet Mail Extensions in the body, which allows you to have a more advanced email than simple text.

    "Content-Type: multipart/mixed; boundary="YaGottaKeepEmSeparated"rn"
    The “Content-Type” header actually does two things: it indicates that there will be multiple parts in the body of various kinds, and it specifies what string is used to divide each part. This boundary string needs cannot appear anywhere else in the email or the mail client will not be able to parse the message correctly. You could use “12” as your boundary for example, though chances are that it will show up elsewhere in the message. I chose “YaGottaKeepEmSeparated”. Most people assign a randomly generated hash to serve as the boundary, like $boundary = md5(time()), since the chance of collision is very low.

    The contents of, which here happens to be located in the same directory as the script, is base64-encoded and broken into “chunks” for easier digestion by the mail client. The result is stored in $goodAttachment which will make an appearance later.

    Finally, the body of the email message is composed…

    This is the first instance of using the boundary defined earlier and says to the mail client, “hey, here’s the start of the first section of the email message.”, and it always starts with the double-dash in front of your chosen boundary string.

    "Content-Type: multipart/alternative; boundary="EachEmailAlternative"rn"
    Besides “multipart/mixed” given in the email’s headers, you can also use a Content-Type header with “multipart/alternative” within the body and with a different boundary specific to this breakdown to provide alternate formats for the message.

    This is the first instance of the nested boundary and starts the first alternate version of the message.

    "Content-Type: text/plain; charset="iso-8859-1"rn"
    This Content-Type header tells the mail client this alternative is plain text. If the client isn’t capable of displaying more complex formats, such as HTML, then it will use this version of the message.

    "Content-Transfer-Encoding: 7bitrn"
    The Content-Transfer-Encoding header specifies the encoding scheme used in the message. For historical reasons, “7bit” is the default value and so this could be omitted. I included it so just so you’ll be aware of it.

    "You have cheap text email you have no money. Please ignore.rn"
    This is the text-only version of the message which people using non-HTML-capable readers will see.

    The end of the first alternative has been reached, and you’re ready to start the next alternate version.

    "Content-Type: text/html; charset="iso-8859-1"rn"
    This Content-Type header informs the client this version is formatted as HTML, and the set of characters that is used.

    "<html> ... </html>rn"
    Notice that this version, besides the inclusion of HTML tags, has content that is considerably different than the plain text version. Some spam filters may view this as one more reason to block me message from getting to the inbox.

    This is the multipart/mixed boundary, indicating that you’ve reached the end of the message body section with all its alternatives.

    "Content-Type: application/zip; name=""rn"
    The Content-Type header signals the next part of the email is an attachment (the file), and that it is ZIP file.

    "Content-Transfer-Encoding: base64rn"
    7bit encoding restricts characters to seven bits and might not be able to faithfully represent all the necessary binary characters for the ZIP file, which was why the file was base64-encoded and chunked. The Content-Transfer-Encoding header here lets the client know how to decode the attachment file.

    "Content-Disposition: attachmentrn"
    The Content-Disposition header details how content should be presented; there are two possible values: attachment and inline. While it would hardly make sense to display a ZIP file inline element in a message, it comes in handy to embed images.

    $goodAttachment . "rn"
    The contents of the attached file is simply dumped into the mix.

    This is the final boundary declaring that nothing more shall follow by ending with one last set of double-dashes.


    There you have it! You’ve seen how to send super-basic text email message, and full-fledge HTML emails with attachments. Simple emails are just a matter of calling the mail() function. For HTML messages, you need to break your email into segments using the MIME standards, divided by a boundary of your choosing. Then, you define what the content is, how it’s encoded, possibly the disposition, and then the content itself. Depending to whom you plan on sending emails, you will want to be conscientious about factors that may cause your message to be more likely flagged as spam, just in case you want to actually want to send something serious.

    Image via Photosani / Shutterstock