I have an automated email system set up to send an html file as an email. I bring that file into my email with PHPMailer, using
$mail->msgHTML(file_get_contents('mailContent.html'), dirname(__FILE__));
In the PHP source, before I add the mailContent.html, I have a variable $name='John Appleseed' (it is dynamic, this is just an example)
In the HTML file, I'm wondering if there is a way that I can use this $name variable in a <p> tag.
You can add a special string like %name% in your mailContent.html file, then you can replace this string with the value your want:
In mailContent.html:
Hello %name%,
…
In your PHP code:
$name='John Appleseed';
$content = str_replace('%name%', $name, file_get_contents('mailContent.html'));
$content will have the value Hello %name%, …, you can send it:
$mail->msgHTML($content, dirname(__FILE__));
You can also replace several strings in one call to str_replace() by using two arrays:
$content = str_replace(
array('%name%', '%foo%'),
array($name, $foo),
file_get_contents('mailContent.html')
);
And you can also replace several strings in one call to strtr() by using one array:
$content = strtr(
file_get_contents('mailContent.html'),
array(
'%name%' => $name,
'%foo%' => $foo,
)
);
You need to use a templating system for this. Templating can be done with PHP itself by writing your HTML in a .php file like this:
template.php:
<html>
<body>
<p>
Hi <?= $name ?>,
</p>
<p>
This is an email message. <?= $someVariable ?>
</p>
</body>
</html>
Variables are added using <?= $variable ?> or <?php echo $variable ?>. Make sure your variables are properly escaped HTML using htmlspecialchars() if they come from user input.
And then to the template in your program, do something like this:
$name = 'John Appleseed';
$someVariable = 'Foo Bar';
ob_start();
include('template.php');
$message = ob_get_contents();
ob_end_clean();
$mail->msgHTML($message, dirname(__FILE__));
As well as using PHP for simple templating, you can use templating languages for PHP such as Twig.
Here is a function to do it using extract, and ob_*
extract will turn keys into variables with their value of key in the array. Hope that makes sense. It will turn array keys into variables.
function getHTMLWithDynamicVars(array $arr, $file)
{
ob_start();
extract($arr);
include($file);
$realData = ob_get_contents();
ob_end_clean();
return $realData;
}
Caller example:
$htmlFile = getHTMLWithDynamicVars(['name' => $name], 'mailContent.html');
In one of my older scripts I have a function which parses a file for variables that look like {{ variableName }} and have them replaced from an array lookup.
function parseVars($emailFile, $vars) {
$vSearch = preg_match_all('/{{.*?}}/', $emailFile, $vVariables);
for ($i=0; $i < $vSearch; $i++) {
$vVariables[0][$i] = str_replace(array('{{', '}}'), NULL, $vVariables[0][$i]);
}
if(count($vVariables[0]) == 0) {
throw new TemplateException("There are no variables to replace.");
}
$formattedFile = '';
foreach ($vVariables[0] as $value) {
$formattedFile = str_replace('{{'.$value.'}}', $vars[$value], $formattedFile);
}
return $formattedFile;
}
Do you have the $name variable in the same file where you send the mail? Then you can just replace it with a placeholder;
Place __NAME__ in the HTML file where you want the name to show up, then use str_ireplace('__NAME__', $name, file_get_contents('mailContent.html')) to replace the placeholder with the $name variable.
Related
In the context of processing html form input and responding by sending email via php, rather than having a lengthy heredoc assignment statement in the middle of my code, I'd like to have the email text in a file and read it in when needed, expanding the embedded variables as required.
Using appropriately prepared HTML form data input, I previously had…
$message = <<<TEXT
NAME: {$_POST["fname"]} {$_POST["lname"]}
POSTAL ADDRESS: {$_POST["postal"]}
EMAIL ADDRESS: {$_POST["email"]}
[... message body etc.]
TEXT;
Moving the text to a file I then tried…
$message = file_get_contents('filename.txt');
…but found that variables in the text are not expanded, resulting in output including the variable identifiers as literals.
Is there a way to expand the variables when reading the file into a string ?
There's probably a duplicate, if found I'll delete. But there are two ways that come to mind:
If you construct your file like this:
NAME: {fname} {lname}
Then loop the $_POST variables:
$message = file_get_contents('filename.txt');
foreach($_POST as $key => $val) {
$message = str_replace('{'.$key.'}', $val, $message);
}
If you construct your file like this:
NAME: <?= $_POST["fname"] ?> <?= $_POST["lname"] ?>
If HTML .txt or other (not .php) then:
ob_start();
$file = file_get_contents('filename.txt');
eval($file);
$message = ob_get_clean();
If it's a .php file then buffer the output and include:
ob_start();
include('filename.php');
$message = ob_get_clean();
Thanks to the help from AbraCadaver I put everything into a function which will handle the file and array, returning the composed message. I constructed the file to use [key] marking the places where data is to be inserted. Works like a charm. Thank you.
function compose_message($file, $array) {
// reads content of $file
// interpolates values from $array into text of file
// returns composed message
$message = file_get_contents($file);
$keys = array_keys($array);
foreach ($keys as $key) {
$message = str_replace("[$key]", $array[$key], $message);
}
return $message;
}
I am a new questioner but a long time reader and I can't seem to find a solution to my PHP code.
I am trying to send and email after a form contact has been submitted. Piece of cake until I decided I wanted my html email saved in a different file rather than having it defined as a string within my form process php file. I have to say I find it much more easier to edit the layout of the message this way.
It turns out it is no easy task, though.
I have "contact-form-process.php"
parsing the information submitted through the form;
adding that information to an array,
i.e.
$formdata = array(
'first-name' => $_POST['first-name'],
'last-name' => $_POST['last-name'],
'email' => $_POST['email'],
'message-subject' => filter_var($_POST['message-subject'], FILTER_SANITIZE_STRING),
'message-body' => filter_var($_POST['message-body'], FILTER_SANITIZE_STRING),
);
and "sender-copy.php" which
is the actual html email that I am sending out;
performs some simple operations with variables defined in "contact-form-process.php",
i.e.
<p>
Dear <?php $value = (isset($formdata['first-name']) ? $formdata['first-name'] : 'NOT-SET'),
</p>
Now back to "contact-form-process.php", in it I include the html email body as a string ($message = file_get_contents('sender-copy.php', TRUE);) and treat it to evaluate PHP code snippets it contains:
$message = preg_replace_callback (
'/<\?php(.+)\?>/',
function ($match) {
eval($match[1]);
return $value;
},
$message
);
So now I have difficulty in retrieving $formdata array values from the evaluated PHP snippets in the "sender-copy.php" string.
Here is a copy of my output:
<p>
Dear NOT-SET,
</p>
Anyone? Thank you.
Unlike JavaScript, PHP does not inherit variables from the parent scope. This means that inside your preg_replace_callback callback, $formdata does not exist.
Fortunately, there is an easy way to inherit it:
function($match) use ($formdata) { ... }
That said, I would strongly recommend changing your approach. You could do something like this:
ob_start();
require("sender-copy.php");
$message = ob_get_contents();
ob_end_clean();
Alternatively, and much more safely, you can change your "sender-copy" to this:
<p>Dear {first-name},</p>
And use this replacement method:
$message = preg_replace_callback("/\{([^}]+)\}/",function($m) use ($formdata) {
if( isset($formdata[$m[1]])) return $formdata[$m[1]];
return "NOT-SET";
},$message);
I have a php page that is reading from a file:
$name = "World";
$file = file_get_contents('html.txt', true);
$file = file_get_contents('html.txt', FILE_USE_INCLUDE_PATH);
echo $file;
In the html.txt I have the following:
Hello $name!
When I go to the site, I get "Hello $name!" and not Hello World!
Is there a way to get var's in the txt file to output their value and not their name?
Thanks,
Brian
The second param of file_get_contents has nothing to do with how to interpret the file - it's about which pathes to check when looking for that file.
The result, however, will always be a complete string, and you can only "reinterpolate" it with evial.
What might be a better idea is using the combination of include and output control functions:
Main file:
<?php
$name = "World";
ob_start();
include('html.tpl');
$file = ob_get_clean();
echo $file;
html.tpl:
Hello <?= $name ?>
Note that php tags (<?= ... ?>) in the text ('.tpl') file - without it $name will not be parsed as a variable name.
One possible approach with predefined values (instead of all variables in scope):
$name = "World";
$name2 = "John";
$template = file_get_contents ('html_templates/template.html');
$replace_array = array(
':name' => $name,
':name2' => $name2,
...
);
$final_html = strtr($template, $replace_array);
And in the template.html you would have something like this:
<div>Hello :name!</div>
<div>And also hi to you, :name2!</div>
To specifically answer your question, you'll need to use the 'eval' function in php.
http://php.net/manual/en/function.eval.php
But from a development perspective, I would consider if there was a better way to do that, either by storing $name somewhere more accessible or by re-evaluating your process. Using things like the eval function can introduce some serious security risks.
I am translating my website into different languages and I have over 130 pages so i want to pass my .php files through a function that will replace keywords
IE: Accessories = อุปกรณ์
Which is English to Thai.
I can get it to work using my method however... I have php (obviously) in these pages, and the output only displays the html and not executing the php
Is there a header method or something I have to pass at the start of my php pages..
here is the function I'm using to find text results and then replace them from my php files..
<?php
// lang.php
function get_lang($file)
{
// Include a language file
include 'lang_thai.php';
// Get the data from the HTML
$html = file_get_contents($file);
// Create an empty array for the language variables
$vars = array();
// Scroll through each variable
foreach($lang as $key => $value)
{
// Finds the array results in my lang_thai.php file (listed below)
$vars[$key] = $value;
}
// Finally convert the strings
$html = strtr($html, $vars);
// Return the data
echo $html;
}
?>
//This is the lang_thai.php file
<?php
$lang = array(
'Hot Items' => 'รายการสินค้า',
'Accessories' => 'อุปกรณ์'
);
?>
A lot of frameworks use a function to translate as it goes instead of replacing after the fact using .pot files. The function would look like this:
<h1><?php echo _('Hello, World') ?>!</h1>
So if it was English and not translated that function would just return the string untranslated. If it was to be translated then it would return the translated string.
If you want to continue with your route which is definitely faster to implement try this:
<?php
function translate($buffer) {
$translation = include ('lang_tai.php');
$keys = array_keys($translation);
$vals = array_values($translation);
return str_replace($keys, $vals, $buffer);
}
ob_start('translate');
// ... all of your html stuff
Your language file is:
<?php
return array(
'Hot Items' => 'รายการสินค้า',
'Accessories' => 'อุปกรณ์'
);
One cool thing is include can return values! So this is a good way to pass values from a file. Also the ob_start is an output buffer with a callback. So what happens is after you echo all of your html to the screen, right before it actually displays to the screen it passes all of that data to the translate function and we then translate all of the data!
I'm writing a mail class that pulls content stored in a database and loads it into a template before sending it as a HTML e-mail. However, because each e-mail contains PHP variables and dynamic content, I've decided to use delimiters. So instead of the content looking like:
Hello $username, welcome to the site.
It'll look like:
Hello {{username}}, welcome to the site.
So far I'm using these methods:
function load($name,$content)
{
// preps the template for HTML
}
function content($template_id)
{
$template = $this->db->get_where('email_templates',array('id'=>$template_id));
return $template->content;
}
function new_email($email,$name,$user_type)
{
$msg = $this->load($name,$this->content(1));
$this->send($email,'Thanks for your application',$msg,1);
}
The trouble I'm having is how to convert a {{variable}} into a $variable so that it will parse - I don't want it to just be loaded as $username in the e-mail template. Is it just a case of using regular expressions and escaping the string so that it'll parse? Something like:
$content = str_replace("{{","'.$",$template->content);
$content = str_replace("}}",".'",$template->content);
Or is this flawed? Does anybody know what's the best thing to do?
I would not create my own templating system, because there are existing ones out there.
The most popular is probably Smarty, but there is an another one which has the same format as you created, that is mustache.
Update:
The problem with your code is that you're replacing the {{ to a .$ and store that in $content variable, then replacing }} to . and overwrite this replaced $content variable.
A possible working solution could be:
if (preg_match_all("/{{(.*?)}}/", $template, $m)) {
foreach ($m[1] as $i => $varname) {
$template = str_replace($m[0][$i], sprintf('$%s', $varname), $template);
}
}
But then you would also need to eval your code somehow.
So after converting {{variable}} to $variable in your email template, you will use eval to get it replaced by the actual contents of that variable?
Why not just replace {{variable}} with the contents of $variable straight away?
Perhaps have a function that takes the template text and an array of placeholder => "text to replace it with". Then it's as simple as making up the placeholders' exact strings by adding {{ and }} around that array's key and doing str_replace.
foreach ($replacements as $placeholder => $value) {
$placeholder = "{{" . $placeholder . "}}" ;
$text = str_replace($placeholder, $value, $text) ;
}
Couple this with (class) constants for the placeholders and you have a very solid and typo-repelant templating system. It will not be as elegant or easy to use as a full blown templating solution, and it might require extra work from whoever writes code that uses it, but they will not make mistakes during development due to mis-named variables.
If you are going to do it yourself it is probably best to just be explicit with str_replace. If you try to convert the curly bracers to $ you'll then need to eval() which is a potential security hole.
This would be my approach with str_replace - this becomes difficult to maintain as you add more variables but it really doesn't get much simpler either.
$content = str_replace(
array('{{username}}','{{var2}}'),
array($username,$var2),
$template->content
);
use preg_replace_callback , see : http://codepad.org/EvzwTqzJ
<?php
$myTemplateStr = "Hello {{username}} , this is {{subject}} ,and other string {{example}}";
$tagRegex = "|{{(.*?)}}|is";
$result = preg_replace_callback($tagRegex,"myReplaceFunc",$myTemplateStr);
echo $result ;
/* output :
Hello $username , this is $subject ,and other string {{example}}
*/
function myReplaceFunc($matches)
{
$validTags = array('username','subject','name');
$theFull = $matches[0];
$theTag = $matches[1];
if(in_array($theTag,$validTags) == true)
return '$'.$theTag;
return $theFull ;
}
?>
$template = "Hello {{username}} , this is {{subject}} ,and other the answer is on page {{example}}";
$replacements = array(
'username' => 'Jeffrey',
'subject' => 'your final notice',
'page' => 43
);
function bind_to_template($replacements, $template) {
return preg_replace_callback('/{{(.+?)}}/',
function($matches) use ($replacements) {
return $replacements[$matches[1]];
}, $template);
}
echo bind_to_template($replacements, $template);
Credit to https://www.labnol.org/code/19266-php-templates