1

因此,我正在建立一个系统,其中包含大量电子邮件和变量替换,因此我正在编写一个类来管理存储在数据库中的模板的一些变量替换。

这是一个简短的例子:

// template is stored in db, so that's how this would get loaded in 
$template = "Hello, %customer_name%, thank you for contacting %website_name%"; 
// The array of replacements is built manually and passed to the class 
// with actual values being called from db 
$replacements = array('%customer_name%'=>'Bob', '%website_name%'=>'Acme'); 
$rendered = str_replace(array_keys($replacements), $replacements, $template); 

现在,这适用于单个 var 替换,基本的东西。但是,有些地方应该有一个 for 循环,我不知道如何实现它。

这个想法是有一个这样的模板:

“您好,%customer_name%,感谢您索取有关 {products} 的信息”

其中,{products} 将是一个传递给模板的数组,它会循环用于请求的产品,格式如下:

我们的产品 %product_name% 的成本为 %product_price%。在 %product_url% 了解更多信息。

因此,一个示例渲染版本将是:

“你好,鲍勃,感谢您要求提供以下信息:

我们的产品 WidgetA 的成本为 1 美元。在示例/A 中了解更多信息

我们的产品 WidgetB 的成本为 2 美元。在示例/B 中了解更多信息

我们的产品 WidgetC 的成本为 3 美元。在示例/C 中了解更多信息。

实现这一目标的最佳方法是什么?

4

2 回答 2

4

好吧,我真的没有看到使用替换/正则表达式的模板引擎的意义

PHP 已经是一个模板引擎,当你写<?php echo $var?>它就像做<{$var}>{$var}

这样想,PHP 已经被它的引擎翻译<?php echo '<b>hello</b>'?><b>hello</b>,那为什么要让它做所有事情 2 次以上。

我实现模板引擎的方式是这样的

首先创建一个模板类

class Template
{
   var $vars = array();

   function __set($key,$val)
   {
      $this->vars[$key] = $val;
   }

   function __get($key)
   {
     return isset($this->vars[$key]) ? $this->vars[$key] : false;
   }

   function output($tpl = false)
   {
      if($tpl === false)
      {
         die('No template file selected in Template::output(...)');
      }

      if(!file_exists(($dir = 'templates/' . $tpl . '.php')))
      {
         die(sprintf('Tpl file does not exists (%s)',$dir));
      }

      new TemplateLoader($dir,$this->vars);
      return true;
   }
}

这是您在登录时使用的,例如 index.php,如果您不确定,您将像stdClass一样设置数据。当您运行输出命令时,它会将数据和 tpl 发送到下面的下一个类。


然后创建一个独立的类来编译其中的 tpl 文件。

class TemplateLoader
{
    private $vars = array();
    private $_vars = array(); //hold vars set within the tpl file
    function __construct($file,$variables)
    {
        $this->vars = $variables;
        //Start the capture;
        ob_start();
           include $file;
           $contents = ob_get_contents();
        ob_end_clean(); //Clean it

       //Return here if you wish
       echo $contents;
    }

    function __get($key)
    {
        return isset($this->vars[$key]) ? $this->vars[$key] : (isset($this->_vars[$key]) ? $this->_vars[$key] : false) : false;
    }

    function __set($key,$val)
    {
       $this->_vars[$key] = $val;
       return true;
    }

   function bold($key)
   {
      return '<strong>' . $this->$key . '</string>';
   }
}

我们保持这个分开的原因是它有自己的空间来运行,你只需将你的 tpl 文件作为包含在你的构造函数中加载,因此它只能加载一次,然后当文件被包含时,它可以访问所有数据和 TemplateLoader 中的方法。


索引.php

<?php
   require_once 'includes/Template.php';
   require_once 'includes/TemplateLoader.php';


   $Template = new Template();

   $Template->foo = 'somestring';
   $Template->bar = array('some' => 'array');

   $Template->zed = new stdClass(); // Showing Objects

   $Template->output('index'); // loads templates/index.php
?>

现在在这里我们真的不想将 html 与此页面混合,因为通过分离 php 和视图/模板,您可以确保所有 php 都已完成,因为当您发送 html 或使用 html 时,它会阻止脚本的某些方面运行。


模板/index.php

header

    <h1><?php $this->foo;?></h1>
    <ul>
        <?php foreach($this->bar as $this->_foo):?>
            <li><?php echo $this->_foo; ?></li>
        <?php endforeach; ?>
    </ul>
     <p>Testing Objects</p>
     <?php $this->sidebar = $this->foo->show_sidebar ? $this->foo->show_sidebar : false;?>
     <?php if($this->sidebar):?>
        Showing my sidebar.
     <?php endif;?>
footer

现在在这里我们可以看到将 html 与 php 混合,但这没关系,因为在 ehre 中您应该只使用基本的东西,例如 Foreach、For 等和变量。


注意:在 TemplateLoader 类中,您可以添加一个函数,如..

function bold($key)
{
   return '<strong>' . $this->$key . '</string>';
}

这将允许您在模板中增加您的操作,如粗体、斜体、自动循环、css_secure、stripslashs..

您仍然拥有所有常规工具,例如 stripslashes/htmlentites 等。

这是一个粗体的小例子。

$this->bold('foo'); //Returns <strong>somestring</string>

您可以在 TempalteLoader 类中添加许多工具,例如 inc() 来加载其他 tpl 文件,您可以开发一个帮助系统,这样您就可以去$this->helpers->jquery->googleSource

如果您还有任何问题,请随时问我。

----------

存储在数据库中的示例。

<?php
if(false != ($data = mysql_query('SELECT * FROM tpl_catch where item_name = \'index\' AND item_save_time > '.time() - 3600 .' LIMIT 1 ORDER BY item_save_time DESC')))
{
    if(myslq_num_rows($data) > 0)
    {
       $row = mysql_fetch_assc($data);
       die($row[0]['item_content']);
    }else
    {
       //Compile it with the sample code in first section (index.php)
       //Followed by inserting it into the database
       then print out the content.
    }
}
?>

如果您希望存储包括 PHP 在内的 tpl 文件,那么这不是问题,在您传入 tpl 文件名的模板中,只需搜索 db 而不是文件系统

于 2010-07-01T14:53:33.450 回答
0
$products = array('...');
function parse_products($matches)
{
    global $products;
    $str = '';
    foreach($products as $product) {
       $str .= str_replace('%product_name%', $product, $matches[1]); // $matches[1] is whatever is between {products} and {/products}
    }
    return $str;
}

$str = preg_replace_callback('#\{products}(.*)\{/products}#s', 'parse_products', $str);

这个想法是在 {products} 和 {products} 之间找到字符串,将它传递给某个函数,做任何你需要做的事情,迭代 $products 数组。无论函数返回什么,都会替换整个“{products}[anything here]{/products}”。

输入字符串如下所示:

Requested products: {products}%product_name%{/products}
于 2010-07-01T14:43:43.697 回答