raelgc/template

PHP模板

v2.2.8 2021-01-14 15:09 UTC

README

raelgc

PHP模板教程

人们问PHP最多的问题之一是关于模板的使用。要么是因为他们在书籍或论坛中读到,要么是因为我在课堂上讨论过。

通过使用模板,我们将整个视觉结构(HTML或XML、CSS等)与编程逻辑(PHP代码)分离,这极大地提高了网站建设和维护的效率。PHP有几种模板机制,而且已经存在很长时间。但哪一种最好?毫无疑问,Smarty是目前最全面的一个。然而,它也是最复杂的,学习曲线较长。它几乎是一种独立的PHP语言。

基于这一点,我决定基于一个更简单的模板机制创建一个教程。也就是说,这不是一个关于Smarty的教程。这是关于模板的,但基于我自己开发并在我自己的项目中使用的库,我在其中花费了大量时间进行开发和改进,始终以易用性为主要目标。

但这个教程中介绍的大部分概念都适用于PHP中大多数模板类,因此,如果您理解了这里解释的思想,您就可以使用您喜欢的任何模板机制。为了创建这个库(我使用模板已经很多年了,当时只有PHPLib),我研究了多种模板机制(PHPLib、Sigma等),因此我并不是“重新发明轮子”,而是“站在巨人的肩膀上”创造了一个更易于使用的东西,它满足了我当时工作的公司和我的需求。事实上,现在很多人都在使用这个库。

我还翻译了公共方法/函数的注释,以便于理解。您会发现,您只需要理解两个基本概念:变量和块。

那么,让我们开始吧。

变更日志

请查看CHANGELOG文件。

必要条件

需要使用PHP 5.3或更高版本。

安装

您可以选择以下方式安装库:

  1. 通过Composer,
  2. 通过Git,
  3. 或下载一个.zip文件。

1. 通过Composer

要使用Composer下载库,请执行以下命令

composer require raelgc/template

1 - 使用require_once包含自动加载和use指令来指定模板类命名空间,如下所示

<?php

  require_once(__DIR__ . "/vendor/autoload.php");
  use raelgc\view\Template;

?>

2. 通过Git

要使用Git下载库,请执行以下命令

git clone https://github.com/raelgc/template.git

1 - 在您的项目中创建一个名为lib的文件夹。

2 - 打开template文件夹,将raelgc文件夹(以及其所有内容)复制到您的项目中的lib文件夹内。

3 - 使用require_once包含模板类并使用use指令来指定类的命名空间,如下所示

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

?>

3. 通过下载

要下载包含库的 .zip 文件,请点击 这里

1 - 解压缩 .zip 文件。

2 - 在您的项目中创建一个名为 lib 的文件夹。

3 - 将 raelgc 文件夹(及其全部内容)复制到您的项目中的 lib 文件夹内。

4 - 使用 require_once 包含 Template 类,并使用 use 声明类所在的 命名空间,如下所示

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

?>

示例和说明:Hello World

模板机制的基本工作原理基于以下理念:您有一个使用库的 PHP 文件(它不会包含任何 HTML 代码)。HTML 代码将分开,存放在一个仅包含 HTML 代码的文件中。该 HTML 文件将由 PHP 文件读取。顺便说一下,所有处理都在 PHP 文件中进行。

话虽如此,让我们来看第一个示例,老套的 Hello World。我们将创建两个文件:一个负责所有逻辑的 PHP 文件,以及包含我们布局的 HTML 文件。

因此,创建一个名为 hello.html 的 HTML 文件,其内容如下

<html>
  <body>

    Olá Mundo, com templates PHP!

  </body>
</html>

现在,创建一个名为 hello.php 的 PHP 文件

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  $tpl->show();

?>

现在只需在浏览器中执行 hello.php 脚本,并检查它是否会显示从 hello.html 读取的内容。

如果出现问题,请参阅错误信息部分。

变量

现在让我们来看一个重要的概念:模板变量。正如您所想象的,我们将想要更改 HTML 文件中的多个部分。如何做到这一点?很简单:在 HTML 方面,您创建模板变量。看下面的示例

<html>
  <body>

    Olá {FULANO}, com templates PHP!

  </body>
