4

所以我一直在研究模板引擎以及如何创建自己的简单模板引擎。从纯粹的学习角度来看,我在这里阅读了其中的几个。

使用上面链接中提到的类的一个小修改版本,我想测试它但遇到了一个问题。

当为内部 HTML 调用同一模板类的实例,然后将其作为 var/value 对分配给父实例时,我无法访问 HTML(子对象)中的主要父级变量。

令人困惑?

也许下面的代码会有所帮助。

因此,如果我这样实例化模板(模板类与上面链接中提到的相同)-

$_page = new tplEngine();
$_page->load(TPLFILES_DIR . "/root.php");

然后将 header.html 实例化为 tplEngine 类的新实例,并将其作为变量分配给第一个实例,如下所示 -

$_header = new tplEngineChild(TPLFILES_DIR . "/common/header.html");
$_page->set("header", $_header->parse());

在哪里...

root.php
---------------

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
        <title><?php print $this->title; ?></title>
        <meta name="keywords" content="<?php print $this->meta_keywords; ?>" />
    <meta name="description" content="<?php print $this->meta_description; ?>" />
        <?php foreach($this->styles as $stylepath) : ?>
        <link rel="stylesheet" type="text/css" href="<?php print $stylepath; ?>" />
        <?php endforeach; ?>
    </head>
    <body>
        <div class="body-wrap">
            <div class="header">
                <?php print $this->header; ?>
            </div>
            <div class="content-wrap">
                <?php var_dump($this->mid_content); ?>
            </div>
            <div class="footer">
                <?php print $this->footer; ?>
            </div>
        </div>
    </body>
</html>

header.html
-----------------

<div class="mainHeader">
    <div class="logo">
        webTrack.in'
    </div>
    <div class="dashboard">

        <?php if($this->get(isLoggedIn) == false) : ?>
        <p class="greeting">Hello <span class="username"><?php echo this->username; ?></span></p>
        <a class="logout">logout</a>
        <?php else : ?>
        <p class="greeting">Hello <span class="username"><?php echo $this->username; ?></span></p>
        <p><a onclick="showLogin()">Login</a></p>
        <form id="loginForm" class="login form" action="" method="post">
            <input type="text" name="username" value="Username" />
            <input type="password" name="password" value="Password" />
        </form>
        <?php endif; ?>
    </div>
</div>
<nav>
    <ul class="headerNav">
        <li><a href="/">Home</a></li>
        <li><a href="/pricing">Plans and Pricing</a></li>
        <li><a href="/aboutUs">About Us</a></li>
    </ul>
</nav>

(在上述情况下$this->get(isLoggedIn),并且this->username是分配给 $_page 实例的变量)我遇到了一个问题,在 header.html 文件中,我无法访问在 tplEngine 类的 $_page 实例下设置的变量。

解决这个问题的最佳方法是什么?

$_page当我在 header.html中将实例设置为全局时,一切正常。但这是正确的做法吗?

4

5 回答 5

3

关于对象继承

类是对象的模板,定义了对象的属性和方法,而对象是类的实例。扩展类时,子类会从父类继承属性和方法。

在您的情况下,没有继承(父子关系),$_header因为单独的对象只是 a 的属性$_page。要启用这两个对象之间的“通信”,$_header必须具有对$_page对象的引用。


模板类

这是您正在使用的 Template 类的修改版本。当您动态创建属性时,__set()应该使用__get() 魔术方法。它还使用__toString()方法,以便可以将模板对象视为字符串。模板文件使用的所有变量都应该分配给模板对象。通过使用这样定义的类,所有模板都会同时呈现。

class tplEngine {
    private $template = '';

    public function __set( $var, $content )
    {
        $this->$var = $content;
    }

    public function __get( $var )
    {
        return ( isset($this->$var) ? $this->$var : null );
    }

    public function __construct( $template )
    {
        // is_readable() - tells whether a file exists and is readable
        if ( !is_readable( $template ) ) throw new IOException( "Could not find or access file: $template" );
        $this->template = $template;
    }

