如何使用纯PHP构建模板系统?


15

我正在阅读关于stackoverflow的这个问题:

/programming/104516/calling-php-functions-within-heredoc-strings

并且接受的答案说要做像这样的纯PHP模板:

template.php:

<html>
    <头>
        <title> <?= $ title?> </ title>
    </ head>
    <身体>
        <?= getContent()?>
    </ body>
</ html>

index.php:

<?php

$ title ='演示标题';

函数getContent(){
    返回'<p> Hello World!</ p>';
}

include('template.php');

?>

对我来说,以上内容在某种意义上来说并不是很好的结构,即template.php依赖于其他脚本中定义的变量。而且您在执行操作时使用include()执行代码include('template.php')(与使用include()包含未立即执行的类或函数相反)。

我觉得更好的方法是将模板包装在函数中:

template.php:

<?php

功能模板($ title,$ content){
    ob_start(); ?>

<html>
    <头>
        <title> <?= $ title?> </ title>
    </ head>
    <身体>
        <?= $内容?>
    </ body>
</ html>

    <?php return ob_get_clean();
}

?>

index.php:

<?php

require_once('template.php');

打印模板(“演示标题”,“ <p> Hello World!</ p>”);

?>

第二种方法更好吗?有更好的方法吗?

Answers:


12

我不会做第二种方法,因为没有命名参数。

Parr 撰写了一篇写得很好的文章,描述了模板系统应该如何工作,编写模板系统和/或Web-MVC框架的人们经常引用它。

就我个人而言,我通常更喜欢用属性数组实现ArrayObject类,而模板将被引用$this->propertyName,实际上是模板对象的$this->property['name']。这也可以简单地通过使用__set和来实现__get,因此:

class Template {
  private $_scriptPath=TEMPLATE_PATH;//comes from config.php
  public $properties;
  public function setScriptPath($scriptPath){
    $this->_scriptPath=$scriptPath;
  }
  public function __construct(){
      $this->properties = array();
  }
  public function render($filename){

   ob_start();
   if(file_exists($this->_scriptPath.$filename)){
     include($this->_scriptPath.$filename);
    } else throw new TemplateNotFoundException();
    return ob_get_clean();
  }
  public function __set($k, $v){
      $this->properties[$k] = $v;
  }
  public function __get($k){
      return $this->properties[$k];
  }
}

模板看起来像:

<html>
      <head>
         <title><?=$this->title?></title>
      </head>
      <body>Hey <?=$this->name?></body>
</html>

并调用它看起来像:

$view = new Template();
$view->title="Hello World app";
$view->properties['name'] = "Jude";
echo $view->render('hello.inc');

据我所记得,这就是旧的Symfony 1.x和Zend_View模板引擎的样子,对我来说很好。


TinyButStrong也做了类似的事情
Izkata 2012年

你如何处理一些分数的循环?您是否使用单独的“子”模板?
瑞安

我想知道是否有任何方法可以摆脱在模板中使用$ this的方式,例如仅使用$ title或$ name。
瑞安

1
@Ryan您可以extract($this->properties);在include语句之前将属性提取到变量中。
乔伊斯·巴布

1

模板将始终依赖于外部变量,而PHP是一种动态语言,因此无法在编译时强制将其存在。因此,第一种方法可能很好。但是仍然存在一些问题:

  • 该示例不关心对输出进行HTML编码,这意味着您可能存在XSS漏洞。
  • 如果未设置这些变量之一,则代码将发出警告。您将看不到警告是否有意义(也许变量是可选的)。

一种非常简单的方法是编写一个函数,最好使用一个非常短的名称,同时兼顾这两个方面。称它_()似乎是一个常见的约定。一个简单的版本可能如下所示:

function _($raw) {
    if (isset($raw)) {
        return htmlspecialchars($raw);
    }
}

然后在模板中,您将执行以下操作:

<title><?= _($title) ?></title>

与该_()功能有关的更多事情:

  • 给它第二个参数以允许覆盖 htmlspecialchars如果要向其中传递HTML而不是原始字符串,调用。
  • 添加类型检查,以便数组和对象不会产生ArrayObject,而是有意义的内容(或空字符串)。
  • 添加国际化支持(还有另一个论点)。
  • 在调试模式下,如果未定义,则以某些特殊格式打印变量名称。这样,您可以一眼看出模板是否有问题。

1

第一个template.php更容易按原样加载到Dreamweaver / bluegriffon /任何文件中并且可以由服务器端的非熟练Web设计人员进行编辑,第二种...虽然还是可以的,但是在编辑过程中很可能会破裂。

我会选择第一个使它看起来像HTML的好处,这是3rd party设计人员通常喜欢的方式。


凭什么?您是说不能通过拖放来更改它吗?从文字上讲,第二个与第一个相同,但是包装了几行PHP。因此,假设它总是具有这种结构,那么他们手工编辑就不难了。
瑞安

1
一个好的模板对开发人员有意义,一个好的模板对设计师有意义。
Citricguy 2012年

1

因为Codeigniter是我最喜欢的框架,所以我向您建议一个类似于Codeigniter的解决方案:

class Template {
    protected $path, $data;

    public function __construct($path, $data = array()) {
        $this->path = $path;
        $this->data = $data;
    }

    public function render() {
        if(file_exists($this->path)){
            //Extracts vars to current view scope
            extract($this->data);

            //Starts output buffering
            ob_start();

            //Includes contents
            include $this->path;
            $buffer = ob_get_contents();
            @ob_end_clean();

            //Returns output buffer
            return $buffer;
        } else {
            //Throws exception
        }
    }       
}

您的模板如下所示:

<html>
    <head>
        <title><?=$title?></title>
    </head>
    <body>
        <?=$content?>
    </body>
</html>

然后,您将通过实例化类,将数据作为构造函数参数传递到数组中并调用'render'方法来呈现它:

$data = array('title' => 'My title', 'content' => 'My content');
$tmpl = new Template('template.php', $data);
echo $tmpl->render();

这样,您将不直接执行所包含的代码,而且还使模板保持可读性,因此设计人员将很高兴。


-3

php不是编程语言。因此,如果您的语言没有实型系统,则无法编写强类型。但是您可以帮助您的IDE(例如PHP-Storm)完成您想要的技巧...

您的视图定义:

interface mySimpleView { 
  public function getTitle(); 
} 

您的模板文件:

<?php
/**
* @var $this mySimpleView  <== Your IDE will try to use this type.
*/
?>

<h1><?= $this->getTitle() ?></h1>
<p><?= $this->getContent() ?></p> <!-- <= IDE will warn you !  -->
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.