[ Team LiB ] Previous Section Next Section

24.4 Email with Attachments

Sending plain email with PHP is easy. The mail function handles all the messy protocol details behind the scenes. But if you want to send attachments, you will need to dig into an RFC, specifically RFC 1341. This RFC describes MIME, Multipurpose Internet Mail Extensions. You can read it at the faqs.org site <http://www.faqs.org/rfcs/rfc1341.html>, but I'll show you a somewhat naïve implementation.

There are several example implementations to be found on the Web. Check out David Sklar's networking section <http://px.sklar.com/section.html?id=10>. Most of these put functionality into a class and attempt to incorporate every aspect of the standard. Listing 24.6 contains code that sends email with multiple attachments using two simple functions. Use this example as a basis for learning the process, and expand its functionality if necessary.

Listing 24.6 Sending attachments
<html>
<head>
<title>Listing 24-6</title>
</head>
<body>
<?php
    /*
    ** Function: makeAttachment
    ** Input: ARRAY attachment
    ** Output: STRING
    ** Description: Returns headers and data for one
    ** attachment.  It expects an array with elements
    ** type, name, and content.  Attachments are naively
    ** base64 encoded, even when unnecessary.
    */
    function makeAttachment($attachment)
    {
        //send content type
        $headers = "Content-Type: " . $attachment["type"];

        if(isset($attachment["name"]))
        {
            $headers .= "; name=\"{$attachment["name"]}\"";
        }

        $headers .= "\r\n" .
            "Content-Transfer-Encoding: base64\r\n" .
            "\r\n" .
            chunk_split(base64_encode($attachment["content"])) .
            "\r\n";

        return($headers);
    }


    /*
    ** Function: mailAttachment
    ** Input: STRING to, STRING from, STRING subject,
    ** ARRAY attachment
    ** Output: none
    ** Description: Sends attachments via email.  The attachment
    ** array is a 2D array.  Each element is an associative array
    ** containing elements type, name and content.
    */
    function mailAttachment($to, $from, $subject, $attachment)
    {
        //add from header
        $headers = "From: $from\r\n";

        //specify MIME version 1.0
        $headers .= "MIME-Version: 1.0\r\n";

        //multiple parts require special treatment
        if(count($attachment) > 1)
        {
            //multiple attachments require special handling
            $boundary = uniqid("COREPHP");

            $headers .= "Content-Type: multipart/mixed" .
                "; boundary = $boundary\r\n\r\n" .
                "This is a MIME encoded message.\r\n\r\n" .
                "--$boundary";

            foreach($attachment as $a)
            {
                $headers .= "\r\n" .
                    makeAttachment($a) .
                    "--$boundary";
            }

            $headers .= "--\r\n";
        }
        else
        {
            $headers .= makeAttachment($attachment[0]);
        }

        //send message
        mail($to, $subject, "", $headers);
    }
    //add text explaining message
    $attach[] = array("content"=>"This is Listing 24-6",
        "type"=>"text/plain");

    //add script to list of attachments
    $fp = fopen(__FILE__, "r");
    $attach[] = array("name"=>basename(__FILE__),
        "content"=>fread($fp, filesize(__FILE__)),
        "type"=>"application/octet-stream");
    fclose($fp);

    //send mail to root
    mailAttachment("root@localhost",
        "httpd@localhost",
        "Listing 24-6",
        $attach);

    print("Mail sent!<br>\n");
?>
</body>
</html>

The mailAttachment function assembles the parts that make up a MIME message. These parts are sent in the fourth argument of the mail function, which is generally used for headers. In the case of a MIME message, this area is used for both headers and attachments. After the customary From headers are sent, a MIME-Version header is sent. Unless there's only one attachment, a boundary string must be created. This is used to divide attachments from one another. We want to avoid using a boundary value that might appear in the message itself, so we use the uniqid function.

Each attachment is surrounded by the boundaries that always start with two dashes. The attachment itself is prepared by the makeAttachment function. Each attachment requires Content-Type and Content-Transfer-Encoding headers. The type of content depends on the attachment itself. If an image file is being sent, it might be image/jpg. These are the same codes discussed above with regard to the HTTP protocol. For the sake of simplicity, this function always encodes attachments using base64, which can turn binary files into 7-bit ASCII. This prevents them from being corrupted as they travel through mail servers that accept only 7-bit ASCII. As you might imagine, text files don't require encoding, and complete implementations encode attachments based on content type.

It may be instructive to see the assembled message in full. Try sending yourself a message. On a UNIX operating system, you should be able to peek at the file itself inside /var/spool/mail before reading it, or perhaps inside ~/Mail/received afterward.

    [ Team LiB ] Previous Section Next Section