</html>

注意变量 FULANO,它在花括号内。它将在 PHP 代码中分配值。

变量的名称只能包含:字母、数字和下划线(_)。使用大写字母仅是一种约定,便于在查看代码时识别。但是,它必须强制性地放在花括号内,且不能有空格。

那么,分配值的 PHP 代码会是什么样子?我们来看看

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");
  $tpl->FULANO = "Rael";
  $tpl->show();

?>

再次执行脚本,您将在浏览器中看到的最终代码将是

<html>
  <body>

    Olá Rael, com templates PHP!

  </body>
</html>

没有分配值的模板变量将在生成的最终代码中被清除。

关于变量的另一件事:您可以在模板变量中重复使用变量(即在多个地方使用相同的变量)。但显然,所有变量都会显示相同的值。

要读取变量的值,以相同的方式访问

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  // Atribuindo valor
  $tpl->FULANO = "Rael";

  // Imprimindo o valor da variável
  die("Valor de FULANO: ".$tpl->FULANO);

  $tpl->show();

?>

注意,使用模板变量,您可以在您喜欢的编辑器中继续编辑 HTML 文件:模板变量将被识别为普通文本,而 HTML 文件将不会因为 PHP 代码而变得混乱。反之亦然:您的 PHP 文件将不会包含 HTML 代码。

检查变量是否存在

如果您想要为模板变量分配值,但不确定该变量是否存在,您可以使用 exists() 方法进行检查。

不出所料,如果变量存在,它返回 true。如果不存在,返回 false

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("layout.html");

  // Checando existência da variável antes da atribuição
  if($tpl->exists("FULANO")) $tpl->FULANO = "Rael";

  $tpl->show();

?>

具有修饰符的变量

只要函数满足以下两个条件,我们就可以在 HTML 文件中调用一些 PHP 函数

  • 函数必须始终返回一个值;
  • 函数必须始终以字符串作为第一个参数;

假设以下 PHP 文件,它分配了模板变量 NOMEVALOR

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("modificadores.html");

  $tpl->NOME = 'Fulano Ciclano da Silva';

  $tpl->VALOR = 100;

  $tpl->show();

?>

以及以下 HTML,已经使用了修饰符

<html>

  <head>
    <title>Exemplo - Modificadores</title>
    <meta charset="UTF-8">
  </head>

  <body>

    <div>Nome: {NOME|replace:Fulano:Rael}</div>
    <div>Valor: R$ {VALOR|str_pad:5:0:0},00</div>

  </body>
</html>

解释:行 {NOME|replace:Fulano:Rael} 等同于在 PHP 中调用 replace('Fulano Ciclano da Silva', 'Fulano', 'Rael')

这个函数 replace 是在 Template 类内部声明的,基本上和 PHP 中的函数 str_replace(替换一个文本为另一个文本)做相同的事情。区别在于它接受一个字符串作为第一个参数。请记住,这是在 HTML 中使用函数的一个条件。

在第二个例子中,我们使用了 PHP 的内置函数 str_pad(因为它已经将字符串作为第一个参数接收,所以我们不需要创建一个新的函数)。在这个例子中,我们使用它来在值的左边添加零,这样值总是有 5 位数字(如果没有,这些数字将被零补齐)。查看该函数的文档以获取更多信息。

这是您需要了解的关于这个模板库的第二个也是最后一个概念:块。

假设您想列出数据库中注册的产品总数。如果没有产品,您将显示一个提示,表明没有找到产品。

因此,我们将使用两个块:一个显示总数;另一个在数据库确实为空的情况下,提示没有注册的产品。用于此的 HTML 代码是

<html>
  <body>

    <p>Quantidade de produtos cadastrados no sistema:</p>

    <!-- BEGIN BLOCK_QUANTIDADE -->
    <div class="destaque">Existem {QUANTIDADE} produtos cadastrados.</div>
    <!-- END BLOCK_QUANTIDADE -->

    <!-- BEGIN BLOCK_VAZIO -->
    <div class="vazio">Não existe nenhum produto cadastrado.</div>
    <!-- END BLOCK_VAZIO -->

  </body>
</html>

