PHPMailer with native API for PHP 5.x
This tutorial on using PHPMailer with Cake (http://bakery.cakephp.org/articles/view/94) has a comment requesting a component that lets you use the native PHPMailer API. This is possible with PHP5's built-in overloading capabilities.
The Mailer component acts as a delegator and looks like this:
In your controller, do the following:
That's all! You can now call any PHPMailer method through $this->Mailer->MethodName() and set any class variable through $this->Mailer->varName = 'value'.
Component Class:
Download code
<?php
class MailerComponent extends Object
{
/**
* PHPMailer object.
*
* @access private
* @var object
*/
var $m;
/**
* Creates the PHPMailer object and sets default values.
* Must be called before working with the component!
*
* @access public
* @return void
*/
function init()
{
// Include the class file and create PHPMailer instance
vendor('phpmailer/class.phpmailer');
$this->m = new PHPMailer;
// Set default PHPMailer variables (see PHPMailer API for more info)
$this->From = 'me@example.com';
$this->FromName ='MyName';
// set more PHPMailer vars, for smtp etc.
}
function __set($name, $value)
{
$this->m->{$name} = $value;
}
function __get($name)
{
if (isset($this->m->{$name})) {
return $this->m->{$name};
}
}
function __call($method, $args)
{
if (method_exists($this->m, $method)) {
return call_user_func_array(array($this->m, $method), $args);
}
}
}
?>
In your controller, do the following:
Controller Class:
Download code
<?php
var $components = array('Mailer');
// Inside your method:
// Set up mail
$this->Mailer->init();
$this->Mailer->AddAddress('recipient@example.com');
$this->Mailer->Subject = 'My Subject';
// Set PHPMailer vars and call PHPMailer methods (see PHPMailer API for more info)
// Set mail body
ob_start();
$this->render('nameOfEmailTemplate', 'nameOfEmailLayout');
$this->Mailer->Body = ob_get_clean();
// Send mail
if ($this->Mailer->send()) {
echo 'Mail was sent successfully.';
} else {
echo 'There was a problem sending mail: '.$this->Mailer->ErrorInfo;
}
?>
That's all! You can now call any PHPMailer method through $this->Mailer->MethodName() and set any class variable through $this->Mailer->varName = 'value'.
Comments
Bug
1 typo
<code>$this->Mailer->errorInfo; </code>
should be:
<code>$this->Mailer->ErrorInfo; </code>
Comment
2 Typo corrected
Question
3 Sending a value to the mail template
Thanks :)
Bug
4 One more tiny typo
{
$this->m->{$name) = $value;
}
should be
function __set($name, $value)
{
$this->m->{$name} = $value;
}
thanks for the example !
Question
5 PHP4
Comment
6 PHP4
For the methods
function send()
{
return $this->m->send();
}
And for properties:
function setBody($value)
{
return $this->m->Body = $value;
}
Question
7 templates
Bug
8 error
Comment
9 Fixed
I fixed it, thank you for reporting.
Question
10 Why so complicated
Controller Class:
<?php
vendor('phpmailer/class.phpmailer');
class MailerComponent extends PHPMailer
{}
?>
Now its possible to use component exactly like PHPmailer object.
Comment
11 I can see why you would want to use the delegator pattern
For example, I added the following snippet to my version of the MailerComponent:
Component Class:
<?php
var $controller;
function startup(&$controller) {
$this->controller = $controller;
$this->init();
}
/**
* Set the body of the email
*/
function setBody($template, $layout = 'email') {
$this->Body = $this->_render($template, $layout);
}
/**
* Set the AltBody, like for the text in HTML+TXT emails
*/
function setAltBody($template, $layout = 'email') {
$this->AltBody = $this->_render($template, $layout);
}
function _render($template, $layout) {
ob_start();
$this->controller->render($template, $layout);
return ob_get_clean();
}
?>
Question
12 html code in email
I am using your guide but ran into a problem.
The email that i receive is the raw html code.
How do I ensure that the actual html file is being sent and no t the raw code ?
Thanks.
Comment
13 set the type to HTML
$this->Mailer->IsHtml(true);
Question
14 Redirecting
Do you know how I can prevent this from happening?
Comment
15 Layout not shown
Is your $layout shown properly with a plain text template/layout using render as above? I've tried and the template content is correct, but no layout is shown (perhaps it would have something to do with ob_get_clean() ending the output rendering prematurely?)
Thanks a lot!
Comment
16 Layout white
Comment
17 Found the error
<?php
function _render($template,$layout) {
ob_start();
$this->controller->autoRender = false;
$this->controller->render($template,$layout);
$this->controller->autoRender = 'auto';
$mail = ob_get_clean();
return $mail;
}
?>
Comment
18 Cakephp 1.2 RC1
I've gotten pretty much everything working, except one major part.
When it sends out emails, the subject and body are completely blank?
Here's my component:
Component Class:
<?php
<?php
class MailerComponent extends Object
{
/**
* PHPMailer object.
*
* @access private
* @var object
*/
var $m;
var $controller;
/**
* Creates the PHPMailer object and sets default values.
* Must be called before working with the component!
*
* @access public
* @return void
*/
function init()
{
// Include the class file and create PHPMailer instance
App::import('Vendor', 'PHPMailer', array('file' => 'phpmailer'.DS.'class
$this->m = new PHPMailer;
// Set default PHPMailer variables (see PHPMailer API for more info)
$this->From = 'verification@aevumdecessus.com';
$this->FromName ='Aevum Decessus Administration';
// set more PHPMailer vars, for smtp etc.
$this->IsSMPT();
$this->SMTPAuth = true;
$this->Host = 'aevumdecessus.com';
$this->Username = 'verification';
$this->Password = 'aS$h0l3';
}
function setBody($template, $layout)
{
$this->Body = $this->_render($template.'_html', $layout.'_html');
$this->AltBody = $this->_render($template.'_text', $layout.'_text');
}
function _render($template, $layout)
{
ob_start();
$this->controller->autoRender = false;
$this->controller->render($template, $layout);
$this->controller->autoRender = 'auto;
return ob_get_clean();
}
function __set($name, $value)
{
$this->m->{$name} = $value;
}
function __get($name)
{
if (isset($this->m->{$name})) {
return $this->m->{$name};
}
}
function __call($method, $args)
{
if (method_exists($this->m, $method)) {
return call_user_func_array(array($this->m, $method), $args);
}
}
}
?>
?>
and my controller:
Controller Class:
<?php$this->User->set($this->data);
$this->Mailer->controller = &$this;
$this->Mailer->init();
$this->set('token', $this->data['User']['email_token']);
$emails = $this->data['User']['email'];
$this->Mailer->AddAddress($emails);
$this->Mailer->Suject = 'Please Validate your account';
$this->Mailer->setBody('/email/validation', 'email');
if ($this->Mailer->send())
{
$errors = $this->Mailer->ErrorInfo;
//$this->User->save();
}
else
$errors = 'Error Sending: '.$this->Mailer->ErrorInfo;
$this->set('errors', $errors);
?>
Comment
19 Rendering Problem Solution
For those of you whom have been having the rendering issue with the blank emails going through due to a glitch somewhere with the output buffering, please have a look at my code below.
function getBody() {
$temp_layout = $this -> controller -> layout;
$this -> controller -> layout = '';
ob_start();
$this -> controller -> autoRender = false;
App::import('Core', array('View'));
$viewClass = new View($this -> controller);
$this -> template = $viewClass -> _getViewFileName($this -> template);
$content = $viewClass -> _render($this -> template, $this -> data);
echo $viewClass -> renderLayout($content, 'email');
$this -> controller -> autoRender = 'auto';
$body = ob_get_clean();
$this -> controller -> layout = $temp_layout;
return $body;
}
It might not be the right way to solve the issue but at least it works fine. I'm in a hurry to get my site fixed right now, but might digg in a bit more later on in order to pinpoint the actual issue.
All the best,
Antonie