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
$this->Mailer->errorInfo;should be:$this->Mailer->ErrorInfo;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
Now its possible to use component exactly like PHPmailer object.vendor('phpmailer/class.phpmailer');
class MailerComponent extends PHPMailer
{}
?>
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
Comment
20 Further solution
I replaced $this->data with $this->controller->viewVars to effectively gain access to any view variables in my email layout.
Works great, thanks for all the good posts!
Comment
21 cant get it to run...
but no matter what i do, it does not work
either i get a plain text result like this:
(sent by mail):
From: MyName
X-Mailer: PHPMailer (phpmailer.codeworxtech.com) [version 2.3] MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"
or some error error trying to get some template to work:
Indirect modification of overloaded property MailerComponent::$controller has no effect
etc..
has anybody a full working phpmailer component running?
maybe even with some example calls
would be very helpful
thx