请注意,块的开始和结束是通过 HTML 注释标识的,使用单词 BEGIN(用于标识开始)或 END(用于标识结束)后面跟着块名。

单词 BEGIN 和 END 总是必须大写。块名应仅包含字母、数字或下划线。

然后,在 PHP 的一侧,我们将检查产品是否存在。如果存在,我们将显示 BLOCK_QUANTIDADE 块。如果不存在,我们将显示 BLOCK_VAZIO 块。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  // Vamos supor que esta quantidade veio do banco de dados
  $quantidade = 5;

  // Se existem produtos cadastrados, vamos exibir a quantidade
  if($quantidade > 0){
      $tpl->QUANTIDADE = $quantidade;
      $tpl->block("BLOCK_QUANTIDADE");
  }

  // Caso não exista nenhum produto, exibimos a mensagem de vazio
  else {
      $tpl->block("BLOCK_VAZIO");
  }

  $tpl->show();

?>

如您所见,块可以包含模板变量。并且只有当我们在 PHP 代码中通过 block() 方法请求时,块才会显示。否则,块不会显示在最终生成的内容中。

另一个重要的细节是:与模板变量不同,每个块必须是唯一的,也就是说,我们不能为多个块使用相同的名称。

请注意,即使使用了块,我们仍然可以在任何 HTML 编辑器中编辑 HTML 文件:指示块开始和结束的注释不会影响任何事情。

现在,让我们使用块的一个例子:假设您需要显示注册的产品数据。我们将使用块来完成这项工作

<html>
  <body>

    <p>Produtos cadastrados no sistema:</p>

    <table border="1">

      <tr><td>Nome</td><td>Quantidade</td></tr>

      <!-- BEGIN BLOCK_PRODUTO -->
      <tr>
        <td>{NOME}</td>
        <td>{QUANTIDADE}</td>
      </tr>
      <!-- END BLOCK_PRODUTO -->

    </table>

  </body>
</html>

请注意,我们只有一个 HTML 表格行用于产品的数据,位于一个块内。然后我们将为这些变量赋值,并根据产品列表重复块的内容

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  // Simulando produtos cadastrados no banco de dados
  $produtos = array(
    array("nome" => "Sabão em Pó", "quantidade" => 15),
    array("nome" => "Escova de Dente", "quantidade" => 53),
    array("nome" => "Creme Dental", "quantidade" => 37)
  );

  // Listando os produtos
  foreach($produtos as $p){
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_PRODUTO");
  }

  $tpl->show();

?>

block() 方法的默认行为是保持块的前内容,然后(或者更确切地说是连接)添加我们刚刚赋值的新内容。

在上面的例子中,产品数据来自 $produtos 数组。如果这些数据存储在数据库中,那么我们只需要像下面的例子一样做

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  // ... Conectar ao banco, selecionar database, etc

  // Produtos da database
  $result = mysql_query("SELECT nome, quantidade FROM produtos");

  // Listando os produtos
  while($linha = mysql_fetch_array($result)){
    $tpl->NOME = $linha["nome"];
    $tpl->QUANTIDADE = $linha["quantidade"];
    $tpl->block("BLOCK_PRODUTO");
  }

  $tpl->show();

?>

嵌套块

现在让我们结合我们之前看到的两个块使用例子:我们想在块中显示产品数据,但如果没有注册产品,我们想显示一个警告消息。我们将使用嵌套块(即块中的块)来完成这个操作

<html>
  <body>

    <p>Produtos cadastrados no sistema:</p>

    <!-- BEGIN BLOCK_PRODUTOS -->
    <table border="1">

      <tr><td>Nome</td><td>Quantidade</td></tr>

      <!-- BEGIN BLOCK_DADOS -->
      <tr>
        <td> {NOME} </td>
        <td> {QUANTIDADE} </td>
      </tr>
      <!-- END BLOCK_DADOS -->

    </table>
    <!-- END BLOCK_PRODUTOS -->

    <!-- BEGIN BLOCK_VAZIO -->
    <div class="vazio">Nenhum registro encontrado.</div>
    <!-- END BLOCK_VAZIO -->

  </body>
</html>

