Sending Email With PHPMailer

By Alex McFadyen (drayen)
[This is a slightly updated copy of the tutorial on now defunct wiki.cakephp.org]

This example will show you how to send HTML mail from you Cake application with PHPMailer.

You will create:

1. cakePHP component
2. a vendor package
3. view for plain text email body
4. view for HTML email body
5. a function in your controller to send mail.

Steps


Get PHPMailer



  1. Get PHPMailer http://phpmailer.sourceforge.net/
  2. Unpack it into app/vendors/phpmailer/ , so you'll have /vendors/phpmailer/class.phpmailer.php etc.etc.


Create views and layouts


  1. Create two views, default_html.thtml and default_text.thtml and place them in app/views/your_controller/email/
  2. Create a layout for the HTML part of the email, call it app/views/layouts/email.thtml


Create component



  1. Create new component email. Paste the following code into app/controllers/components/email.php


Component Class:

Download code <?php 
/**
 * This is a component to send email from CakePHP using PHPMailer
 * @link http://bakery.cakephp.org/articles/view/94
 * @see http://bakery.cakephp.org/articles/view/94
 */

class EmailComponent
{
  
/**
   * Send email using SMTP Auth by default.
   */
    
var $from         'phpmailer@cakephp';
    var 
$fromName     "Cake PHP-Mailer";
    var 
$smtpUserName '';  // SMTP username
    
var $smtpPassword ''// SMTP password
    
var $smtpHostNames"";  // specify main and backup server
    
var $text_body null;
    var 
$html_body null;
    var 
$to null;
    var 
$toName null;
    var 
$subject null;
    var 
$cc null;
    var 
$bcc null;
    var 
$template 'email/default';
    var 
$attachments null;

    var 
$controller;

    function 
startup( &$controller ) {
      
$this->controller = &$controller;
    }

    function 
bodyText() {
    
/** This is the body in plain text for non-HTML mail clients
     */
      
ob_start();
      
$temp_layout $this->controller->layout;
      
$this->controller->layout '';  // Turn off the layout wrapping
      
$this->controller->render($this->template '_text'); 
      
$mail ob_get_clean();
      
$this->controller->layout $temp_layout// Turn on layout wrapping again
      
return $mail;
    }

    function 
bodyHTML() {
    
/** This is HTML body text for HTML-enabled mail clients
     */
      
ob_start();
      
$temp_layout $this->controller->layout;
      
$this->controller->layout 'email';  //  HTML wrapper for my html email in /app/views/layouts
      
$this->controller->render($this->template '_html'); 
      
$mail ob_get_clean();
      
$this->controller->layout $temp_layout// Turn on layout wrapping again
      
return $mail;
    }

    function 
attach($filename$asfile '') {
      if (empty(
$this->attachments)) {
        
$this->attachments = array();
        
$this->attachments[0]['filename'] = $filename;
        
$this->attachments[0]['asfile'] = $asfile;
      } else {
        
$count count($this->attachments);
        
$this->attachments[$count+1]['filename'] = $filename;
        
$this->attachments[$count+1]['asfile'] = $asfile;
      }
    }


    function 
send()
    {
    
vendor('phpmailer'.DS.'class.phpmailer');

    
$mail = new PHPMailer();

    
$mail->IsSMTP();            // set mailer to use SMTP
    
$mail->SMTPAuth true;     // turn on SMTP authentication
    
$mail->Host   $this->smtpHostNames;
    
$mail->Username $this->smtpUserName;
    
$mail->Password $this->smtpPassword;

    
$mail->From     $this->from;
    
$mail->FromName $this->fromName;
    
$mail->AddAddress($this->to$this->toName );
    
$mail->AddReplyTo($this->from$this->fromName );

    
$mail->CharSet  'UTF-8';
    
$mail->WordWrap 50;  // set word wrap to 50 characters

    
if (!empty($this->attachments)) {
      foreach (
$this->attachments as $attachment) {
        if (empty(
$attachment['asfile'])) {
          
$mail->AddAttachment($attachment['filename']);
        } else {
          
$mail->AddAttachment($attachment['filename'], $attachment['asfile']);
        }
      }
    }

    
$mail->IsHTML(true);  // set email format to HTML

    
$mail->Subject $this->subject;
    
$mail->Body    $this->bodyHTML();
    
$mail->AltBody $this->bodyText();

    
$result $mail->Send();

    if(
$result == false $result $mail->ErrorInfo;

    return 
$result;
    }
}
?>


Useing it in your controller



Controller Class:

Download code <?php 
class MyController extends AppController{
    var 
$components = array('Email'); //  use component email

    
...
 
