Ah, sending mail, the bane of everyone who has ever used the php mail() function for anything remotely non-trivial. There are lots of entries in the “helps you send mail” category in PHP userland:

While each of these will work with Solar, the new Solar_Mail and Solar_Smtp packages work “natively”, in that they support automatic configuration, locale and exception inheritance, and so on. Read on for some examples on how to use them.

A Basic Mail Message

Here’s a short example on how to build a basic message and set the recipients. In this example, I’ll send a quick note to my old friend Bolivar Shagnasty, and give him both a text and HTML version of the message.

Note that the Solar_Mail_Message set*() methods are fluent, so you can chain them together when you find it appropriate. (You can review the full set of Solar_Mail_Message methods if you like.)

$text = <<<TEXT
The quick brown fox jumps over the lazy dog.

Now is the time for all good men to come to the aid of their country.
TEXT;

$html = <<<HTML
<p>The quick brown fox jumps <em>over</em> the lazy dog.</p>

<p><strong>Now</strong> is the time for all good men
to come to the aid of their country.</p>
HTML;

$mail = Solar::factory('Solar_Mail_Message');

$mail->setCharset('utf-8')
     ->setFrom('pmjones@example.com', 'Paul M. Jones')
     ->addTo('boshag@example.com', 'Bolivar Shagnasty')
     ->addCc('nobody@example.net')
     ->setSubject('A Short Test Message')
     ->setText($text)
     ->setHtml($html);

That’s pretty easy … but is it safe?

Headers and Header Injection

Anything that ends up getting sent as a mail header, including addresses and the subject line, is sanitized against header-injection attacks by removing newlines from the header label and value. Let’s say you want to add a new custom header:

$mail = Solar::factory('Solar_Mail_Message');
$mail->setHeader('X-Custom-Header', "FoornrnAn evil message");

Under a less-secure system this might cause the header to be sent as:

X-Custom-Header: Foo

An evil message.

That’s no good – somebody just injected their own message into our email.

With Solar_Mail_Message, when the mail gets sent, that header will go out as:

X-Custom-Header: FooAn evil message

We strip the newlines in header labels and values automatically, so you should be safe against header injections. (If there are other aspects to securing against header injections I would be happy to hear them.)

Attachments

Adding an attachment to an email is pretty easy; you can use a convenience method to attach a file, or you can build it yourself.

Convenience method attachFile():

$file_path = '/path/to/file.pdf';
$mime_type = 'application/pdf';

// this will automatically set the attachment name
// to the basename() of the attached file
$mail = Solar::factory('Solar_Mail_Message');
$mail->attachFile($file_name, $mime_type);

Or you can build and add a MIME part yourself with attachPart():

$file_path = '/path/to/file.pdf';
$mime_type = 'application/pdf';

$part = Solar::factory('Solar_Mail_Message_Part');
$part->setDisposition('attachment');
$part->setFilename(basename($file_name));
$part->setContent(file_get_contents($file_name));
$part->setType($mime_type);

$mail = Solar::factory('Solar_Mail_Message');
$mail->attachPart($part);

Sending The Message

Now that we have a message to send, let’s actually get it out there. To send the message, we need to pick what kind of “transport” we’re going to use, then send the message through that transport.

Solar comes with two Solar_Mail_Transport adapters: the Phpmail one, which uses mail() internally, and an SMTP factory-adapter one, which takes a little more setup but is a lot more robust.

Easy one first: the mail()-based adapter.

$mail = Solar::factory('Solar_Mail_Message');
// .. build the message ...

// build a transport and send the message
$transport = Solar::factory('Solar_Mail_Transport', array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Phpmail'
));

$transport->send($mail);

The SMTP factory-adapter is a little more complicated, but still not too hard. We need to build an SMTP connection object, then tell the SMTP transport adapter to use it, then send the message.

$mail = Solar::factory('Solar_Mail_Message');
// ...

// build an SMTP connection object
$smtp = Solar::factory('Solar_Smtp', array(
    'adapter' => 'Solar_Smtp_Adapter_NoAuth',
    'host'    => 'mail.example.com',
));

// build a transport and send the message
$transport = Solar::factory('Solar_Mail_Transport', array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Smtp',
    'smtp'    => $smtp,
));

$transport->send($mail);

The SMTP connection object is itself a factory, and has adapters for various kinds of authentication:

Transport Dependency-Injection

Let’s say you always use SMTP with plain authentication when sending a message. You can register the transport objects in your bootstrap file or your Solar_Controller_Page::_setup() method so they are available throughout the application:

// build a Solar_Smtp object
$smtp = Solar::factory('Solar_Smtp', array(
    'adapter'  => 'Solar_Smtp_Adapter_PlainAuth',
    'username' => 'pmjones',
    'password' => 'secret',
    'host'     => 'mail.example.com',
));

// register it as 'smtp'
Solar::register('smtp', $smtp);

// build a Solar_Mail_Transport object with the SMTP object injected
$transport = Solar::factory('Solar_Mail_Transport', array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Smtp',
    'smtp'    => 'smtp', // uses the registered 'smtp' object
);

// register the transport as 'mail-transport'
Solar::register('mail-transport', $transport);

Now when you create a new mail message, you can tell it that there’s a transport already available, and call send() directly on the message.

$mail = Solar::factory('Solar_Mail_Message', array(
    'transport' => 'mail-transport',
));

// ... build the message, and then:
$mail->send();

Automation Via Config File Settings

Finally, you can take the configuration of the SMTP, transport, and mail objects entirely out of your logic, and put it all in the Solar config file.

Then you can lazy-load the objects from the registry, and they will automatically have all the right settings. The various dependency objects will be lazy-loaded automatically for you, since dependency injection is a core component of Solar.

In your Solar.config.php file:

// configure SMTP
$config['Solar_Smtp'] = array(
    'adapter'  => 'Solar_Smtp_Adapter_PlainAuth',
    'username' => 'pmjones',
    'password' => 'secret',
    'host'     => 'mail.example.com',
);

// configure mail transport
$config['Solar_Mail_Transport'] = array(
    'adapter' => 'Solar_Mail_Transport_Adapter_Smtp',
    'smtp'    => 'smtp',
);

// tell all mail messages to use the registered 'mail-transport'
$config['Solar_Mail_Message']['transport'] = 'mail-transport';

Then all you have to do in your setup logic is register class names instead of objects …

Solar::register('smtp', 'Solar_Smtp');
Solar::register('mail-transport', 'Solar_Mail_Transport');

… which will cause those objects to be created only at the moment they are first called as depndencies.

Now you can send an email message very simply:

// create and build a message
$mail = Solar::factory('Solar_Mail_Message');
// ...

// send the message; this creates the SMTP and mail-transport objects
// from the config-file settings, and sends the message trough the
// registered transport.
$mail->send();

Using the config-file-centered process instead of the logic-centered process means that you can have all your settings in one place for use throughout your application. You can even change adapters from the config file without changing any of the mail-sending code, as long as you keep the same registry names.

Conclusion

That’s a whirlwind tour of how to send mail in Solar. If you have questions or comments, please feel free to leave them here, or join the Solar mailing list – we’d love to have you around.

Are you stuck with a legacy PHP application? You should buy my book because it gives you a step-by-step guide to improving you codebase, all while keeping it running the whole time.