    public function __toString() 
    {
        ob_start();
        require ( $this->template );
        $content = ob_get_clean();
        return $content;
    }
}

// usage:
$_page = new tplEngine( TPLFILES_DIR . "/root.php" );

$_header = new tplEngine( TPLFILES_DIR . "/common/header.html" );
$_header->isLoggedIn = true;
$_header->username = 'some-username';

$_page->header = $_header;

// in root.php 
echo $this->header;

访问父变量

父属性

访问“父”对象中的变量的一种方法是通过构造函数将父属性添加到模板类:

public function __construct( $template, $parent = null )
{
    // is_readable() - tells whether a file exists and is readable
    if ( !is_readable( $template ) ) throw new IOException( "Could not find or access file: $template" );
    $this->template = $template;
    $this->_parent = $parent;
}   

访问模板中的父属性,例如:

$this->_parent->username;

使父属性本地化

另一种方法是使它们成为本地(如果您不想打扰$this->_parent电话,那就巧妙的技巧):

public function __toString() 
{
    ob_start();
    if ( $this->_parent ) {
        foreach ( get_object_vars( $this->_parent ) as $key => $value ) $$key = $value;
    }
    require ( $this->template );
    $content = ob_get_clean();
    return $content;
}

附加信息

适配器设计模式

PHP 重载

魔术方法

Smarty 模板引擎 - 变量范围

于 2013-05-11T16:32:46.897 回答
1

the problem is that $_header is not a child of $_page in the means of php class inheritance and you dont want them to be true php parent and child.

instead, change the tplEngineChild constructor to take a $parent as additional argument, in this case $_page.

constructor could look like this:

function __construct($parent = null, $template = null)
{
    if(isset($parent))
    {
        $this->parent = $parent;
    }

    if (isset($template))
    {
        $this->load($template);
    }
}

now $header could use $this->parent->username. Be sure to have "public $username" in your parent class definition. You could use the php method overloading (_get) to automatically resolve the properties at the parent if it does not exist in the child.

Also you could pass $_header instead of $_header->publish(); to $_page->set and change the $_page template to header->publish() ?>. That way, the header gets published when the $_page does, and not at the time of calling $_page->set().

于 2013-05-08T07:43:23.690 回答
1

一些概念以不同的方式在不同的语言中使用。尽管使用了相同的词,但它们并不相同。这可能会让人很困惑。我认为这是您问题的来源。我将描述模板中使用的三种模式。每种模式都有其特定的父子关系。

1. 区分实现

模板设计模式具有严格的父子关系。子类是父类的扩展,父类通常是一个抽象类。每个孩子都是父母的不同实现。抽象模板类的一个示例是 Shape。子实现可以是三角形、正方形和圆形。它们都共享一些公共抽象方法,例如 draw() 和 resize(),它们都具有相同的实现。抽象方法的目的是确保它们都具有该方法的唯一、特征实现(对于那个孩子)。基类也可以有一些非抽象方法。例如 fill() 和 rotate() 等方法。每个孩子都可以覆盖这些方法,但如果默认实现足够,则不需要。http://en.wikipedia.org/wiki/Template_method_pattern

2. 可重复的组成

html 文件中的模板系统服务于类似的概念,但目的不同。html-files 中模板系统的目的是创建一个灵活的独立块插入系统。每个块都独立于任何其他块,因此您不能假设它们有任何共同点。他们可能有,但这不是强制性的。您可以将其与抽象类的共享方法进行比较,因为在每个 html 文件中,您必须以与在其他文件中相同的方式实现它。它们是独立的块,以确保设计的最大灵活性。相同的内容块可以在不同的模板文件中使用,没有任何问题。他们应该能够以这种方式运作。这就是复合设计模式。父子关系可以最好地描述为部分-整体关系,请参阅http://en.wikipedia.org/wiki/Composite_pattern

3. 共享数据,独特呈现

