先前的单词
在大多数项目组里,开发一个Web程序时都会发生以下的过程:在规划文档提交之后,前端工程师创建网站外观的模型然后交给后端工程师,后端工程师使用后端代码实现程序逻辑,使用外观模型创建基本架构。之后将项目返回给前端工程师进行进一步的完善。这样,项目可能在后端工程师和前端工程师之间来回折腾好几次。由于后端工程师不会干涉任何相关的HTML标签,因此没必要将前端代码和后端代码混杂在一起,前端工程师只需要配置文件、动态块等界面部分,不需要接触错综复杂的后端代码。因此,此时拥有良好的模板支持就显得非常重要。本文将详细介绍PHP中的模板引擎。
概述
什么是网站模板?准确的说,是指网站页面模板,也就是每个页面只是一个模板,包括结构、样式、页面布局等。它是创建网页内容的模板,也可以理解为现有的网页框架。模板中原有的内容可以被服务器端数据库中的动态内容替换,以保持页面样式的一致性。
PHP 是一种嵌入在 HTML 中并在服务器端执行的脚本语言。因此,大多数用 PHP 开发的 Web 应用程序最初的开发模板都是混合层数据编程。虽然 MVC 设计模式可以强行将应用程序逻辑与网页展示逻辑分开,但它只分离了应用程序的输入、处理和输出。网页展示逻辑(视图)仍然会将 HTML 代码与 PHP 程序强耦合在一起。PHP 脚本的编写者必须既是网页设计师,又是 PHP 开发人员。
有许多解决方案可以将网站页面设计和 PHP 应用程序几乎完全分离。这些解决方案被称为“模板引擎”,它们正在逐渐消除由于缺乏层分离而导致的问题。模板引擎的目的就是实现上述逻辑分离的功能。它使程序开发人员可以专注于数据控制或功能实现。因此,模板引擎非常适合公司的 Web 开发团队使用,以便每个人都能发挥自己的专长
模板引擎技术的核心比较简单,只需要将前端页面指定为一个模板文件,并将模板文件中的动态内容,比如数据库输出、用户交互等,定义为包含在特殊“分隔符”中的“变量”,然后放到模板文件中的相应位置即可。当用户浏览时,PHP脚本程序打开模板文件,并替换模板文件中定义的变量。这样,当模板中的特殊变量被替换成不同的动态内容时,就会输出所需的页面。
目前在PHP中可以应用的模板引擎比较多,有IPB等几十种,使用这些用PHP编写的模板引擎,可以使代码脉络更加清晰,结构更加合理,也可以使网站的维护更新更加简单,营造更好的开发环境,使开发和设计工作更容易结合。但是没有一种PHP模板是最适合、最完美的,因为PHP模板是大众化的东西,并不是专门为某一个人而开发的,如果能对模板的特点和应用有清晰的认识,充分认识到模板的优缺点,就会知道是否要选择使用某个模板引擎,或者使用哪种模板引擎。
自定义模板引擎类
自定义模板引擎可以帮助你更好的了解模板引擎的工作机制,为学习做好准备,更重要的是你自己的PHP模板引擎永远不是固定的,可以根据项目的需要进行自定义。
下面的例子中,通过上面介绍的模板引擎概念,创建了一个属于自己的简单模板引擎,可以用来处理基本的模板功能,比如变量替换、分支结构、数组循环遍历、模板嵌套等,如下所示:
php /** file: mytpl.class.php 类名为MyTpl是自定义的模板引擎 通过该类对象加载模板文件并解析,将解析后的结果输出 */ class Mytpl { public $template_dir = 'templates'; //定义模板文件存放的目录 public $compile_dir = 'templates_c'; //定义通过模板引擎组合后文件存放目录 public $left_delimiter = '<{'; //在模板中嵌入动态数据变量的左定界符号 public $right_delimiter = '}>'; //在模板中嵌入动态数据变量的右定界符号 private $tpl_vars = array(); //内部使用的临时变量 /** 将PHP中分配的值会保存到成员属性$tpl_vars中,用于将模板中对应的变量进行替换 @param string $tpl_var 需要一个字符串参数作为关联数组下标,要和模板中的变量名对应 @param mixed $value 需要一个标量类型的值,用来分配给模板中变量的值 */ function assign($tpl_var, $value = null) { if ($tpl_var != '') $this->tpl_vars[$tpl_var] = $value; } /** 加载指定目录下的模板文件,并将替换后的内容生成组合文件存放到另一个指定目录下 @param string $fileName 提供模板文件的文件名 */ function display($fileName) { /* 到指定的目录中寻找模板文件 */ $tplFile = $this->template_dir.'/'.$fileName; /* 如果需要处理的模板文件不存在,则退出并报告错误 */ if(!file_exists($tplFile)) { die("模板文件{$tplFile}不存在!"); } /* 获取组合的模板文件,该文件中的内容都是被替换过的 */ $comFileName = $this->compile_dir."/com_".$fileName.'.php'; /* 判断替换后的文件是否存在或是存在但有改动,都需要重新创建 */ if(!file_exists($comFileName) || filemtime($comFileName) < filemtime($tplFile)) { /* 调用内部替换模板方法 */ $repContent = $this->tpl_replace(file_get_contents($tplFile)); /* 保存由系统组合后的脚本文件 */ file_put_contents($comFileName, $repContent); } /* 包含处理后的模板文件输出给客户端 */ include($comFileName); } /** 内部使用的私有方法,使用正则表达式将模板文件'<{ }>'中的语句替换为对应的值或PHP代码 @param string $content 提供从模板文件中读入的全部内容字符串 @return $repContent 返回替换后的字符串 */ private function tpl_replace($content) { /* 将左右定界符号中,有影响正则的特殊符号转义 例如,<{ }>转义\<\{ \}\> */ $left = preg_quote($this->left_delimiter, '/'); $right = preg_quote($this->right_delimiter, '/'); /* 匹配模板中各种标识符的正则表达式的模式数组 */ $pattern = array( /* 匹配模板中变量 ,例如,"<{ $var }>" */ '/'.$left.'\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*'.$right.'/i', /* 匹配模板中if标识符,例如 "<{ if $col == "sex" }> <{ /if }>" */ '/'.$left.'\s*if\s*(.+?)\s*'.$right.'(.+?)'.$left.'\s*\/if\s*'.$right.'/ies', /* 匹配elseif标识符, 例如 "<{ elseif $col == "sex" }>" */ '/'.$left.'\s*else\s*if\s*(.+?)\s*'.$right.'/ies', /* 匹配else标识符, 例如 "<{ else }>" */ '/'.$left.'\s*else\s*'.$right.'/is', /* 用来匹配模板中的loop标识符,用来遍历数组中的值, 例如 "<{ loop $arrs $value }> <{ /loop}>" */ '/'.$left.'\s*loop\s+\$(\S+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*'.$right.'(.+?)'.$left.'\s*\/loop\s*'.$right.'/is', /* 用来遍历数组中的键和值,例如 "<{ loop $arrs $key => $value }> <{ /loop}>" */ '/'.$left.'\s*loop\s+\$(\S+)\s+\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*=>\s*\$(\S+)\s*'.$right.'(.+?)'.$left.'\s*\/loop \s*'.$right.'/is', /* 匹配include标识符, 例如,'<{ include "header.html" }>' */ '/'.$left.'\s*include\s+[\"\']?(.+?)[\"\']?\s*'.$right.'/ie' ); /* 替换从模板中使用正则表达式匹配到的字符串数组 */ $replacement = array( /* 替换模板中的变量 tpl_vars["var"]; */ 'tpl_vars["${1}"]; ?>', /* 替换模板中的if字符串 */ '$this->stripvtags(\'\',\'${2}\')', /* 替换elseif的字符串 */ '$this->stripvtags(\'\',"")', /* 替换else的字符串 */ '', /* 以下两条用来替换模板中的loop标识符为foreach格式 */ 'tpl_vars["${1}"] as $this->tpl_vars["${2}"]) { ?>${3}', 'tpl_vars["${1}"] as $this->tpl_vars["${2}"] => $this->tpl_vars["${3}"]) { ?>${4}', /*替换include的字符串*/ 'file_get_contents($this->template_dir."/${1}")' ); /* 使用正则替换函数处理 */ $repContent = preg_replace($pattern, $replacement, $content); /* 如果还有要替换的标识,递归调用自己再次替换 */ if(preg_match('/'.$left.'([^('.$right.')]{1,})'.$right.'/', $repContent)) { $repContent = $this->tpl_replace($repContent); } /* 返回替换后的字符串 */ return $repContent; } /** 内部使用的私有方法,用来将条件语句中使用的变量替换为对应的值 @param string $expr 提供模板中条件语句的开始标记 @param string $statement 提供模板中条件语句的结束标记 @return strin 将处理后的条件语句相连后返回 */ private function stripvtags($expr, $statement='') { /* 匹配变量的正则 */ $var_pattern = '/\s*\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\s*/is'; /* 将变量替换为值 */ $expr = preg_replace($var_pattern, '$this->tpl_vars["${1}"]', $expr); /* 将开始标记中的引号转义替换 */ $expr = str_replace("\\\"", "\"", $expr); /* 替换语句体和结束标记中的引号 */ $statement = str_replace("\\\"", "\"", $statement); /* 将处理后的条件语句相连后返回 */ return $expr.$statement; } } ?>
Mytpl类中声明的多个方法中,除了封装的方法之外,只有两个公共方法()和()可以在对象创建后调用。()方法用于将PHP脚本中的数据赋值给模板中相应的变量,()方法用于将特定目录中的模板文件加载到PHP脚本中。同时,将模板文件中以“>”标记声明的自定义模板语句匹配并替换为相应的PHP语法格式,然后将替换的内容保存在特定目录中。在运行时,也会编译为非模板技术的PHP文件,以前缀为“com_”、扩展名为“.php”的模板文件形式保存。然后通过()函数包含处理后的模板文件,并使用PHP解析后发送给客户端。
使用模板引擎
使用自定义模板引擎比较简单,即自己定义语法格式。但请记住,所有流行的模板引入解决方案都遵循同一套核心实现原则,就像编程语言一样,学习一门语言可以让你更容易掌握其他语言。使用模板引擎的主要原因是为了将前端工程师和后端工程师的工作分开,因此模板引擎不仅是后端工程师需要的,前端工程师也需要。
1. 后端工程师对模板引擎的使用
在PHP脚本中包含模板引擎类文件如下:
require("mytpl.class.php"); //包含模板引擎类,相当于模板引擎安装
创建模板引擎类的对象,并初始化一些成员属性,如下所示:
$tpl=new MyTpl(); //创建模板引擎类的对象,也可以根据参数对成员初始化
动态数据(包括标量和数组类型数据,比如从数据库表获取的数据数组)使用模板引擎对象中的()方法赋值给模板文件。该方法可以多次使用,将任意数量的变量赋值给模板。如下图所示:
$tpl->assign("var","this is a value"); //可以分配标量类型数据,可以使用多次 $tpl->assign("arr",array(array(1,2),array("a","b"))); //也可以分配数组包括多维数组
在 PHP 脚本中,通过调用模板对象中的 () 方法并传入模板文件名作为参数,就会将指定目录下的相应模板文件加载到 PHP 脚本中。然后通过模板引擎中的 方法解析模板中的自定义语法,并输出处理后的模板。如下图所示:
$tpl->display("test.tpl"); //参数“test.tpl”为特定目录下的模板文件
2. 前端工程师对模板引擎的使用
前端工程师需要将写好的模板文件存放在一个指定的目录中,这个目录是通过对象中的$属性来指定的,默认是当前目录下的“”目录。另外,模板文件名和后缀名可以任意设置,比如index.tpl、test.htm、.tp等。
模板文件是用 HTML、CSS 等 Web 前端语言编写的纯静态文件。但是,你可以在模板文件中使用“”分隔符定义一个变量(类似 PHP 中的变量格式),该变量可以接受并输出 PHP 脚本赋值的动态数据。模板中使用的“”分隔符也可以根据个人喜好在模板引擎类中进行修改。如下图所示:
姓名:<{$name}>,年龄:<{$age}>,性别:<{$sex}> //模板中使用占位符
如果在 PHP 脚本中给模板赋值数组,那么在模板中也可以进行遍历,还可以嵌套遍历多维数组。使用模板引擎中定义的“”标签对,使用方法与 PHP 中结构体的语法格式类似。如下图所示:
<{loop $arr $value }> //遍历数组$arr中的元素值 数组中的元素值<{$value}> //每次遍历输出元素中的值 <{/loop}> //在模板中遍历数组的结束标记 <{loop $arr $key=>$value }> //遍历数组$arr中的元素下标和元素值 数组中的元素键<{$key}> //每次遍历输出元素中的下标 数组中的元素值<{$value}> //每次遍历输出元素中的值 <{/loop}> //在模板中遍历数组的结束标记 <{loop $arr $value }> //遍历数组$arr中的元素值 <{loop $arr $data }> //使用嵌套标记遍历二维数组 数组中的元素值<{$value}> //每次遍历输出元素中的值 <{/loop}> //在模板中遍历数组的内层结束标记 <{/loop}> //在模板中遍历数组的外层结束标记
模板引擎还可以解析模板文件中使用特殊标签写成的分支结构,语法风格类似PHP的分支结构,使用模板文件中的“”标签对实现选择结构,还可以实现多分支、嵌套分支的选择结构,如下图所示:
<{if($var=="red")}>这是“红色”的字
<{elseif($var=="green")}>这是“绿色”的字
<{else}> <{if($size=20)}>这是“20px”大小的字
<{/if}> <{/if}>
在自定义模板引擎中,还增加了在模板文件中引入其他模板文件的功能,可以使用“”标签将子模板引入到当前模板中,也支持在子模板中引入另一个子模板。如下图所示:
<{include 'other.tpl' }>
使用实例分析
通过在程序中加载模板引擎,可以将前端语言代码与后端语言代码分离,在PHP程序中首先获取数据库中存储的数据,然后通过加载模板引擎对数据进行分配,最后由模板引擎加载处理并输出模板文件。因此PHP程序只需要创建动态数据,加载模板引擎并将动态数据分配给模板,就完成了PHP程序的工作。模板的设计只需要前端工程师独立完成,用HTML、CSS等前端页面设计语言编写即可。另外,模板文件还需要使用模板引擎可以解析的标签来引用模板中从PHP分配的动态数据。
1.数据库设计
假设数据库服务器在主机“”,用户名和密码分别为“admin”和“”,在服务器上创建一个名为“mydb”的数据库,并在数据库中创建一个名为“User”的用户表,创建该表的SQL查询语句如下:
CREATE TABLE User( id SMALLINT(3) NOT NULL AUTO_INCREMENT, name VARCHAR(10) NOT NULL DEFAULT '', sex VARCHAR(4) NOT NULL DEFAULT '', age SMALLINT(2) NOT NULL DEFAULT '0', email VARCHAR(20) NOT NULL DEFAULT '', PRIMARY KEY (id) );
用户表User创建完成后,为了演示,可以往表中插入一些数据,SQL查询语句如下:
INSERT INTO User (name,sex,age,email) VALUES ("a","男",27,"a@a.com"), ("b","女",22,"b@b.com"), ("c","女",30,"c@c.com"), ("d","女",24,"d@d.com");
2.模板设计
模板的设计不应该包含任何PHP代码,可以由前端人员完成。在自定义模板引擎中,规定模板文件应该在指定的目录中找到。这个特定的目录可以在创建模板引擎对象时指定,也可以使用默认的目录设置。默认情况下,模板文件可以存放在当前目录下的“”目录中。本示例一共需要三个模板文件main.tpl、.tpl和.tpl,都存放在这个默认的目录设置中。这三个模板文件的代码如下:
模板头文件.tpl
<{$title}>
模板尾文件.tpl
##### <{$author}> #####
主模板文件main.tpl
<{include 'header.tpl'}>
<{elseif $u == "女"}> | <{else}> | <{/if}> <{$u}> | <{/loop}>
main.tpl文件为主模板文件,该文件顶部和底部分别用两个标签将独立的页眉和页脚模板文件包含到这个主模板文件中。该文件中用标签获取PHP动态赋值的表名,用双层标签嵌套遍历PHP动态赋值的从数据库获取的二维数组$Users。标签中还用了条件选择标签组合,将数据中性别为“男”的表格背景设置为红色等判断。包含的页眉模板文件.tpl和页脚模板文件.tpl也可以获取PHP动态赋值给模板的数据。
3. PHP 编程
通过使用模板引擎,PHP程序员在编写代码时只需要使用PHP,而不再需要使用HTML、CSS等页面设计语言来完成前端工作。下面是一个名为index.php的PHP脚本文件,与模板引擎类所在的文件.php处于同一目录下,代码如下:
php //包含模板引擎类 include "mytpl.class.php"; //创建模板引擎对象 $tpl = new Mytpl; //连接数据库 $pdo = new PDO("mysql:host=localhost;dbname=mydb", "admin", "123456"); //执行SQL语句 $stmt = $pdo -> prepare("select id, name, sex,age,email from User order by id"); $stmt ->execute(); $data = $stmt -> fetchAll(PDO::FETCH_ASSOC); //这是从数据库获取的动态数据,需要在模板中显示 $tpl->assign('title',"自定义模板引擎");$tpl->assign('auto',"小火柴"); $tpl->assign('users',$data); $tpl -> display("main.tpl"); ?>
上述PHP脚本文件中,通过PDO对象连接MySQL服务器,获取用户表User中的所有记录,并以PHP二维数组变量的形式保存在变量$data中。然后包含当前目录下的“.php”文件,创建并初始化模板引擎类的对象$tpl。然后通过对象中的()方法将一些数据赋值给模板,再使用对象中的()方法加载模板文件main.tpl。将模板中标注的特殊变量替换为PHP动态赋值的数据,处理后输出模板页面。页面输出结果如下
由于时间、经验等各方面的限制,制作自定义的PHP模板引擎是非常困难的,其实你需要的并不是重新构造一个PHP模板,而是选择一个最接近你的PHP模板,然后进行修改。
扫一扫在手机端查看
-
Tags : web 程序开发流程 php 模板引擎
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。