Sending Email With PHPMailer

by 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. [li]Create new component email. Paste the following code into app/controllers/components/email.php

Component Class:

<?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:

<?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.

Report

More on Tutorials

Advertising

Comments

  • mushfique posted on 03/03/11 02:37:54 PM
    The given step by step procedure is not suitable for CakePHP1.3 or more. I have tried hard but failed to send mail by following this one. It can send message without the message body, just the subject. I have also followed the suggestion given in the reply/comments but unable to send mail body.

    Here's given folder structure is also different then latest releases. Therefore I think a new updated article is needed for this task.
  • pablo posted on 06/16/10 03:51:03 AM
    On some servers, e-mail content is not rendered properly. In this case, the content of email is blank.

    The problem is the loading of body content using a buffer (ob_start (), etc.). To avoid this problem, you must slightly change the code and directly set the result of rendering to the $mail varible:

    function bodyText() {
    /** This is the body in plain text for non-HTML mail clients
    */
    //ob_start(); remove this line
    $temp_layout = $this->controller->layout;
    $this->controller->layout = ''; // Turn off the layout wrapping
    $mail = $this->controller->render($this->template . '_text'); //result of rendering to $mail
    //$mail = ob_get_clean(); remove this line
    $this->controller->layout = $temp_layout; // Turn on layout wrapping again
    return $mail;
    }
    • anderson631 posted on 02/18/11 05:47:31 PM
      [quote] On some servers, e-mail content is not rendered properly. In this case, the content of email is blank.

      The problem is the loading of body content using a buffer (ob_start (), etc.). To avoid this problem, you must slightly change the code and directly set the result of rendering to the $mail varible:

      function bodyText() {
      /** This is the body in plain text for non-HTML mail clients
      */
      //ob_start(); remove this line
      $temp_layout = $this->controller->layout;
      $this->controller->layout = ''; // Turn off the layout wrapping
      $mail = $this->controller->render($this->template . '_text'); //result of rendering to $mail
      //$mail = ob_get_clean(); remove this line
      $this->controller->layout = $temp_layout; // Turn on layout wrapping again
      return $mail;
      }
      [end quote]

      t worked perfectly: D
      Just what it says to do this in the role bodyText (), but it is also necessary in bodyHTML ()

      Thanks (and sorry for the bad english)
      Anderson Ferreira
  • xpharsh123 posted on 10/23/09 04:07:42 AM
    Thanks It's Really Very Help Full for me and i am sure help full for others also
  • robinduckett posted on 07/22/09 08:52:42 AM
    $this->attachments[$count+1]['filename'] = $filename;
    $this->attachments[$count+1]['asfile'] = $asfile;

    probably should be

    $this->attachments[$count]['filename'] = $filename;
    $this->attachments[$count]['asfile'] = $asfile;

    since arrays in PHP start from 0, not 1.
  • senor_geek posted on 03/26/09 09:18:23 PM
    I've just posted on my blog an updated version of this component ... cleaned up and refactored for the 1.2.x version of CakePHP and PHP5.

    You can find it at http://phazeforward.blogspot.com/2009/03/emailing-from-cakephp-12x.html
    Will see about incorporating the embed() function that Kristofer poste previously.
  • flying_c posted on 02/07/09 06:33:26 AM
    Would be great if you could update the component according to the code in the comments.
  • flying_c posted on 02/07/09 06:32:02 AM
    I needed to embed images into my html mail so i made some additions to the code.

    Add this to the component:

    var $embedded = null;

    function embed($filename,$cid,$name = '') {
    if (empty($this->embedded)) {
    $this->embedded = array();
    $this->embedded[0]['filename'] = $filename;
    $this->embedded[0]['cid'] = $cid;
    $this->embedded[0]['name'] = $name;
    } else {
    $count = count($this->embedded);
    $this->embedded[$count+1]['filename'] = $filename;
    $this->embedded[$count+1]['cid'] = $cid;
    $this->embedded[$count+1]['name'] = $name;
    }
    }

    and make this addtion to the send function:

    if (!empty($this->embedded)) {
    foreach ($this->embedded as $image) {
    if (empty($image['name'])) {
    $mail->AddEmbeddedImage($image['filename'],$image['cid'],$image['cid']);
    } else {
    $mail->AddEmbeddedImage($image['filename'],$image['cid'],$image['name']);
    }
    }
    }


    Use it in this way in your controller:

    $this->Email->embed('/home/domain/public_html/app/webroot/img/logo.jpg','logo');


    And in this way in the view or layout:

    Logo
  • flakyspark posted on 01/26/09 02:04:30 AM
    Above comments from Rogerio Girodo Marques fixing problems with blank mails, but isnt working correct. After some googling i decided to use functions from swift_mail component and now all working fine. So some steps to get this:

    1)Create email_html.thtml and email_text.thtml with <?php echo $content_for_layout ?> in app\views\layouts\

    2)Open your component controller email.php and add var $layout = 'email'; (where are all vars defined)

    3) change functions bodyText() and bodyHTML() to this:

     function bodyText() {
        /** This is HTML body text for HTML-enabled mail clients
         */

        // Temporarily store vital variables used by the controller.
           $tmpLayout = $this->controller->layout;
           $tmpAction = $this->controller->action;
           $tmpOutput = $this->controller->output;
           $tmpRender = $this->controller->autoRender;

          ob_start();
          $this->controller->output = null;
          $mail = $this->controller->render($this->template . '_text', $this->layout . '_text');
          ob_get_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 $mail;
        }

        function bodyHTML() {
        /** This is HTML body text for HTML-enabled mail clients
         */

        // Temporarily store vital variables used by the controller.
           $tmpLayout = $this->controller->layout;
           $tmpAction = $this->controller->action;
           $tmpOutput = $this->controller->output;
           $tmpRender = $this->controller->autoRender;

          ob_start();
          $this->controller->output = null;
          $mail = $this->controller->render($this->template . '_html', $this->layout . '_html');
          ob_get_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 $mail;
        }

    Now it must be working fine.
    (Sorry for bad english)
  • justclint posted on 12/21/08 04:44:32 PM
    Hello, thanks for this code. I got it working and my form is sending out the email.

    Unfortunately the email being received is blank.

    My component is as follows:
    function send() {

    $this->Contact->set($this->data);
    $this->Email->template = 'email/contact'; // this will use contact_html.ctp
    $this->Email->from = $this->data['Contact']['Email'];
    $this->Email->fromName = 'My Company';
    $this->Email->to = 'xxx@xxx.com';
    $this->Email->subject = 'Contact Request';

    $result = $this->Email->send();

    $this->redirect('thankyou');
    exit();

    }// end send()

    And my email template is app/views/contact/email/contact_html.ctp:

    echo $content_for_layout;

    Im sure im missing something but I just cant figure it out. Any help is appreciated.

    Thanks!

    justclint
    • justclint posted on 12/21/08 06:00:06 PM
      After reading the comments more thoroughly I was able to fix the problem suggested by Rogerio Girodo Marques and CED.

      Thanks!

      justclint
  • pwesthagen posted on 11/18/08 04:31:43 PM
    The email component includes the PHPMailer class using the vendor() function, which is now deprecated.

    Replace:


    vendor('phpmailer'.DS.'class.phpmailer');

    With:


    App::import('Vendor', 'phpmailer', array('file' => 'phpmailer'.DS.'class.phpmailer.php'));
    • ranjangoyal posted on 12/03/08 01:36:39 AM

      I applied what you said and I get the following error:

      Fatal error: Class 'App' not found in ....controllers/components/email.php on line 74

      Why it is happening?

      I am new to cake.
      Please help.
      Regards,
      Ranjan

      The email component includes the PHPMailer class using the vendor() function, which is now deprecated.

      Replace:


      vendor('phpmailer'.DS.'class.phpmailer');

      With:


      App::import('Vendor', 'phpmailer', array('file' => 'phpmailer'.DS.'class.phpmailer.php'));
  • rogeriogirodo posted on 08/12/08 07:28:17 PM
    I got the same issue. But I solve the problem with this


    echo $this->controller->render($this->template.'_html');
    $mail = ob_get_clean();

    But I think the problem is not the ob_start() or ob_get_clean() functions. I don't know, but for some reason the controller render function doesn't output the result
  • frycake posted on 07/16/08 03:55:53 PM
    First thanks for your work.

    I encountered an issue when I tried to use your code with cakephp 1.2

    It seems that the solution is to catch the rendering by an other way
    $mail = $this->controller->render($this->template . '_text');
    $this->controller->output = ''; 

    I don't know really why, but ob_start doesn't work for me.

    If someone can confirm the problem ? or find an other solution !
  • ssaeed84 posted on 07/12/08 09:32:33 AM
    Hi, I'm trying to send email with Arabic content, i have got the following error in data : ���Ö ��ã 2008
    i try to chag the $CharSet variable in class.phpmailer.php file to 'windows-1256' and no thing happened, then i try to to put a meta tag in my view : charset('windows-1256');?> and also no thing happened, finally i try to use header() function in the beforeRender function in my controller : header('Content-Type:text/html; charset=windows-1256'); and also no thing happened the characters is still like ���Ö ��....... !!!!! -- :-( .. any can help me please...
    Thanks a lot.
  • mohidul posted on 06/24/08 09:10:40 AM
    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.

  • maurot21 posted on 06/11/08 09:28:39 AM
    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.
  • flipflops posted on 01/16/08 08:27:45 AM
    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/
  • squidliberty posted on 01/15/08 09:12:59 PM
    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!
  • flipflops posted on 12/18/07 02:54:24 AM
    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.
  • stef.an posted on 12/16/07 08:16:22 AM
    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
  • flipflops posted on 12/07/07 06:18:23 AM
    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
    • Ales posted on 06/28/08 07:29:02 AM
      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;
      ?>
  • baran_khan posted on 10/17/07 05:51:57 AM
    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
  • arthuc01 posted on 10/12/07 01:24:56 PM
    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 :-).
  • Bo_oz posted on 09/15/07 05:55:11 AM
    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 :*(
  • eugene posted on 08/04/07 03:14:53 AM
    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:

    var $smtpHostPort= "26";

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

  • neilc posted on 04/11/07 07:35:02 AM
    @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.
  • dionak posted on 04/10/07 01:56:57 PM
    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.
  • kabturek posted on 11/08/06 07:59:37 AM
    anyone interested in a more flexible, smaller, quicker & more intuitive full OOP library look @ http://www.swiftmailer.org/ ;)
    tut coming soon
  • kitten posted on 10/24/06 05:00:43 AM
  • xhenxhe posted on 10/19/06 07:35:25 AM
    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?
  • Gkelly posted on 11/30/99 12:00:00 AM
    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
login to post a comment.