因此,如果存在产品,我们将显示 PRODUTOS 块。如果不存在,我们将显示 VAZIO

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  // Produtos cadastrados
  $produtos = array(
    array("nome" => "Sabão em Pó", "quantidade" => 15),
    array("nome" => "Escova de Dente", "quantidade" => 53),
    array("nome" => "Creme Dental", "quantidade" => 37)
  );

  // Listando os produtos
  foreach($produtos as $p){
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_DADOS");
  }

  // Se existem produtos, então mostramos o bloco com os dados de todos
  if(isset($produtos) && is_array($produtos) && sizeof($produtos) > 0){
    $tpl->block("BLOCK_PRODUTOS");
  }
  // Senão, mostramos o bloco com o aviso de nenhum cadastrado
  else {
    $tpl->block("BLOCK_VAZIO");
  }

  $tpl->show();

?>

默认自动块

与旧版本相比,这个新版本(2.0 及以上版本)的一个非常重要的细节:现在,如果嵌套块被显示,所有父块将自动显示。

也就是说,以之前的例子为例,我们可以将之前的PHP代码简化如下

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("hello.html");

  // Produtos cadastrados
  $produtos = array(
    array("nome" => "Sabão em Pó", "quantidade" => 15),
    array("nome" => "Escova de Dente", "quantidade" => 53),
    array("nome" => "Creme Dental", "quantidade" => 37)
  );

  // Listando os produtos
  foreach($produtos as $p){
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_DADOS");
  }

  // Se não existem produtos, mostramos o bloco com o aviso de nenhum cadastrado
  if(!isset($produtos) || !is_array($produtos) || !sizeof($produtos)){
    $tpl->block("BLOCK_PRODUTOS");
  }

  $tpl->show();

?>

也就是说,如果存在产品,并且因此显示了BLOCK_DADOS,则BLOCK_PRODUTOS将自动显示。

但继续阅读,我们将能够更自动地完成更多事情,并且使用更少的代码,使用FINALLY块。

FINALLY块

在之前的例子中,我们使用BLOCK_PRODUTOS来显示产品,如果没有产品,则使用BLOCK_VAZIO来显示一条友好的消息,表示没有已登记的产品。

我们可以更自动地完成这件事:使用FINALLY块。

看看在这种情况下HTML文件会是什么样子

<html>
  <body>

    <p>Produtos cadastrados no sistema:</p>

    <!-- BEGIN BLOCK_PRODUTOS -->
    <table border="1">

      <tr><td>Nome</td><td>Quantidade</td></tr>

      <!-- BEGIN BLOCK_DADOS -->
      <tr>
        <td> {NOME} </td>
        <td> {QUANTIDADE} </td>
      </tr>
      <!-- END BLOCK_DADOS -->

    </table>
    <!-- END BLOCK_PRODUTOS -->

    <div class="vazio">Nenhum registro encontrado.</div>

    <!-- FINALLY BLOCK_PRODUTOS -->

  </body>
</html>

而PHP文件呢?好吧,它将变得更加简单

<?php

    require_once("lib/raelgc/view/Template.php");
    use raelgc\view\Template;

    $tpl = new Template("hello.html");

    // Produtos cadastrados
    $produtos = array(
      array("nome" => "Sabão em Pó", "quantidade" => 15),
      array("nome" => "Escova de Dente", "quantidade" => 53),
      array("nome" => "Creme Dental", "quantidade" => 37)
    );

    // Listando os produtos
    foreach($produtos as $p){
      $tpl->NOME = $p["nome"];
      $tpl->QUANTIDADE = $p["quantidade"];
      $tpl->block("BLOCK_DADOS");
    }

    $tpl->show();

?>

首先重要的细节:在PHP文件中,FINALLY块永远不需要被调用。如果它在HTML中存在,它总是会被调用,即使相关的块没有被显示。

也就是说,如果没有产品,FINALLY块将自动显示没有找到记录的警告。方便,对吧?

其次,在PHP文件中,BLOCK_PRODUTOS块甚至没有被调用。但是,由于内部最深的BLOCK_DADOS块被调用,父块将自动显示。

带有HTML Select的块

最常见的疑问之一是:如何使用模板类与HTML的select元素?或者说,如何使用模板使一个option元素被选中?

