【问题标题】:How to model a templating class如何为模板类建模
【发布时间】:2010-01-17 13:10:58
【问题描述】:

我正在为我自己的项目编写一个基本的模板类。基本用法是这样的:

$template = new Template('template_file.php');
$template->assignVariable('pageTitle', 'Home page');
$template->render();

'template_file.php'的内容:

<?php print $pageTitle; ?>

这是模板类一步一步做的:

  1. 在调用 assignVariable 方法时将变量存储在私有数组中
  2. 调用渲染方法时,提取存储的变量,将模板文件包含在ob_start()ob_end_clean() 块中。将输出存储在带有ob_get_contents() 的变量中,然后打印存储的输出。

我知道这是一个非常简单的模板类,但可以按预期工作。问题是我应该将包含模板文件的内容委托给另一个类吗?当我为这个类编写单元测试时,我遇到了这个问题。我认为应该封装文件系统交互。你怎么看?如果您认为不应该,我该如何模拟在我的测试中包含一个文件?

也许我只是像这样将模板文件的内容传递给类:

$templateContent = file_get_contents('template_file.php');
$template = new Template($templateContent);
...

编辑:为了编写更好的单元测试和封装,我决定封装模板类的输入过程。但正如 johannes 指出的那样,我需要使用 eval() 来达到这个目的,这似乎是不正确的。 Johannes 向我指出了流包装器的方向,用于模拟单元测试中的包含。但这激发了我的一个新想法。这是我要做的;我将继续在我的模板类中使用include(),但这次使用流包装器。我将在初始化时将协议处理程序传递给我的模板类。通过这种方式,我可以创建自己的流包装器,用于从数据库中获取模板数据或使用局部变量。以下是示例:

$template = new Template('file://template_file.php');

stream_wrapper_register('database', 'My_Database_Stream');
$template = new Template('database://templates/3'); // templates table, row id 3

stream_wrapper_register('var', 'My_Var_Stream');
$myTemplate = '<?php print "Hello world!"; ?>';
$template = new Template('var://myTemplate');

我已经为局部变量实现了自定义流包装器。这里是:

class My_Var
{
    protected $position;
    protected $variable;
    function stream_open($path, $mode, $options, &$openedPath) {
        $url = parse_url($path);
        global $$url['host'];
        $this->variable = $$url['host'];
        $this->position = 0;

        return true;
    }
    public function stream_read($count) {
        $ret = substr($this->variable, $this->position, $count);
        $this->position = strlen($ret);
        return $ret;
    }
    public function stream_eof() {
        return $this->position >= strlen($this->variable);
    }
}


stream_wrapper_register('var', 'My_Var');
$myvar = '<?php print "mert"; ?>';
include 'var://myvar';
exit;

【问题讨论】:

    标签: php unit-testing


    【解决方案1】:

    我一直很喜欢这家伙的做法:

    http://www.massassi.com/php/articles/template_engines/

    这种方法利用了 PHP 已开始作为模板引擎这一事实。 (作者还指出,用 PHP 编写一个臃肿的模板引擎是愚蠢的,而实际上它本身就是一个模板引擎。)它可能无法真正直接回答您的问题,但也许对您有所帮助。

    【讨论】:

    • 我的模板类和他的很相似,其实是一样的。但我的问题是我应该封装包括文件吗?如果我需要解析从 db 检索到的文本怎么办?
    • 上述链接中描述的方法与 Zend Framework 中视图脚本的处理方式相差不到一百万英里。视图脚本位于父视图对象的上下文中,允许您调用(然后通常代理到插件)帮助方法。例如,如果您需要转义,这很有帮助(您可以只做 escape($this->var);)。
    【解决方案2】:

    通过使用 file_get_contents() 传递内容,您必须使用 eval() 执行,这在多个方面都不好。这里最相关的一个是操作码缓存不能缓存文件。做一个 include('template_file.php');让我们APC或其他人缓存编译好的脚本。

    【讨论】:

    • 是的,但是当使用包含功能时,我需要在我的测试文件夹中创建一个虚拟模板文件。我也不应该只测试模板类吗?另外我将如何模拟包含功能?
    • 您可以使用自定义流包装器或数据提供者来发送模拟数据。 php.net/stream_wrapper_registerphp.net/manual/en/wrappers.data.php
    • 嗨,约翰内斯,谢谢!你启发了我一个新的想法。我已经编辑了问题并添加了我想出的解决方案。
    【解决方案3】:

    不管 eval 与使用包含的弊端如何,为了回答您的问题,我必须同意并使用一个单独的类来封装您解决方案的 I/O 方面。

    虽然这可能看起来有点矫枉过正(在大多数情况下都会如此),但这可能是提供您所追求的控制/依赖注入隔离的唯一明智的方法。

    【讨论】:

      【解决方案4】:

      问题是我应该将包含模板文件委托给另一个类吗?

      问题是,为什么不呢?

      【讨论】:

      • 当然不是矫枉过正。我想出了一个完全封装我的模板类的解决方案。我已经为我的问题添加了解决方案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多