Improved SwiftMailer Component
I've used the PHPMailer component previously, and it's useful for basic SMTP usage. However, I recently needed to make an SMTP connection requiring TLS authentication, which PHPMailer does not provide.
To resolve this, I decided to switch to SwiftMailer. Unfortunately, I wasn't very satisfied with the ease of use of the existing SwiftMailer component I found on the Bakery.
As such, I created my own component that is more similar to the original PHPMailer component I was using. It doesn't have attachments implemented, but it's handy for sending HTML/plaintext emails.
To resolve this, I decided to switch to SwiftMailer. Unfortunately, I wasn't very satisfied with the ease of use of the existing SwiftMailer component I found on the Bakery.
As such, I created my own component that is more similar to the original PHPMailer component I was using. It doesn't have attachments implemented, but it's handy for sending HTML/plaintext emails.
1. Download SwiftMailer
First, download SwiftMailer, and place it under vendors/SwiftMailer.
2. Create SwiftMailer Component
To easily take advantage of SwiftMailer, you'll need to create a component that does all the work for you. Create a file named swift_mailer.php under app/controllers/components. Paste the code below into the file, and save it.
Component Class:
Download code
<?php
/*
* SwiftMailer 3 Component
* @author Matt Huggins
* @version 1.0
* @license MIT
*/
App::import('Vendor', 'Swift', array('file' => 'SwiftMailer'.DS.'Swift.php'));
class SwiftMailerComponent extends Object {
var $controller = false;
var $layout = 'email';
var $viewPath = 'email';
var $smtpType = 'open'; // open, ssl, tls
var $smtpUsername = '';
var $smtpPassword = '';
var $smtpHost = ''; // specify host or leave blank to auto-detect
var $smtpPort = null; // null to auto-detect, otherwise specify (e.g.: 25 for open, 465 for ssl, etc.)
var $smtpTimeout = 10; // seconds before timeout occurs
var $sendmailCmd = null; // null to auto-detect, otherwise manually defined (e.g.: '/usr/sbin/sendmail -bs')
var $from = null;
var $fromName = null;
var $to = null; // Each of $to, $cc, and $bcc should all be formatted as an array of
var $cc = null; // key => value pairs that represent email address/name. e.g.:
var $bcc = null; // array('bob@google.com'=>'Bob Smith', 'joe@yahoo.com'=>'Joe Shmoe')
function startup(&$controller) {
$this->controller =& $controller;
}
function _connect($method) { // smtp, sendmail, native
// Create the appropriate Swift mailer object based upon the connection type.
switch ($method) {
case 'smtp':
return $this->_connectSMTP();
case 'sendmail':
return $this->_connectSendmail();
case 'native': default:
return $this->_connectNative();
}
}
function _connectNative() {
App::import('Vendor', 'Swift_Connection_NativeMail', array('file' => 'SwiftMailer'.DS.'Swift'.DS.'Connection'.DS.'NativeMail.php'));
// Return the swift mailer object.
return new Swift(new Swift_Connection_NativeMail());
}
function _connectSendmail() {
App::import('Vendor', 'Swift_Connection_Sendmail', array('file' => 'SwiftMailer'.DS.'Swift'.DS.'Connection'.DS.'Sendmail.php'));
// Auto-detect the sendmail command to use if not specified.
if (empty($this->sendmailCmd)) {
$this->sendmailCmd = Swift_Connection_Sendmail::AUTO_DETECT;
}
// Return the swift mailer object.
return new Swift(new Swift_Connection_Sendmail($this->sendmailCmd));
}
function _connectSMTP() {
App::import('Vendor', 'Swift_Connection_SMTP', array('file' => 'SwiftMailer'.DS.'Swift'.DS.'Connection'.DS.'SMTP.php'));
// Detect SMTP host if not provided.
if (empty($this->smtpHost)) {
$this->smtpHost = Swift_Connection_SMTP::AUTO_DETECT;
}
// Detect SMTP port if not provided.
if (empty($this->smtpPort)) {
$this->smtpPort = Swift_Connection_SMTP::AUTO_DETECT;
}
// Determine what type of connection to use (open, ssl, tls).
switch ($this->smtpType) {
case 'ssl':
$smtpType = Swift_Connection_SMTP::ENC_SSL; break;
case 'tls':
$smtpType = Swift_Connection_SMTP::ENC_TLS; break;
case 'open': default:
$smtpType = Swift_Connection_SMTP::ENC_OFF;
}
// Create the swift mailer object, and prepare authentication if required.
$smtp =& new Swift_Connection_SMTP($this->smtpHost, $this->smtpPort, $smtpType);
$smtp->setTimeout($this->smtpTimeout);
if (!empty($this->smtpUsername)) {
$smtp->setUsername($this->smtpUsername);
$smtp->setPassword($this->smtpPassword);
}
// Return the swift mailer object.
return new Swift($smtp);
}
function _getBodyText($view) {
// Temporarily store vital variables used by the controller.
$tmpLayout = $this->controller->layout;
$tmpAction = $this->controller->action;
$tmpOutput = $this->controller->output;
$tmpRender = $this->controller->autoRender;
// Render the plaintext email body.
ob_start();
$this->controller->output = null;
$body = $this->controller->render($this->viewPath . DS . $view . '_text', $this->layout . '_text');
ob_end_clean();
// Restore the layout, view, output, and autoRender values to the controller.
$this->controller->layout = $tmpLayout;
$this->controller->action = $tmpAction;
$this->controller->output = $tmpOutput;
$this->controller->autoRender = $tmpRender;
return $body;
}
function _getBodyHTML($view) {
// Temporarily store vital variables used by the controller.
$tmpLayout = $this->controller->layout;
$tmpAction = $this->controller->action;
$tmpOutput = $this->controller->output;
$tmpRender = $this->controller->autoRender;
// Render the HTML email body.
ob_start();
$this->controller->output = null;
$body = $this->controller->render($this->viewPath . DS . $view . '_html', $this->layout . '_html');
ob_end_clean();
// Restore the layout, view, output, and autoRender values to the controller.
$this->controller->layout = $tmpLayout;
$this->controller->action = $tmpAction;
$this->controller->output = $tmpOutput;
$this->controller->autoRender = $tmpRender;
return $body;
}
function send($view = 'default', $subject = '', $method = 'smtp') {
// Create the message, and set the message subject.
$message =& new Swift_Message($subject);
// Append the HTML and plain text bodies.
$bodyHTML = $this->_getBodyHTML($view);
$bodyText = $this->_getBodyText($view);
$message->attach(new Swift_Message_Part($bodyHTML, "text/html"));
$message->attach(new Swift_Message_Part($bodyText, "text/plain"));
// Set the from address/name.
$from =& new Swift_Address($this->from, $this->fromName);
// Create the recipient list.
$recipients =& new Swift_RecipientList();
// Add all TO recipients.
if (!empty($this->to)) {
if (is_array($this->to)) {
foreach($this->to as $address => $name) {
$recipients->addTo($address, $name);
}
} else {
$recipients->addTo($this->to, $this->to);
}
}
// Add all CC recipients.
if (!empty($this->cc)) {
if (is_array($this->cc)) {
foreach($this->cc as $address => $name) {
$recipients->addCc($address, $name);
}
} else {
$recipients->addCc($this->cc, $this->cc);
}
}
// Add all BCC recipients.
if (!empty($this->bcc)) {
if (is_array($this->bcc)) {
foreach($this->bcc as $address => $name) {
$recipients->addBcc($address, $name);
}
} else {
$recipients->addBcc($this->bcc, $this->bcc);
}
}
// Attempt to send the email.
$mailer =& $this->_connect($method);
$result = $mailer->send($message, $recipients, $from);
$mailer->disconnect();
return $result;
}
}
?>
3. Create Email Layouts
You will need to create two views for this component to work properly. By default, the layouts used for emails are "email_html.ctp" and "email_text.ctp".
Anything you include in "email_html.ctp" will be used as the layout for HTML content, and anything you include in "email_text.ctp" will be used as the layout for text content.
IMPORTANT: Make sure you include within each layout wherever the content should be rendered.
4. Create Email Views
Similar to the layouts created in the previous step, two views must be created for each action where you wish to send an email. By default, these layouts must be placed within an "email" subdirectory within the current action's view path.
For example, if you allow users to register new accounts within users_controller.php, and you intend to send an email to each new user when an account is created, then you'd create files "register_html.ctp" and "register_text.ctp" within app/views/users/email.
Similar to standard views, variables can be placed within views. Sticking with the previous example, you might include the new username within the email by doing something like the following.
View Template:
Download code
Thanks for joining My Awesome Site, <?php echo $username;?>!
Any variables included within your view can be passed by the controller's set() method as usual.
5. Prepare the Controller
Whichever controllers you're planning to use SwiftMailer in will need to be updated to reference the component. Within your controller's $components array, you'll need to add 'SwiftMailer'.
Controller Class:
Download code
<?php
class MyController extends AppController {
var $components= array('SwiftMailer');
// ...other class code here...
}
?>
6. Sending Emails
Now that your controller, views, and layouts are ready, you can send emails. The following is a basic example of the code you'll use to accomplish this.
Controller Class:
Download code
<?php
// Default to localhost port 25, no user authentication.
$this->SwiftMailer->from = 'webmaster@mysite.com';
$this->SwiftMailer->fromName = 'Webmaster';
$this->SwiftMailer->to = $this->data['Member']['email'];
$this->set(array(
'username' => $this->data['User']['username'],
'password' => $this->data['User']['password'],
));
if (!$this->SwiftMailer->send('register', 'Thanks for Registering!')) {
$this->log('Error sending email "register".', LOG_ERROR);
}
?>
Below is an example of connecting to Gmail, which requires TLS authentication, a username, and password.
Controller Class:
Download code
<?php
$this->SwiftMailer->smtpType = 'tls';
$this->SwiftMailer->smtpHost = 'smtp.gmail.com';
$this->SwiftMailer->smtpPort = 465;
$this->SwiftMailer->smtpUsername = 'my_username';
$this->SwiftMailer->smtpPassword = 'my_password';
$this->SwiftMailer->from = 'my_username@gmail.com';
$this->SwiftMailer->fromName = 'My Name';
$this->SwiftMailer->to = $this->data['Member']['email'];
$this->set(array(
'username' => $this->data['User']['username'],
'password' => $this->data['User']['password'],
));
if (!$this->SwiftMailer->send('register', 'Thanks for Registering!')) {
$this->log('Error sending email "register".', LOG_ERROR);
}
?>
7. Additional Notes
7.1. Connection Types
By default, the SwiftMailer component will use an open (plaintext) connection. Additional options include SSL and TLS. To use these, simply set the value of smtpType.
Controller Class:
Download code
<?php
$this->SwiftMailer->smtpType = 'open'; // use plaintext
$this->SwiftMailer->smtpType = 'ssl'; // use SSL
$this->SwiftMailer->smtpType = 'tls'; // use TLS
?>
7.2. Non-SMTP Alternatives
Sending can also be performed in ways other than SMTP. An optional third parameter included in the send() method allows for sendmail and native approaches to be used instead of the default.
Controller Class:
Download code
<?php
$this->SwiftMailer->send('register', 'Thanks for Registering!', 'sendmail'); // use sendmail
$this->SwiftMailer->send('register', 'Thanks for Registering!', 'native'); // use native
?>
7.3. Sending to Multiple Recipients
Instead of passing a single email address to SwiftMailer, you can provide an array of key/value pairs representing the address/name respectively. For example:
Controller Class:
Download code
<?php
$this->SwiftMailer->to = array(
'bob@gmail.com' => 'Bob Smith',
'joe@yahoo.com' => 'Joe Schmoe',
);
?>
7.4. Carbon Copying and Blind Carbon Copying
Similarly, single email addresses or arrays of address/name combinations can be provided to SwiftMailer for the sake of carbon copying. For Example:
Controller Class:
Download code
<?php
$this->SwiftMailer->cc = 'rick@msn.com';
$this->SwiftMailer->bcc = array(
'bob@gmail.com' => 'Bob Smith',
'joe@yahoo.com' => 'Joe Schmoe',
);
?>
Comments
Comment
1 Change we had to do to get it working
Thanks for this very helpful component. We had to make a change to get it to work here. We had to change the render sections to look like this:
ob_start();
$$this->controller->output = null;
this->controller->render($this->viewPath . DS . $view . '_html', $this->layout . '_html');
$body = ob_get_contents();
ob_end_clean();
to get the body of the message to come through. Just FYI.
Thanks,
Daniel
Comment
2 Helpful tips
1) When you create the SwiftMailer folder in components, upload ONLY the contents of the lib folder - not the whole archive. I uploaded everything and couldn't figure out why I was getting "Fatal error: Class 'Swift_Message' not found..."
2) The instructions weren't terribly clear when it came to layouts and views:
- add two files in your views/layouts folder: email_text.ctp and email_html.ctp. This is where the call goes.
- next, add two MORE files, (register_html.ctp and register_text.ctp in the case of this tutorial) in the email folder of your controller's view.
Hope this helps a few others...
Comment
3 improvements
I want to make a little contribution because I didn't like the way that the developers have to declare all of the configuration in the controllers over and over again. Another solution would be to add it to the app_controller.php but that is not satisfactory (at least for me). Thus I suggest some changes and please comment on this.
What I recommend ables the most common parameteres to be configured using the Configure::read method and make the component more automatic by setting these variables by project.
1. Add a new method called _setup to the component
Component Class:
<?php
class SwiftMailerComponentextends Object
...
function _setup()
{
$this->smtpUsername = Configure::read('Email.smtpUsername');
$this->smtpPassword = Configure::read('Email.smtpPassword');
$this->smtpHost = Configure::read('Email.smtpHost');
}
...
?>
2. and then add the _setup() function to the startup to set this up automatically for each project.
Component Class:
<?php
class SwiftMailerComponentextends Object
...
function startup(&$controller) {
$this->controller =& $controller;
$this->_setup();
}
...
?>
Comment
4 Finally one that works!
Comment
5 Thanks
Comment
6 SwiftMailer
Component Class:
<?php
App::import('Vendor', 'Swift', array('file' => 'SwiftMailer'.DS.'swift_required.php'));
class SwiftMailerComponent extends Object {
var $controller = false;
var $layout = 'email';
var $viewPath = 'email';
var $from = null;
var $fromName = null;
var $to = null;
function startup(&$controller) {
$this->controller =& $controller;
}
function _getBodyText($view) {
// Temporarily store vital variables used by the controller.
$tmpLayout = $this->controller->layout;
$tmpAction = $this->controller->action;
$tmpOutput = $this->controller->output;
$tmpRender = $this->controller->autoRender;
// Render the plaintext email body.
ob_start();
$this->controller->output = null;
$body = $this->controller->render($this->viewPath . DS . $view . '_text', $this->layout . '_text');
ob_end_clean();
// Restore the layout, view, output, and autoRender values to the controller.
$this->controller->layout = $tmpLayout;
$this->controller->action = $tmpAction;
$this->controller->output = $tmpOutput;
$this->controller->autoRender = $tmpRender;
return $body;
}
function _getBodyHTML($view) {
// Temporarily store vital variables used by the controller.
$tmpLayout = $this->controller->layout;
$tmpAction = $this->controller->action;
$tmpOutput = $this->controller->output;
$tmpRender = $this->controller->autoRender;
// Render the HTML email body.
ob_start();
$this->controller->output = null;
$body = $this->controller->render($this->viewPath . DS . $view . '_html', $this->layout . '_html');
ob_end_clean();
// Restore the layout, view, output, and autoRender values to the controller.
$this->controller->layout = $tmpLayout;
$this->controller->action = $tmpAction;
$this->controller->output = $tmpOutput;
$this->controller->autoRender = $tmpRender;
return $body;
}
function send($view = 'default', $subject = '') {
// Create the message, and set the message subject.
$message =& Swift_Message::newInstance();
$message->setSubject($subject);
// Append the HTML and plain text bodies.
$bodyHTML = $this->_getBodyHTML($view);
$bodyText = $this->_getBodyText($view);
$message->setBody($bodyText, "text/plain");
$message->addPart($bodyHTML, "text/html");
// Set the from address/name.
$message->setFrom(array($this->from => $this->fromName));
// Create the recipient list.
//$recipients =& new Swift_RecipientList();
$message->setTo(array($this->to => 'Test'));
$transport = Swift_SmtpTransport::newInstance();
$transport->setHost('smtp.googlemail.com');
$transport->setPort(465);
$transport->setEncryption('tls');
$transport->setUsername('user@gmail.com');
$transport->setPassword('mypass');
$mailer = Swift_Mailer::newInstance($transport);
// Attempt to send the email.
$result = $mailer->send($message);
return $result;
}
}
?>