那么,让我们构建我们的HTML页面,使用select元素和相应的options,代表一个城市列表。

<html>
  <body>

    <select name="cidades">

    <!-- BEGIN BLOCK_OPTION -->
    <option value="{VALUE}" {SELECTED}>{TEXT}</option>
    <!-- END BLOCK_OPTION -->

    </select>

  </body>
</html>

现在我们来看看相应的PHP文件。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("index.html");

  // Array de cidades
  $cidades = array(0 => "Cidade 0", 1 => "Cidade 1", 2 => "Cidade 2");

  // Valor selecionado
  $atual = 1;

  foreach($cidades as $value => $text){

    $tpl->VALUE = $value;
    $tpl->TEXT = $text;

    // Vendo se a opção atual deve ter o atributo "selected"
    if($atual == $value) $tpl->SELECTED = "selected";

    // Caso esta não seja a opção atual, limpamos o valor da variável SELECTED
    else $tpl->clear("SELECTED");

    $tpl->block("BLOCK_OPTION");

  }

  $tpl->show();

?>

作为结果,浏览器将显示以下代码

<html>
  <body>

    <select name="cidades">

      <option value="0" >Cidade 0</option>
      <option value="1" selected>Cidade 1</option>
      <option value="2" >Cidade 2</option>

    </select>

  </body>
</html>

注意,在PHP文件中我们调用了clear方法吗?如果我们不调用这个方法(它清除变量的值),所有的选项(option)都会保留selected属性(显然,这是我们不希望的效果)

<html>
  <body>

    <select name="cidades">

      <option value="0" selected>Cidade 0</option>
      <option value="1" selected>Cidade 1</option>
      <option value="2" selected>Cidade 2</option>

    </select>

  </body>
</html>

使用多个HTML文件

模板的常见用途之一是使用一个包含网站基本结构的HTML文件:页眉、页脚、菜单等。另一个文件包含我们想要显示的页面内容,也就是“主体”。这样,我们不需要在所有HTML文件中重复使用共同的元素(页眉、页脚等),并且包含内容的HTML页面(“主体”)将更简洁、更小,并且更容易维护。

如何使用模板来做这件事?首先,我们创建我们的“基础”HTML文件,即base.html文件。

<html>
  <head>
    <title>Título da Página</title>
  </head>
  <body>

    <div>{FULANO}, seja bem vindo!</div>

    <div>{CONTEUDO}</div>

    <div>Deseja maiores informações? Clique <a href="info.php">aqui</a> para saber</div>

  </body>
</html>

现在,让我们创建包含我们HTML页面“主体”内容的文件,即miolo.html文件。

<p>Produtos cadastrados no sistema:</p>

<!-- BEGIN BLOCK_PRODUTOS -->
<table border="1">

  <tr><td>Nome</td><td>Quantidade</td></tr>

  <!-- BEGIN BLOCK_DADOS -->
  <tr>
    <td> {NOME} </td>
    <td> {QUANTIDADE} </td>
  </tr>
  <!-- END BLOCK_DADOS -->

</table>
<!-- END BLOCK_PRODUTOS -->

<div class="vazio">Nenhum registro encontrado.</div>

<!-- FINALLY BLOCK_PRODUTOS -->

然后,在PHP文件中,我们使用addFile()方法,其中我们提供了两个信息:在模板的哪个变量中放入新文件的 内容,以及该文件的路径。然后,只需像通常一样使用变量和块,不管它们在哪个HTML文件中。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("base.html");

  // Adicionando mais um arquivo HTML
  $tpl->addFile("CONTEUDO", "miolo.html");

  $tpl->FULANO = "Rael";

  // Produtos cadastrados
  $produtos = array(
    array("nome" => "Sabão em Pó", "quantidade" => 15),
    array("nome" => "Escova de Dente", "quantidade" => 53),
    array("nome" => "Creme Dental", "quantidade" => 37)
  );

  // Listando os produtos
  foreach($produtos as $p){
    $tpl->NOME = $p["nome"];
    $tpl->QUANTIDADE = $p["quantidade"];
    $tpl->block("BLOCK_DADOS");
  }

  $tpl->show();

