I have a function that sends out site emails (using phpmailer), what I want to do is basically for php to replace all the placheholders in the email.tpl file with content that I feed it. The problem for me is I don't want to be repeating code hence why I created a function (below).
Without a php function I would do the following in a script
// email template file
$email_template = "email.tpl";
// Get contact form template from file
$message = file_get_contents($email_template);
// Replace place holders in email template
$message = str_replace("[{USERNAME}]", $username, $message);
$message = str_replace("[{EMAIL}]", $email, $message);
Now I know how to do the rest but I am stuck on the str_replace(), as shown above I have multiple str_replace() functions to replace the placeholders in the email template. What I would like is to add the str_replace() to my function (below) and get it to find all instances of [\] in the email template I give it and replace it with the placeholders values that I will give it like this: str_replace("[\]", 'replace_with', $email_body)
The problem is I don't know how I would pass multiple placeholders and their replacement values into my function and get the str_replace("[{\}]", 'replace_with', $email_body) to process all the placeholders I give it and replace with there corresponding values.
Because I want to use the function in multiple places and to avoid duplicating code, on some scripts I may pass the function 5 placeholders and there values and another script may need to pass 10 placeholders and there values to the function to use in email template.
I'm not sure if I will need to use an an array on the script(s) that will use the function and a for loop in the function perhaps to get my php function to take in xx placeholders and xx values from a script and to loop through the placeholders and replace them with there values.
Here's my function that I referred to above. I commented the script which may explain much easier.
// WILL NEED TO PASS PERHAPS AN ARRAY OF MY PLACEHOLDERS AND THERE VALUES FROM x SCRIPT
// INTO THE FUNCTION ?
function phpmailer($to_email, $email_subject, $email_body, $email_tpl) {
// include php mailer class
require_once("class.phpmailer.php");
// send to email (receipent)
global $to_email;
// add the body for mail
global $email_subject;
// email message body
global $email_body;
// email template
global $email_tpl;
// get email template
$message = file_get_contents($email_tpl);
// replace email template placeholders with content from x script
// FIND ALL INSTANCES OF [{}] IN EMAIL TEMPLATE THAT I FEED THE FUNCTION
// WITH AND REPLACE IT WITH THERE CORRESPOING VALUES.
// NOT SURE IF I NEED A FOR LOOP HERE PERHAPS TO LOOP THROUGH ALL
// PLACEHOLDERS I FEED THE FUNCTION WITH AND REPLACE WITH THERE CORRESPONDING VALUES
$email_body = str_replace("[{\}]", 'replace', $email_body);
// create object of PHPMailer
$mail = new PHPMailer();
// inform class to use smtp
$mail->IsSMTP();
// enable smtp authentication
$mail->SMTPAuth = SMTP_AUTH;
// host of the smtp server
$mail->Host = SMTP_HOST;
// port of the smtp server
$mail->Port = SMTP_PORT;
// smtp user name
$mail->Username = SMTP_USER;
// smtp user password
$mail->Password = SMTP_PASS;
// mail charset
$mail->CharSet = MAIL_CHARSET;
// set from email address
$mail->SetFrom(FROM_EMAIL);
// to address
$mail->AddAddress($to_email);
// email subject
$mail->Subject = $email_subject;
// html message body
$mail->MsgHTML($email_body);
// plain text message body (no html)
$mail->AltBody(strip_tags($email_body));
// finally send the mail
if(!$mail->Send()) {
echo "Mailer Error: " . $mail->ErrorInfo;
} else {
echo "Message sent Successfully!";
}
}
Simple, see strtrĀDocs:
$vars = array(
"[{USERNAME}]" => $username,
"[{EMAIL}]" => $email,
);
$message = strtr($message, $vars);
Add as many (or as less) replacement-pairs as you like. But I suggest, you process the template before you call the phpmailer function, so things are kept apart: templating and mail sending:
class MessageTemplateFile
{
/**
* #var string
*/
private $file;
/**
* #var string[] varname => string value
*/
private $vars;
public function __construct($file, array $vars = array())
{
$this->file = (string)$file;
$this->setVars($vars);
}
public function setVars(array $vars)
{
$this->vars = $vars;
}
public function getTemplateText()
{
return file_get_contents($this->file);
}
public function __toString()
{
return strtr($this->getTemplateText(), $this->getReplacementPairs());
}
private function getReplacementPairs()
{
$pairs = array();
foreach ($this->vars as $name => $value)
{
$key = sprintf('[{%s}]', strtoupper($name));
$pairs[$key] = (string)$value;
}
return $pairs;
}
}
Usage can be greatly simplified then, and you can pass the whole template to any function that needs string input.
$vars = compact('username', 'message');
$message = new MessageTemplateFile('email.tpl', $vars);
The PHP solutions can be:
usage of simple %placeholder% replacement mechanisms:
str_replace,
strtr,
preg_replace
use of pure PHP templating and conditional logic (short open tags in PHP and alternative syntax for control structures)
Please, find the wide answer at Programmers.StackExchange to find out other approaches on PHP email templating.
Why dont you just make the email template a php file aswell? Then you can do something like:
Hello <?=$name?>, my name is <?=$your_name?>, today is <?=$date?>
inside the email to generate your HTML, then send the result as an email.
Seems to me like your going about it the hard way?
Related
I am using the ExtendedMailMessage class to try and send an email with an attachments to my clients. The issue is I am sending the email inside a foreach loop and for some reason even though I am defining a new attachment the new attachment is just being appended to an array and each client gets multiple attachments instead of one.
$extendedMailer = $this->ci->extendedMailer;
foreach ($emails as $email) {
$attachment = new MailAttachment(
base64_decode($base64String), "report.pdf"
);
try {
$message = new ExtendedTwigMailMessage($this->ci->view, 'mail/pdf-reports.html.twig');
$message->from($config['address_book.admin'])
->addEmailRecipient(new EmailRecipient($email, 'Client'))
->setFromEmail($config['address_book.admin'])
->setReplyEmail($config['address_book.admin'])
->addAttachment($attachment);
$extendedMailer->sendDistinct($message);
}
catch(\Exception $ex) {
var_dump($email);
}
}
The first client will receive 1 attachment and then the 2nd will receive 2 attachments, 3rd will receive 3 attachments etc...
How do I just send 1 attachement with each email instead of appending it to the old attachments
Since PHPMailer instance is reused for each email, anything set on PHPMailer is carried on to the next email. When the Mailer's send or sendDistinct methods are called, only the recipients are explicitly cleared. So in your case, you might need to explicitly clear the attachement from $message between each email (depending how your addAttachment is implemented).
Another solution might be to move the addAttachment call outside the loop, assuming each email as the same attachement :
$extendedMailer = $this->ci->extendedMailer;
$attachment = new MailAttachment(
base64_decode($base64String), "report.pdf"
);
$message = new ExtendedTwigMailMessage($this->ci->view, 'mail/pdf-reports.html.twig');
$message->from($config['address_book.admin'])
->setFromEmail($config['address_book.admin'])
->setReplyEmail($config['address_book.admin'])
->addAttachment($attachment);
foreach ($emails as $email) {
try {
$message->addEmailRecipient(new EmailRecipient($email, 'Client'));
$extendedMailer->sendDistinct($message);
}
catch(\Exception $ex) {
var_dump($email);
}
}
Note that the following fix will be introduced in UserFrosting V5 send/sendDistinct to avoid this issue :
// Clone phpMailer so we don't have to reset it after sending.
$phpMailer = clone $this->phpMailer;
i am trying to understand why my subject method in index.php triggers an error of not being defined.i am using phpmailer 5.2.7 with php 7.2 and wampserver 3.1.7
//here is my extended class from phpmailer//
<?php
include('phpmailer.php');
class Mail extends PhpMailer
{
// Set default variables for all new objects
public $From = 'xxxxxx#gmail.com';
public $FromName = MM;
public $Host = 'smtp.gmail.com';
public $Mailer = 'smtp';
public $SMTPAuth = true;
public $Username = 'xxxxxxx#gmail.com';
public $Password = 'xxxxxx';
public $SMTPSecure = 'ssl';
public $WordWrap = 75;
public function subject($subject)
{
$this->Subject = $subject;
}
public function body($body)
{
$this->Body = $body;
}
public function send()
{
$this->AltBody = strip_tags(stripslashes($this->Body))."\n\n";
$this->AltBody = str_replace(" ", "\n\n", $this->AltBody);
return parent::send();
}
}
and here is part of my index page where i have defined my variables
$to = $_POST['email'];
$subject = "Registration Confirmation";
$body = "<p>Thank you for registering at demo site.</p>
<p>To activate your account, please click on this link: <a href='".DIR."activate.php?x=$id&y=$activasion'>".DIR."activate.php?x=$id&y=$activasion</a></p>
<p>Regards Site Admin</p>";
$mail = new PHPMailer(true);
$mail->setFrom(SITEEMAIL);
$mail->addAddress($to);
$mail->subject($subject);
$mail->body($body);
$mail->send();
//redirect to index page
header('Location: index.php?action=joined');
exit;
Firstly, why are you using a version of PHPMailer that's literally years out of date? Get the latest, which has new features, fixed bugs, and fewer security holes. While you're upgrading, consider switching to using composer to manage your dependencies.
The problem you're having is quite straightforward: you have created a subclass that adds the subject() method, but the instance you've created in your script is of the original PHPMailer class, not your subclass. Do this instead:
$mail = new Mail(true);
Naming your class with a very generic "Mail" name is very likely to bring you an unexpected lesson on why namespacing is a good idea, so I'd recommend adding a namespace for your app to avoid name clashes.
While it's a good idea to subclass like this to set default values easily, it's also inviting you to check in credentials to your source repo, which is usually a bad idea. Better to use your child class to read those values from an environment file ("dot env") using a package like this.
When I'm building and testing my website on local server, I would like to emulate successful sending via phpmailer, so I avoid actually sending emails.
I normally use if ($mail->Send()) { the mail was sent, now do this }.
For local testing I think the best would be to skip the whole phpmailer inclusion, instead of adding a lot of if statements etc.
But skipping phpmailer would then cause php to complain about $mail->Send(), $mail->addAddress('emailaddress') etc.
How could I fake the function (or object/class) so that calls to $mail->Send() are always true, and the rest $mail->something() etc. are just ignored/true, so that no email is sent?
Extend the PHPMailer class and override the public function send().
class UnitTestMailer extends PHPMailer {
public function send() {
return $this;
}
}
class User {
public function __construct(PHPMailer $mailer) {
$this->mailer = $mailer;
}
public function sendActiviation() {
return $this->mailer->send();
}
}
// ... somewhere in your test
public function test_if_from_is_properly_set() {
// ...
$user = new User(new UnitTestMailer);
// ...
$mailer = $user->sendActivation();
$this->assertEquals($expectedFrom, $mailer->From);
}
Why emulate?
I use INI files to provide configuration variables for PHPMailer depending on the environment. Live obviously has the server's mail settings. Local uses my Gmail account credentials to send mail.
You can have the best of both worlds :)
Keep a config.ini file for example somewhere in your working directory (I tend to use root but that's preference) and make sure it's in your .gitignore or similar. Your local version would look something like:
[PHPMailer Settings]
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_SMTPSECURE = "tls"
EMAIL_SMTPAUTH = "true"
EMAIL_USERNAME = "my_email#gmail.com"
EMAIL_PASSWORD = "my_password"
then in your PHP:
$ini = parse_ini_file($_SERVER['DOCUMENT_ROOT'] . "/config.ini", true, INI_SCANNER_TYPED);
// use settings from INI file:
$foo = $ini['PHPMailer Settings']['EMAIL_HOST'];
$bar = $ini['PHPMailer Settings']['EMAIL_PORT'];
Security Bonus
Change the syntax of your INI file to look like the below and rename it to config.ini.php:
; <?php
; die();
; /*
[PHPMailer Settings]
EMAIL_HOST = "smtp.gmail.com"
EMAIL_PORT = 587
EMAIL_SMTPSECURE = "tls"
EMAIL_SMTPAUTH = "true"
EMAIL_USERNAME = "my_email#gmail.com"
EMAIL_PASSWORD = "my_password"
; */ ?>
(remember to use the new filename in your PHP code)
PHP can still parse the settings, but if anyone tried to access the INI file it would be parsed as PHP comments and just show ";"
I use swift mailer for send email, and after send email in box I have mail without html, like in screen
but in another mail services everything fine
this my code
public function createMessage($subject, $receivers, $template, $context)
{
$message = \Swift_Message::newInstance($subject);
$message->setFrom($this->from_address);
$message->setTo($receivers);
$body = $this->twig->render($template, $context);
$plaintext = strip_tags($body);
$message->setBody($body, "text/html");
$message->addPart($plaintext, "text/plain");
$this->mailer->send($message);
}
What problem in this code? I set body test/html. What problem not understand
Try with this:
public function __construct(Container $oContainer) {
$this->oContainer = $oContainer;
}
$this->oContainer->get('templating')->render()
And set the content yourself:
$message->setContentType('text/html');
When sending a mail using Mail::queue / Mail::send you have to pass a mail template and the subject separately.
Is there a way to manage the subject in the mail templates (better for multi-languages).
I.e. as the first line in the template
mail.blade.php
This is the subject
Hello User,
foobar
It's not that hard:
Mail::queue($template, $data, function (Message $message) use ($toUser, $sendingName, $sendingAddress) {
// take subject from first line of the template
$body = $message->getSwiftMessage()->getBody();
$bodyLines = explode("\n", $body);
if (count($bodyLines) == 0) {
Log::warning('Empty mail');
return;
}
$subject = $bodyLines[0];
unset($bodyLines[0]);
// send
$message->getSwiftMessage()->setBody(implode("\n", $bodyLines));
....