raelgc / template
PHP模板
Requires
- php: >=5.3.0
Requires (Dev)
- phpunit/phpunit: ^5
This package is auto-updated.
Last update: 2024-09-14 22:59:22 UTC
README
PHP模板教程
人们问PHP最多的问题之一是关于模板的使用。要么是因为他们在书籍或论坛中读到,要么是因为我在课堂上讨论过。
通过使用模板,我们将整个视觉结构(HTML或XML、CSS等)与编程逻辑(PHP代码)分离,这极大地提高了网站建设和维护的效率。PHP有几种模板机制,而且已经存在很长时间。但哪一种最好?毫无疑问,Smarty是目前最全面的一个。然而,它也是最复杂的,学习曲线较长。它几乎是一种独立的PHP语言。
基于这一点,我决定基于一个更简单的模板机制创建一个教程。也就是说,这不是一个关于Smarty的教程。这是关于模板的,但基于我自己开发并在我自己的项目中使用的库,我在其中花费了大量时间进行开发和改进,始终以易用性为主要目标。
但这个教程中介绍的大部分概念都适用于PHP中大多数模板类,因此,如果您理解了这里解释的思想,您就可以使用您喜欢的任何模板机制。为了创建这个库(我使用模板已经很多年了,当时只有PHPLib),我研究了多种模板机制(PHPLib、Sigma等),因此我并不是“重新发明轮子”,而是“站在巨人的肩膀上”创造了一个更易于使用的东西,它满足了我当时工作的公司和我的需求。事实上,现在很多人都在使用这个库。
我还翻译了公共方法/函数的注释,以便于理解。您会发现,您只需要理解两个基本概念:变量和块。
那么,让我们开始吧。
变更日志
请查看CHANGELOG文件。
必要条件
需要使用PHP 5.3或更高版本。
安装
您可以选择以下方式安装库:
- 通过Composer,
- 通过Git,
- 或下载一个.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 文件,它分配了模板变量 NOME
和 VALOR
<?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文件中的代码更加简洁(当然,前提是你使用对象),并且因此,性能有了一些(几乎察觉不到的)提升。
为了让示例更清晰,我们将使用一个假设的页面来展示一个产品的详细信息。产品具有以下属性:id
和name
。
Template类可以与使用封装(get
和set
属性)的类一起使用,也可以与直接调用属性(通常通过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库不使用get
和set
(如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许可的约束。也就是说,即使是在商业项目中,您也可以将其作为库来使用。
只需记住,作为一个合法的人,返回任何可能的修改、修正或改进。