?>

保存模板内容

到目前为止,我们已经通过show()方法将模板生成的内容显示在屏幕上。但是,如果我们想对内容进行其他使用,比如保存到文件或其他类似的东西,怎么办?只需使用parse()方法,它生成最终内容并返回。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("base.html");
  $tpl->addFile("CONTEUDO", "miolo.html");

  // Variáveis, blocos, etc
  $tpl->FULANO = "Rael";

  // Pega o conteúdo final do template
  $conteudo = $tpl->parse();
  // Salva em um arquivo
  file_put_contents("arquivo.txt", $conteudo);

?>

使用对象

从版本1.5开始,模板类支持将对象分配给模板变量(请检查类源代码中的版本)。

这在我们使用像 Doctrine2、Kohana ORM 或 Laravel Eloquent 这样的 ORM 库时非常有帮助。

这使得PHP文件中的代码更加简洁(当然,前提是你使用对象),并且因此,性能有了一些(几乎察觉不到的)提升。

为了让示例更清晰,我们将使用一个假设的页面来展示一个产品的详细信息。产品具有以下属性:idname

Template类可以与使用封装(getset属性)的类一起使用,也可以与直接调用属性(通常通过PHP的魔术方法)的类一起使用。

首先,让我们看看从Doctrine2的示例中直接提取的类Produtos。

<?php

  // src/Product.php
  class Product
  {
    /**
    * @var int
    */
    protected $id;
    /**
    * @var string
    */
    protected $name;

    public function getId()
    {
      return $this->id;
    }

    public function getName()
    {
      return $this->name;
    }

    public function setName($name)
    {
      $this->name = $name;
    }
  }

?>

然后,我们将修改PHP文件以加载一个产品,并使用Template对象的支撑。

<?php

  # Bootstrap da Doctrine2
  require_once "bootstrap.php";

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("produtos.html");

  # Doctrine buscando o produto de ID = 1
  $produto = $entityManager->find('Product', 1);

  # Atribuindo a variável template
  $tpl->P = $produto;

  $tpl->show();

?>

HTML文件也必须修改以显示产品的属性。

  Id: {P->ID} <br/>
  Nome: {P->NAME} <br/>

P->NAME将调用存在的方法$p->getName(),如果存在。如果该类中没有这个方法,将会抛出错误。

这适用于在HTML中尝试调用的任何属性:它将被翻译为$meuObjeto->getAtributo()。如果PHP中的方法名由多个单词组成,例如$p->getExpirationDate(),只需在HTML中使用下划线_作为名称的分隔符:在示例中,将变为P->EXPIRATION_DATE

即使ORM库不使用getset(如Kohana或Laravel),它也可以工作:Template类会查看类Product是否有属性$meuObjeto->atributo,并且如果该类有魔术方法__get,Template类也会尝试调用它。

注释

与编程语言类似,Template类支持HTML中的注释。注释对于许多事情都很有用,例如识别HTML的作者、版本、包括许可等。

与HTML注释不同,这些注释会显示在页面的源代码中,Template类中的注释是从最终HTML中提取的。实际上,Template类的注释是在任何处理之前提取的,并且任何在注释之间的内容都将被忽略。

注释位于标签<!------>之间。请注意,我们使用3个连字符,而不是2个(2个用于标识HTML注释)。原因很简单:允许我们区分它们,并允许编辑器继续将<!------>之间的内容识别为注释。

请看下面的示例

<!---
Listagem de produtos.

@author Rael
@version 1.0
--->

  <p>Produtos cadastrados no sistema:</p>

  <!-- BEGIN BLOCK_PRODUTOS -->
  <table border="1">

    <tr><td>Nome</td><td>Quantidade</td></tr>

    <!-- BEGIN BLOCK_DADOS -->
    <tr>
      <td>{NOME}</td>
      <td>{QUANTIDADE}</td>
    </tr>
    <!-- END BLOCK_DADOS -->

  </table>
  <!-- END BLOCK_PRODUTOS -->

  <div class="vazio">Nenhum registro encontrado.</div>

  <!-- FINALLY BLOCK_PRODUTOS -->

创建XML、CSV和其他文件

