简单的模板var替换,但扭曲

时间:2010-07-01 14:29:39

标签: php arrays templates loops str-replace

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

以下是一个简短的例子:

// 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美元。   在example / A

了解更多信息      

我们的产品WidgetB的成本为2美元。   在example / B

了解更多信息      

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

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

2 个答案:

答案 0 :(得分:4)

好吧,我真的没有看到使用repalcements / regex的模板引擎中的重点

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中的所有数据和方法。


的index.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混合但是这没关系,因为你应该只使用Foreach,For等基本内容和变量。


注意:在TemplateLoader类中,您可以添加类似..

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

这将允许您在模板中增加您的操作,使其变为粗体,斜体,atuoloop,css_secure,striplashs ..

你仍然拥有所有常规工具,例如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.
    }
}
?>

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

答案 1 :(得分: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} [这里的任何东西] {/ products}”。

输入字符串如下所示:

Requested products: {products}%product_name%{/products}