Email function using templates. Includes via ob_st

2019-02-19 03:29发布

I have a simple Email() class. It's used to send out emails from my website.

<?
Email::send($to, $subj, $msg, $options);
?>

I also have a bunch of email templates written in plain HTML pierced with a few PHP variables. E.g. /inc/email/templates/account_created.php:

<p>Dear <?=$name?>,</p>
<p>Thank you for creating an account at <?=$SITE_NAME?>. To login use the link below:</p>
<p><a href="https://<?=$SITE_URL?>/account" target="_blank"><?=$SITE_NAME?>/account</a></p>

In order to have the PHP vars rendered I had to include the template into my function. But since include does not return the contents but rather just sends it directly to the output, I had to wrap it with the buffer functions:

<?
abstract class Email {
    public static function send($to, $subj, $msg, $options = array()) {
        /* ... */
        ob_start();
        include '/inc/email/templates/account_created.php';
        $msg = ob_get_clean();
        /* ... */
    }
}

After that I realized that the PHP vars are not rendered as they are being inside of the function scope, so I had to globalize the variables inside of the template:

<?
global $SITE_NAME, $SITE_URL, $name;
?>
<p>Dear <?=$name?>,</p>
...

So the question is whether there is a more elegant solution to this? Mainly I am concerned about my workarounds using ob_start() and global. For some reason that seems to me odd. Or this is pretty much the common practice?

3条回答
时光不老,我们不散
2楼-- · 2019-02-19 03:47

One of the ways to do this is to read the file content to the variable and then, using regexps, replace the placeholders. So for example you have new-users-template.phtml. You reading it content with $content = file_get_content('new-users-templae.phtml');. In those template you will have placeholders like %%username%%, %%sitename%%, %%siteurl%%. You need to process this content with str_replace, replacing the placeholders. Move this code to some "prepareEmailTemplate" function and put the result of this function to your "send" function.

查看更多
啃猪蹄的小仙女
3楼-- · 2019-02-19 04:00

You can find a more elegant solution to your problem in this answer.
Notice the usage of the PHP extract function to instantiate the template variables.
In other words, you should move the template parsing logic outside the e-mail sending function.
For example:

<?php

class SimpleTemplate {
    private $_tpl  = "";
    private $_vars = array();

    function __construct($tpl_name) {
         $this->_tpl = $tpl_name;
    }

    public function __set($name, $value) {
        $this->_vars[$name] = $value;
    }

    public function setVars($values) {
        $this->_vars = $values;
    }

    public function parse() {
        ob_start();
        extract($this->_vars);
        include $this->_tpl;
        return ob_get_clean();
    }
}

abstract class Email {
    public static function send($to, $subj, $msg, $options = array()) {
        /* ... */
    }
}

$tpl = new SimpleTemplate('/inc/email/templates/account_created.php');
$tpl->name = 'Stack Overflow';
$tpl->SITE_NAME = 'site_name';
$tpl->SITE_URL = 'localhost';
Email::send("me@localhost", "Subject", $tpl->parse());

?>
查看更多
SAY GOODBYE
4楼-- · 2019-02-19 04:13

One solution is to globalize the variables needed not inside the template but inside the function send.

public static function send($to, $subj, $msg, $options = array()) {
    global $SITE_NAME, $SITE_URL, $name;

    /* ... */
    ob_start();
    include '/inc/email/templates/account_created.php';
    $msg = ob_get_clean();
    /* ... */
}

Another solution is to pass those extra variables as parameters. This maybe ugly as the number of parameters in the set function may grow a lot depending on how many of them you need in your template. To solve this issue, another way of implementing this solution is by passing those extra variables as a hash and create those variables on-the-fly (using the function eval). Here's an example:

public static function send($to, $extra_vars = array()) {
    foreach ($extra_vars as $key => $value) {
        eval("\$$key = '$value';");
    }

    /* ... */
    ob_start();
    include '/inc/email/templates/account_created.php';
    $msg = ob_get_clean();
    /* ... */
}

Then when you should call send like this:

$SITE_NAME = "www.somewebsite.com";
Email::send("recipient", array('SITE_NAME' => $SITE_NAME));
查看更多
登录 后发表回答