模板最常见的用途是用于HTML文件。但因为这个库旨在用于任何类型的文本文件,我们可以用它来处理多种其他文件格式,如XML和CSV文件。

如何做到这一点?再简单不过了:不需要做任何改变,只需要将HTML文件改为指向Template的任何其他文本文件。然后,使用变量和块,如我们所见,在屏幕上显示内容或将其保存到文件中。

创建Office文件

如果您需要创建一个需要在Word(.doc)或Excel(.xls)格式中显示的报告,我们也可以使用Template类来完成。

首先,在Office中正常创建您的报告。完成后,选择“另存为”选项,并选择HTML格式。完成此操作后,在您的PHP编辑器中打开此HTML文件(不要担心,它非常复杂,充满了奇怪的标签),就像我们之前看到的那样使用它:创建变量,声明块,无需做任何不同的事情。如果您要保存内容到文件,请将此文件扩展名改为.doc(对于工作表,则为.xls)。Office将正常打开此文件,并在第一次打开时自动将其从HTML转换为所需的格式。

如果您要在浏览器中显示内容而不是将其保存到文件中,您需要修改头部信息来通知浏览器这是一份Office文档,强制浏览器将其解释为文档(Firefox会下载文件,IE会以插件形式打开Microsoft Office并在浏览器中显示文件)。

使用PHP的header()指令来实现。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  // Forçando o cabeçalho para o formato escolhido do Office
  header('Content-type: application/msword');
  header('Content-Disposition: attachment;filename="Relatorio.doc"');
  header("Pragma: no-cache");
  header("Expires: 0");

  // Arquivo relatorio.html, gerado no Word
  $tpl = new Template("relatorio.html");

  // Variáveis, blocos, etc
  $tpl->FULANO = "Rael";

  $tpl->show();

?>

错误管理

当发生错误时,您应该注意到由Template类生成的错误消息与PHP中的常规消息略有不同:它不是仅包含错误消息的die(),而是生成一个异常(Exception)。

为什么是这样呢?

使用异常,我们有两大优势:首先,我们可以看到整个错误堆栈,即从错误起源的地方开始,到所有出现错误的地方。这极大地简化了调试和错误修正的工作。为了正确查看错误代码和堆栈,请要求浏览器显示页面源代码,因为只有在这种模式下,行断才能正确显示。

第二个优势是,如果需要的话,我们可以管理错误,并通过使用try/catch来确保脚本执行不会被中断。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("index.html");

  // Tentando acessar variável que não existe
  try {
      $tpl->FOO = "bar";
  // Capturando erro e evitando que o script seja interrompido
  } catch (Exception $e){
      echo "FOO não existe!";
  }

  $tpl->show();

?>

动态变量

想象一个场景,您在HTML文件中有多个模板变量,需要将一个值赋给其中一个。但问题是:需要接收值的变量仅在脚本执行期间定义。换句话说,这是一个动态变量,或者像一些人说的,“变量变量”。那么我们的HTML文件将是这样的:

<html>
  <body>

	 Olá {NOME_FULANO}!

  </body>
</html>

请注意,在HTML文件中没有什么不同。然后在PHP文件中,只需使用花括号即可。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  $tpl = new Template("base.html");

  // Nome da variável  
  $varname = "fulano";

  // Variável definida dinamicamente
  $tpl->{"NOME_".strtoupper($varname)} = "Rael";

  $tpl->show();

?>

变量转义

假设出于某种原因,您需要保持一个模板变量在HTML最终结果中。例如:您正在编写一个为您自动生成模板的系统。

为此,假设您有以下的HTML:

<html>
  <body>

    {CONTEUDO}

  </body>
</html>

您需要确保{CONTEUDO}不被替换(或删除),但仍然保留在HTML最终结果中。

为此,您需要在变量中包含{_}来进行转义。

<html>
  <body>

    {{_}CONTEUDO}

  </body>
</html>

就这样:在最终HTML中{CONTEUDO}仍然存在。

错误消息