在每个 html-fragment 中都使用了相同的数据(当前请求和会话的所有数据)。它们共享相同的数据,但不应相互继承任何东西。它们也不共享任何实现特性。您可以有一个内容块,您可以在其中使用寻呼机显示搜索结果的概述。或者你可以有一个带有静态页脚的块。在每个 html 模板中,只有一个 header 和一个 body。然而,可以有更多的菜单,并且特定的菜单可以在不同的地方以不同的形状出现。因此,您应该考虑观察者或 MVC 模式。使用的数据是模型部分。在一个请求期间对数据的所有视图都是相同的。每个块都是模型上的不同视图,并将使用它需要的信息。没有块需要了解任何其他块的状态或数据。我认为这就是您问 $_page 是否应该是全局的原因?由于共享数据,您需要它。不是因为文件的 html 部分。(这是一个组合问题。)但是,如果您将 root.php 的这两个部分分开并且对所有块都有一个公共数据对象,那么它可以在 $_page 不是全局的情况下工作。

结论:

在模板设计模式中的父子关系是类之间(而不是对象)的情况下,html 片段块之间存在 html 文件的部分-整体关系以及共享数据和共享数据之间的模型-视图关系任何特定 html 片段所需的数据。类之间的父子关系是固定的,html-fragments之间的部分-整体关系是灵活的,模型-视图关系是标准化的,但不是固定的。

如果数据对象只创建一次并且在请求期间保持不变,则效率最高。全局可以做到这一点,但方法中的局部变量也可以做到这一点,就像类的静态变量一样。我建议您使用最适合维护应用程序的实现。

于 2013-05-14T00:29:06.577 回答
0

另一种可能的方法是不区分 tplEngine 和 tplEngineChild。这是我的意思的简单实现。

class tmplEngine {

    protected $template_root = "";

    // container for the template variables
    protected $tmpl_vars = array();

    public function __construct($tmpl_dir = null){
         if ($tmpl_dir) $this->setTemplateDir($dir);
    }

    // sets a template root directory
    public function setTemplateDir($dir){
        $this->template_root = $dir;
    }

    // parses an external file from the current scope
    public function parse($file){
        ob_start();
            require($this->template_root.$file);
        return ob_get_clean();
    }

    // magic getter allows access as $tmplEninge->propertyName;
    public function __get($property){
        return array_key_exists($property, $tmpl_vars)?$this->tmpl_vars[$property]:"";    
    }

    // magic setter allow access as $tmplEngine->propertyName = "myVal"
    public function __set($property, $value){
        $this->tmpl_vars[$property] = $value;
    }
}

所以现在你的调用将是这样的。

$t = new tmplEngine(TPLFILES_DIR);
$t->header = $t->parse("/common/header.html");
$t->main = $t->parse("/common/article.html");
$t->footer = $t->parse("/common/footer.html");

echo $t->parse("/root.html");

希望这可以帮助

于 2013-05-12T05:31:33.503 回答
0

只是为了指出一些关于模板系统的有趣点。根据我自己的经验,模板应该是……它们的意思,模板。不是一堆PHP代码。

所有那些著名的系统(称为 tpl、magento、oscommerce,并且列表很长)实际使用的所有模板系统都不是模板。

为什么我这样说:

模板应该是一段(您命名语言、HTML、XML、WAP、CSS、JS 等)自然代码,并带有可替换的参数。

模板不应包含编程语言,为什么: - 图形设计师或 HTML 播放器通常不理解代码 - 您需要程序员修改模板 - 模板变成了另一段 PHP 代码 - 您需要 (通常)对编程语言有深入了解,以便能够修改和维护您的代码。

模板应该是具有低编程技能人员易于理解的参数的自然语言代码

这是一个很好的模板系统,就像 Java 模板、C 模板和其他语言模板一样,因为它没有将 PHP 与本机代码混合:

http://www.webability.info/?P=documentacion&wiki=/DomCore/reference/Templates

只需我的 2 美分即可获得更好的编程

于 2013-05-13T21:48:04.300 回答