    function 
send() {
            
$this->Email->template 'email/confirm';
            
// You can use customised thmls or the default ones you setup at the start
           
            
$this->set('data'$data);
            
$this->Email->to 'someone@somewhere.com';
            
$this->Email->subject 'your new account';
           
           
            
$this->Email->attach($fully_qualified_filenameoptionally $new_name_when_attached);
            
// You can attach as many files as you like.
           
            
$result $this->Email->send();
 
        
//the rest of the controller method...
      
}
}
?>


Credits


This is lifted almost word for word from the original piece at http://wiki.cakephp.org/tutorials:sending_email_with_phpmailer but i figured as i was about to use it, i would rewrite it for the bakery.

Enjoy.

Drayen.

 

Comments 94

CakePHP Team Comments Author Comments
 

Comment

1 This is nice but...

Is there a way to use all the phpMailer functions directly. For example, what if you wanted to add more than one address? Also, I wonder why you chose to change all the mail variables. Why didn't you keep the same variables used in phpMailer to avoid confusion?
Posted Oct 19, 2006 by Dustin
 

Comment

2 Native PHPMailer API

Posted Oct 24, 2006 by Heiner Gassen
 

Comment

3 SwiftMailer

anyone interested in a more flexible, smaller, quicker & more intuitive full OOP library look @ http://www.swiftmailer.org/
;)
tut coming soon
Posted Nov 8, 2006 by Marcin Domanski
 

Comment

4 Add Multiple Addresses.

This code will allow users to email multiple addresses.

1. Put the folowing line inside your controller action:
$this->Email->attach_to($to_address, $to_name);
/*Add this line as many times as you want.*/

2. Add the following to the email component.
//Initialize variable
var $to_arr = null;

//Add function
function attach_to($to_address, $to_name = '') {
if (empty($this->to_arr)) {
$this->to_arr = array();
$this->to_arr[0]['to_address'] = $to_address;
$this->to_arr[0]['to_name'] = $to_name;
} else {
$count = count($this->to_arr);
$this->to_arr[$count+1]['to_address'] = $to_address;
$this->to_arr[$count+1]['to_name'] = $to_name;
}
}

//Add this loop within the send function
if (!empty($this->to_arr)) {
foreach ($this->to_arr as $to) {
$mail->AddAddress($to['to_address'], $to['to_name']);
}
}


Thats it hope it helps someone out.

~Gene Kelly
Posted Dec 31, 1969 by Gene Kelly
 

Question

5 What is the data variable in controller

In the Controller Class code... is $data initialized somewhere outside the scope of this article? Or should it be $this->data? I'm not clear on if that's a typo or if how the contents of $data should be constructed (e.g. named array?).

Thanks.
Posted Apr 10, 2007 by Diona Kidd
 

Comment

6 Re What is the data variable in controller

@Diona - Your first guess is correct. $data is some data that you want to set to be available in the email views (text and html). If your email views are static, you don't need to set it.

You can call it whatever you want.

For example, for a purchase confirmation email, you might list the products purchased in the emails, so you might use:

$products = $this->Session->read('shopping_basket');
$this->set('products',$products);

or something like that.
Posted Apr 11, 2007 by Neil Crookes
 

Comment

7 Other port usage

I was running into error
"Language string failed to load: recipients_failed"

It happens when you have smtp on other port then 25 (in my case it was port 26). So I put one more variable to email component class:

<code>
var $smtpHostPort= "26";
</code>

And a line to function send()
<code>
$mail->Password = $this->smtpPassword;
</code>