以下是Template类显示的错误消息的含义:

  • Parse error: syntax error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}':您可能正在使用PHP 4(请参阅使用此库的必要要求)。

  • addFile: var <varname> não existe:您正在使用addFile()方法添加一个HTML文件(或等效文件),但您要填充内容的模板变量不存在。

  • var <varname> não existe:您正在尝试为一个不存在的变量赋值。请确保模板变量的名称正确,并且您只使用字母、数字和下划线作为该变量的名称,并在花括号中使用。

  • arquivo <filename> não existe:您提供的HTML文件(或等效文件)路径不存在,或其读取权限被拒绝。

  • arquivo <filename> está vazio:您传递的HTML文件(或等效文件)为空。如果为空,可能是您提供了错误的文件,或者忘记在其中添加内容。

  • bloco duplicado: <blockname>:您正在尝试分配给块的名称已分配给另一个块。请记住,块的名称必须是唯一的。如果您正在使用多个HTML文件(或等效文件),则与您的块具有相同名称的块可能位于其他文件中。

  • 格式错误:您声明的块存在缺陷。也许您使用了带有名称的BEGIN BLOCK标签,但结束(END BLOCK)时使用了另一个名称。或者,您忘记了END BLOCK标签。或者,您将FINALLY BLOCK标签放在了错误的位置。

  • 不存在:您正在向block()方法提供一个不存在的块的名称。请确保块名称正确,并且只使用字母、数字和下划线作为块名称。

  • 在类 中不存在用于访问 -> 的方法:不存在用于访问您正在调用的属性的方法。如果您在HTML中使用OBJETO->NOME,则该对象的类需要有一个名为getNome()或isNome()的方法。更多详细信息请参阅“使用对象”部分。

精度和性能

使用模板时常见的疑问是:这会影响到性能吗?答案是:比您想象的要少。让我解释一下原因。

这个库比旧的模板库(如已弃用的PHPLib)和其他类似的库要快得多。原因是它避免了使用正则表达式,不像旧库那样用它来做所有的工作。

尽管如此,这个库没有像Smarty模板那样的缓存机制。原因有两个。第一个是使用简单:不需要创建或配置目录、Unix权限等。第二个是性能已经足够好,可以保持这种简单性。无论如何,您都可以自由地创建缓存机制。

我使用这个库多年,并不断提高其性能。它已经在高流量网站上使用过,从未成为瓶颈。如果您使用这个库,并对您的系统进行性能测量,您会发现98%的情况下瓶颈是数据库,而不是处理器。总是是不当构建的查询,或者是在循环中执行的查询这类事情。

这个库良好性能的一个副作用是:如果您要求浏览器显示由Template类生成的页面的源代码,您会看到最终代码没有删除每个模板块开头的某些制表符(tab或\t),使得最终代码看起来比应该的更混乱。原因是如果默认删除这些制表符,这将会降低Template类的性能。

由于源代码中的制表符对HTML最终内容没有任何影响,Template类的默认行为是忽略这些块开头的制表符,将它们留在最终代码中。唯一可能成为问题的情况是您需要精确复制HTML文件,例如在使用<pre><code>标签时。因此,在Template对象的声明中存在一个可选的第二个参数:$accurate。如果您使用它并将值设置为true,那么您的最终HTML代码将精确复制模板中的HTML文件(但性能会有所下降)。

<?php

  require_once("lib/raelgc/view/Template.php");
  use raelgc\view\Template;

  // Parâmetro $accurate com valor TRUE
  $tpl = new Template("base.html", true);

  // ...

  $tpl->show();

?>

结论

使用模板机制是Web应用开发的一个重大进步,因为它允许我们将应用程序的视觉结构从PHP编程中分离出来。

我试图在这个教程中包括所有关于使用模板的要点。如果您遇到问题,请首先查看这里的所有主题。请记住,这项工作是自愿的,我花了大量时间编写这个教程,以及在这个库上的时间。因此,在给我发邮件报告问题之前,请先尝试自己解决问题:大部分的学习都在这里。如果您无法解决,请继续,并与我联系。

如果有缺陷、建议或改进,请创建一个GitHub问题

许可证

本库的许可受LGPL许可的约束。也就是说,即使是在商业项目中,您也可以将其作为库来使用。

只需记住,作为一个合法的人,返回任何可能的修改、修正或改进。