View Source

<p>Zend_Mail was initially a very popular component in ZF1, as it provided a fluent interface for creation of mail messages, abstraction around the transport used to send messages, and reasonable attachment support.</p>

<p>Several problems present themselves, however. </p>

<li>The relationships between classes are inverted. Current, Zend_Mail is capable of sending itself, and either requires that you provide a default transport, or pass a transport to the send() method. This makes cloning of messages expensive, sending of many messages at once expensive, and muddies configuration of mailers. Ideally, the mail transport should be the base object, and mail messages should be passed to the transport to send.</li>
<li>Message assembly has largely occurred in the transports; this should be the responsibility of the Message itself.</li>
<li>Numerous issues exist regarding MIME encoding as well as unicode support of mail messages.</li>
<li>It's relatively difficult to provide non-standard headers to a mail message.</li>
<li>Attachments are not intuitve.</li>

<h2>Proposed Architecture</h2>

<p>I propose the following core architecture for the Mail component:</p>

<li><strong>Mailer:</strong> an interface with a single method, <code>send($message)</code>. <code>$message</code> could be either a single message of type <code>Message</code>, or a collection of messages.
<li>We would supply mailers for Sendmail, SMTP, and the filesystem at the minimum</li>
<li><strong>Message:</strong> a class that aggregates the following:
<li>To, From, B/CC, and Reply-To Addresses</li>
<li>Arbitrary Headers</li>
<li>De/Serialization methods (specifically, from/to string)</li>
<li><strong>Address:</strong> a value object containing a <strong>name</strong> and <strong>email</strong></li>
<li><strong>Header:</strong> a value object containing a <strong>fieldName</strong> and <strong>fieldValue</strong></li>
<li><strong>Headers:</strong> an aggregate of Header objects</li>
<li><strong>Attachment:</strong> a value object containing:
<li>Content (which could in turn be made up of Attachments)</li>
<li><strong>Attachments:</strong> an aggregate of Attachment objects. Additionally:
<li>A Mime boundary</li>
<li>Convenience methods such as &quot;isMultiPart()&quot; (do we have &gt; 1 attachments)</li>
<li><strong>MessageCollection:</strong> a collection of Message objects</li>

<p>The To, From, B/CC, Reply-To, and Subject values would in turn populate individual Header objects.</p>


<p>The primary unit of usage is a Mailer.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use Zend\Mail\Mailer\Smtp,
$options = new SmtpOptions(array(
'host' => 'localhost',
'port' => 25,
$mailer = new Smtp($options);

<p>You create Message objects and pass them to your mailer.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
use Zend\Mail\Message;

$message = new Message();

// Convenience:
$message->from('', "Matthew Weier O'Phinney");
// Explicit:
$from = new Address('', "Matthew Weier O'Phinney");

// Fluent API:
$body = new Attachment();
$body->setContent('Here is the invite for our meeting tomorrow.');
$invite = file_get_contents('meeting_invite.ics');

$message->to('', 'Ralph Schindler')
->subject('Meeting today')
->cc('') // omit full name
->addAttachment($invite, 'text/calendar');
$message->headers()->addHeader('X-Mailer', 'ZF2-Zend\Mail');

// Send the message


<p>The fluent API of a Message object can take arguments that can be used to create the appropriate objects, or actual concrete instances of those objects. This allows sculpting the message exactly how one wants, including nesting attachments, setting specific attachment headers, etc. </p>


<li>Easier to test base functionality of Messages, including serialization to string.</li>
<li>Easier to test discrete operations of each mailer type.</li>
<li>More flexibility in message creation.</li>

<h2>What we're not addressing</h2>

<p>A number of feature requests/concerns were raised in the <ac:link><ri:page ri:content-title="Zend Mail 2.0" /><ac:link-body>Zend_Mail 2.0 proposal</ac:link-body></ac:link>. I'm specifically not addressing several of these, as I think they fall outside the responsibilities of a mail component. As examples:</p>

<li>Templating. Since the bodies can be seeded separately, and message objects will be basically value objects, I see no value to adding templating capabilities. This sort of thing can be easily achieved in userland, and would prevent coupling of the Mail component with the View layer.</li>
<li>Inline images. I'd like to address this, but I'm not sure how we should approach it, as the proposed use cases require parsing attachments for links as well as transformation of the parsed attachments.</li>
<li>CSS inlining. Same argument as for Inline images</li>
<li>Advanced IMAP/Storage operations. This proposal is really directed primarily at the generation and sending of emails, not reading of them. Such requirements should be addressed in a separate proposal.</li>

<p>The first three items feel like a fit for a filtering or event system, and we could potentially compose an EventManager into the Message and/or Mailer classes to allow such operations. However, I'd like feedback as to where in the workflow we should trigger events.</p>