Posted Aug 4, 2007 by Eugene
 

Comment

8 Language string failed to load

The: "Language string failed to load: recipients_failed" error

is the result of that the PHPmailer class cannot locate its language files when placing the complete class in the vendors directory. Moving the complete languages folder of the PHPmailer directory into the webroot of CakePHP fixes the problem.

Although I'm still strugling with a SMTP error, at least I do now receive the correct error :*(
Posted Sep 15, 2007 by Bo
 

Comment

9 Call to undefined method stdClass send

I was getting the following error..

Fatal error: Call to undefined method stdClass::send() in F:\xampplite
\htdocs\cake\app\controllers\users_controller.php

turned out that I had declared
var $components = array('email');
NOT!!
var $components = array('Email');

Just thought I'd post in case anyone else was as daft as me :-).
Posted Oct 12, 2007 by Chris Arthur
 

Question

10 Error

getting this warning when i try to run the code:

fsockopen(): unable to connect to :25 in /home/content/i/m/r/imranbaran/html/app/vendors/phpmailer/class.smtp.php on line 105

please help
Posted Oct 17, 2007 by Baran Khan
 

Comment

11 HTML layout not rendering

Thanks for a great component. It is very easy to set up and use.

I do have one problem though, the HTML layout - app/views/layouts/email.thtml simply isn't rendering and I can't work out why.

The emails are sent perfectly just without the layout wrapped around them. I was hoping somebody else had experienced the same problem and found a solution.

Thanks
Posted Dec 7, 2007 by John Elliott
 

Question

12 SMTP data in config file

hi everyone :)

how can i declare smtp data (host,user,pass,port) in a config file, so the component reads it from config/email.php for example?!

cheers,
stefan
Posted Dec 16, 2007 by Stefan
 

Comment

13 RE SMTP data in config file

Hi Stefan

The easiest thing to do is just add these values in your app/config/core.php file e.g.

define('CAKE_EMAIL_USER', 'website@mywebsite.com');


Then in the mailer component you just reference these values:


class EmailComponent
{
  /**
   * Send email using SMTP Auth by default.
   */
    var $from         = 'phpmailer@cakephp';
    var $fromName     = "Cake PHP-Mailer";
    var $smtpUserName = CAKE_EMAIL_USER;  // SMTP username
    


This way you've set the default values, but you can still over ride them when you use the component.
Posted Dec 18, 2007 by John Elliott
 

Question

14 Spam Vulnerability

Does anyone know what sanitization needs to occur in order to thwart injection? I assumed that using cake's cleanArray() would be sufficient, but someone seems to be abusing my forms. Any advice would be appreciated!
Posted Jan 15, 2008 by Scott
 

Comment

15 re Spam Vulnerability

There was a good article on Digital-Web recently that goes through securing contact forms in quite a lot of detail - might be of help to you.

http://www.digital-web.com/articles/bulletproof_contact_form_with_php/
Posted Jan 16, 2008 by John Elliott
 

Bug

16 headers already sent

Hi, I'm trying to use the component, but I get the following error:

Warning: Cannot modify header information - headers already sent by (output started at /home/visioniz/public_html/books/app/controllers/components/email.php:114) in /home/visioniz/public_html/books/cake/libs/controller/controller.php on line 447

The funny thing is, I receive the sent emails, no problem, but cake can't render the page.

Please help.
Posted Jun 11, 2008 by Mauro Trigo
 

Question

17 Attachment file location

Hi, would you please tell about the location of attachment file. I mean where the files should be kept?

$this->Email->attach($fully_qualified_filename, optionally $new_name_when_attached);

What will I use instead of $fully_qualified_filename. Example please.

Thanks in advance.

Posted Jun 24, 2008 by Mohidul
 

Comment

18 HTML layout not rendering


I do have one problem though, the HTML layout - app/views/layouts/email.thtml simply isn't rendering and I can't work out why.

app/views/layouts/email.thtml:

View Template:


<?php
                    
echo $content_for_layout;
?>
Posted Jun 28, 2008 by Ales