Handling mail and mime in PHP using the Zend Framework

Handling mail is a very common requirement in web applications. Even the most basic sites usually have a contact form that sends a mail through the server instead of putting a contact mail address for spam-spiders to find. Using PHP’s built in function (aptly named mail() ) is relatively straightforward – until you need slightly more advanced features, such as adding and encoding email headers or sending multiple mails efficiently.

Fortunately, the Zend Framework comes with a very capable mail component called Zend_Mail. Zend_Mail abstracts some of the more tedious aspects of mail handling with PHP, including:

  1. Adding and encoding email headers
  2. Protection against header injection
  3. Multiple mail transports (SMTP is useful for sending multiple mails)
  4. Creating and handling mime-compliant multipart messages
  5. Composing HTML emails
  6. Reading mail boxes

Sending mails

Using Zend_Mail is extremely straightforward: (Taken from the ZF documentation)

 require_once 'Zend/Mail.php';
$mail = new Zend_Mail();
$mail->setBodyText('This is the text of the mail.'); //Alternatively using setBodyHtml for HTML messages
$mail->setFrom('somebody@example.com', 'Some Sender'); //Adds a 'from' header
$mail->addTo('somebody_else@example.com', 'Some Recipient');
$mail->setSubject('TestSubject');
$mail->send();

Simple.

For sending multiple mails it is better to use the SMTP protocol for performance reasons. Doing this manually with PHP is quite elaborate however, but with Zend_Mail it is almost as simple as before:

 require_once 'Zend/Mail.php';

// Create transport
require_once 'Zend/Mail/Transport/Smtp.php';
$transport = new Zend_Mail_Transport_Smtp('localhost');

// Sending out multiple mails at once
for ($i = 0; $i > 5; $i++) {
    $mail = new Zend_Mail();
    $mail->addTo('studio@peptolab.com', 'Test');
    $mail->setFrom('studio@peptolab.com', 'Test');
    $mail->setSubject('Demonstration - Sending Multiple Mails per SMTP Connection');
    $mail->setBodyText('...Your message here...');
    $mail->send($transport); //Using the SMTP transport for sending the mail
}

* SMTP connections can be secured with SSL or TLS for sending sensitive mails.

This procedure reuses the SMTP connection for the lifetime of the entire process which is much better than sending out mails using the mail() function.

Reading mail boxes

Reading mail boxes is similarly simple. Zend_Mail supports POP3 and IMAP for remote connections, and Mbox and Maildir for local connections (mail boxes residing on the same server). Sample usage:

$mail = new Zend_Mail_Storage_Pop3( //Configuring with host and credentials
	array('host' => 'example.com',
	      'user' => 'test',
	      'password' => 'test'
	)
);

echo $mail->countMessages() . " messages found\n"; //Outputting message count in mail box
foreach ($mail as $message) { //Outputting message subjects
    echo "Mail from " . $message->from . ": " . $message->subject . "\n";
}

Using the different storage options is very similar. Messages returned are instances of the Zend_Mail_Message class, which provides an API for manipulating mail messages.

Advanced Usage

Working with multipart MIME messages is very tedious. The MIME specifications are rather complex and MIME messages aren’t very readable to humans, and therefor we would like to be able to use Zend_Mail_Message API to reduce the pains of dealing with MIME messages. There are several general cases when working with MIME messages:

  1. Working with messages which are stored as strings in a database
  2. Working with messages which are read from files
  3. Working with incoming messages directly (from an input stream)

Zend_Mail_Messsage can receive either a raw message string in its constructor, a file name or a file handle. Examples of instancing a Zend_Mail_Message object with all three:

//From a raw string
$message = new Zend_Mail_Message(array('raw' => $rawMessage)); 

//From a file name
$message = new Zend_Mail_Message(array('file' => '/path/to/mail/message'));

//From a file handle
$handle = fopen('/path/to/mail/message');
$message = new Zend_Mail_Message(array('file' => $handle));

The last one might seem a bit redundant. However it opens up creative ways to intercept mail directly using the input wrapper:

$handle = fopen("php://stdin", "r");
$message = new Zend_Mail_Message(array('file' => $handle));

The tricky part is directing the mail message stream into the script. You can read more about that in this nice tutorial from evolt.

Some minor bugs and fixes

Nothing is perfect however. Current version of Zend_Mail (shipped with the Zend_Framework ver. 1.5.2) has a buggy implementation of the headers encoding. This will become noticeable when working with UTF encoded mails. Fortunately there is a quick fix for that posted in the ZF issue tracker (and hopefully will be tested and integrated in the next release).

To know when the next article is published, please subscribe to new articles using your Email below or follow me on Twitter.

Subscribe to Blog via Email

Enter your email address to receive notification about new posts.