分享
 
 
 

一个方便易用的小模板处理类

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Title : 一个方便易用的小模板处理类

Author : Stangly Wrong

注:本模板类,经测试尚有BUG,估计还很多。如果你发现的话,请贴此,或来信告之,不胜感激。

在PHP中使用模板技术,一直是一个受欢迎的技术。自从2001年开始,PHP的很多爱好者,在PHP中引用了MVC开发模式后,模板技术就更为火热。

网上很多出名的模板引擎,如phplib的template、FastTemplate、easyTPL、BTemplate以及现在受php.net推宠的Smarty模板引擎。

我个人非常喜欢钟爱那些短小精悍的php代码,像Smarty这些动折2000多行的代码实现的模板引擎,一般不是我的钟爱。

前一段时间,看了phpe.net上有一篇文章叫<<超越模板引擎>>感觉那个老外所说确实极其有道理。他这样说:Smarty的目标是"把业务逻辑从表现中分离出来"而不是"PHP代码和HTML代码的分离"。但是Smarty确实也太庞大了,如果我在使用Smarty的话,我会感觉Smarty会不会影响php代码执行效率,首先在解析2000多行的Smarty代码就可能会带来很大延迟。

受<<超越模板引擎>>一文作者影响,我感觉到php本身就是一个嵌入式的脚本语言,如果我们能够使用用简单的php代码写成的模板,而在运行时直接将他include进来那不是更为快捷?比如像如下这个样子的代码:

My name is {myname}.

为什么我们不直接写成

My name is <?=$myname?>.

这样样子呢?

还有模板里常用的列表,其实也是简单的loop

<!-- BEGIN user_list -->

User ID : {var0}

User Name : {var1}

<!-- END user_list -->

为什么我们不直接写成如下的代码

PHP:

foreach ( $user_list as $user ) { ?>

User ID : ['id']?>

User Name : ['name']?>

} ?>

其实早在一年前,我就尝试用这种方法去做一些企业和政府的WebOS,只是当时,还没有意识这样做带来的好处。只是感觉这样的模板我不需要单独的代码去处理他,只是简单的include一下就ok了。而且,我记得当时在做这些用php语言直接写出的模板的时候,非常快捷,以至于到后来,参与开发几个美工都因此对php的程序开发,发生了很大的兴趣,开始学习php的脚本语言。

最近又受<<超越模板引擎>>一文影响,所以决定根据他的思想去实现一个模板处理的类,经过了二三天的每天像榨水果汁一样挤出的几个小时去完成了一个模板类。现在张贴在此,以便能有志同道合朋友能够与我一起讨论。

考虑了现在带会经常使用{name}的这样的标记去做模板,所以我写一个简单的ParseHTM()的方法,去解析这样的文件,将其转换为<?=$name?>这样的php模板。然后将转换后的文件存贮到相应的cache目录中去,然后提供一个ParsePHP()方法去简单的include文件然后在主程序中echo输出。在实现的时候,我为了简化调用方法.使用了一个Parse()方法去呼叫上述两个私有方法.

以下是代码示例:

首先给出目录结构:

\ 根目录

├─cache 存放php模板缓存的文件

├─includes 类库存放目录

│ └─template 模板处理template.php类库文件存放目录

└─templates 模板目录

├─imgs 模板文件的图片目录

│ └─ver.1

└─tpls 模板文件目录

└─ver.1

示例一:对于html类型的模板文件的处理.

test.htm.php 模板文件存放在 \templates\tpls\ver.1\目录下

<html>

<head>

<title>{title}</title>

</head>

<body>

<!-- Trim php code that can't occur in HTML type template file. Now , that will be disable . -->

<?

print_r($_ENV);

?>

<?=$test?>

<? if ( $a == $b ) echo 'a==b'; ?>

<!--IF condition-->

<table border="1" align="center">

<tr>

<td>$condition is TRUE</td>

</tr>

</table>

<!--ENDIF-->

<!--IF a == b-->

<table border="1" align="center">

<tr>

<td>$a == $b</td>

</tr>

</table>

<!--ENDIF-->

<!--IF a != b-->

<table border="1" align="center">

<tr>

<td>$a != $b</td>

</tr>

</table>

<!--ENDIF-->

<table border="1" align="center">

<tr>

<td>id</td>

<td>name</td>

</tr>

<!--LIST user_list AS user-->

<tr>

<td>{user[id]}</td>

<td>{user[name]}</td>

</tr>

<!--ENDLIST-->

</table>

</body>

</html>

test_htm.php文件对类库调用示例,本文件存放在 \ 目录下

下面是对这样的html类型的模板出理调用的具体过程.

PHP:

require_once('includes/template/template.php');

$tpl = new Template;

$tpl->SetFile('templates/tpls/ver.1/test.htm.php');

// 对于htm类型模板来说,通过此设置编译后的模板文件

$tpl->SetCacheDir('cache/');

$tpl->SetVar('title','I love jear');

$tpl->SetVar('condition',TRUE);

$tpl->SetVar('a','a');

$tpl->SetVar('b','a');

$user_list = array(

array(

'id' => '1',

'name' => 'Stangly.wrong'

),

array(

'id' => '2',

'name' => 'Jear'

),

);

$tpl->SetVar('user_list',$user_list);

echo $tpl->Parse(TPL_HTM, TRUE);

?>

观察一下目录结构的变化

├─cache

│ └─templates

│ └─tpls

│ └─ver.1

├─includes

│ └─template

└─templates

├─imgs

│ └─ver.1

└─tpls

└─ver.1

程序会自动在cache目录下,建立与template文件存放的路径相同的目录路径去存放编译后的模板文件。这样个人感觉非常便于查找与排错。

示例二:对于php类型的模板文件的处理,观察一下与htm类型的模板文件有什么不同?是不是所有 {title} 这样的标记都被替换为 <?=$title?>这种形式,以及对于所谓的 block 也被直接替换为 foreach() 方式。

test.php.php 模板文件存放在 \templates\tpls\ver.1\目录下

<html>

<head>

<title><?=$title?></title>

</head>

<body>

<? if ($condition) { ?>

<table border="1" align="center">

<tr>

<td>$condition is TRUE !</td>

</tr>

</table>

<? } ?>

<? if ($a == $b) { ?>

<table border="1" align="center">

<tr>

<td>$a == $b</td>

</tr>

</table>

<? } ?>

<? if ($a != $b) { ?>

<table border="1" align="center">

<tr>

<td>$a != $b</td>

</tr>

</table>

<? } ?>

<table border="1" align="center">

<tr>

<td>id</td>

<td>name</td>

</tr>

<? foreach ( $user_list as $user ) { ?>

<tr>

<td><?=$user[id]?></td>

<td><?=$user[name]?></td>

</tr>

<? } ?>

</table>

</body>

</html>

test_htm.php文件对类库调用示例,本文件存放在 \ 目录下

下面是对这样的html类型的模板出理调用的具体过程.

PHP:

require_once('includes/template/template.php');

$tpl = new Template;

$tpl->SetFile('templates/tpls/ver.1/test.php.php');

// 对于 php 文件来说, 此设置无效

//$tpl->SetCacheDir('cache/');

//

$tpl->SetVar('title','I love jear');

$tpl->SetVar('condition',TRUE);

$tpl->SetVar('a','a');

$tpl->SetVar('b','a');

$users_list = array(

array(

'id' => '1',

'name' => 'Stangly.wrong'

),

array(

'id' => '2',

'name' => 'Jear'

),

);

$tpl->SetVar('user_list',$users_list);

echo $tpl->Parse(TPL_PHP);

?>

以上就是模板类的调用示例,以及模板文件内容的格式。

最后给出template.php文件,他存放于 /includes/template/ 目录下。

PHP:

// -------------------------------------------------------

// Filename : template.php

// Created : 2004-11-6 13:46

// Author : Stangly.Wrong

// Version : 1.0.0

// Description : Parse template

// Modified : 2004-11-7 15:10

// -------------------------------------------------------

// 定义模板类型

define( 'TPL_HTM', 'htm' );

define( 'TPL_PHP', 'php' );

// 定义默认的缓存目录

define( 'TPL_CACHE_DIR', './cache/');

// 编译后的 TPL_HTM 类型缓存文件名 前缀

define( 'TPL_COMPLIE_FILE_PREFIX','_c_');

Class Template

{

// 模板文件存放的目录

var $_root_dir = NULL;

// 模板文件名

var $_file = NULL;

// 模板文件类型

var $_file_type = TPL_PHP;

// 存贮模板内定义的变量

var $_var = array();

// 编译和缓存文件存放目录

var $_cache_dir = TPL_CACHE_DIR;

// Private function

//

// 参数:

// $f 为PHP类型模板完整路径文件名

//

// 解析php模板文件

//

// 返回解析的结果, 用于直接输出到客户端

//

function &_ParsePHP( $f )

{

if ( !file_exists( $f ) )

{

die('Open file '.$f.' failed !');

}

extract($this->_var);

ob_start();

include($f);

$contents = &ob_get_contents();

ob_end_clean();

return $contents;

}

// Private function

//

// 参数:

// $f 为HTM类型模板完整路径文件名

// $c 确定是否需要重新编译htm模板

//

// 解析htm模板文件

//

// 返回解析的结果, 用于直接输出到客户端

function &_ParseHTM( $f, $c = FALSE )

{

if ( $c )

{

$f = $this->_CompileHTM( $f );

}

else

{

$f = $this->_cache_dir.$this->_root_dir.'/'.TPL_COMPLIE_FILE_PREFIX.$this->_file;

}

return $this->_ParsePHP( $f );

}

// Private function

//

// 参数:

// $f 为HTM类型模板完整路径文件名

//

// 编译htm模板文件

//

// 编译htm模板文件,将存贮到缓存目录中

// 同时返回编译后的文件名

//

function &_CompileHTM( $f )

{

if ( !file_exists( $f ) )

{

die('Open file '.$f.' failed !');

}

ob_start();

$fp = fopen( $f, 'r');

$c = fread($fp, filesize($f));

// 屏蔽php代码

$c = preg_replace('/(\<\?)(.*)(\?\>)/siU', '', $c);

$c = preg_replace('//', '} ?>', $c);

$c = preg_replace('//', '} ?>', $c);

$p = array(

0 => '/\{([^}]+)\}/',

1 => '/[a-zA-Z0-9_]+)--/',

2 => '/[a-zA-Z0-9_]+) AS ([a-zA-Z0-9_]+)--/',

3 => '/[a-zA-Z0-9_]+) (==|!=) ([a-zA-Z0-9_]+)--/',

4 => '/[a-zA-Z0-9_]+) AS ([a-zA-Z0-9_]+) = ([a-zA-Z0-9_]+)--/',

);

$s = array(

0 => '\$\1?>',

1 => '\$\1) { ?>',

2 => '\$\1 as \$\2 ) { ?>',

3 => '\$\1 \2 \$\3) { ?>',

4 => '\$\1 as \$\2 => \$\3 ) { ?>',

);

echo $c = preg_replace($p, $s, $c);

$c = &ob_get_contents();

ob_end_clean();

// 生成需要创建的缓存目录

$d = $this->_cache_dir.$this->_root_dir;

$this->_MakeDirectory( $d );

// 创建编译后的模板文件

$cf = $d.'/'.TPL_COMPLIE_FILE_PREFIX.$this->_file;

if(!@$fp = fopen($cf, 'w'))

{

die('Open file '.$cf.' failed !');

}

flock( $fp, 3 );

fwrite($fp, $c);

fclose($fp);

return $cf;

}

// Private function

//

// 参数

// $d 要创建的目录名

//

// 创建目录

//

function _MakeDirectory( $d )

{

if (is_null($d))

{

return TRUE;

}

$p = explode('/', $d);

foreach ( $p as $k => $v )

{

$prefix .= $v.'/';

if ( is_dir( $prefix ) )

continue;

if (!empty($v) && !@mkdir($prefix, 0777))

die('Cant create cache dir : '.$prefix);

}

return TRUE;

}

/******************************************************************************************/

// 公共成员方法 调用接口

// 初始化成员变量

function Template() {}

// public function

//

// 参数:

// $d 缓存目录路径

//

// 设置缓存和编译文件存放目录

//

function SetCacheDir( $d )

{

$this->_cache_dir = $d;

return TRUE;

}

// Public function

//

// 参数:

// $f 模板文件存放的完整路径名

// $t 模板类型

// TPL_HTM

// TPL_PHP

//

// 设置模板文件存放的目录和文件名以及模板的类型

//

function SetFile( $f = NULL, $t = TPL_HTM )

{

$this->_root_dir = dirname($f);

$this->_file = basename($f);

$this->_file_type = $t;

return TRUE;

}

// Public function

//

// 参数:

// $n 可以为 模板内在{}号内定义的名称

// 或为数组

// $n[]['id'], $n[]['name'], $n[]['sex']

//

// $v 当 $n 为数组时, $v 为空值

//

// 设置模板内定义变量

//

function SetVar( $n, $v = NULL )

{

if ( is_array( $n ) )

{

foreach ( $n as $k => $v )

{

$this->_var[$k] = $v;

}

}

else

{

$this->_var[$n] = $v;

}

return TRUE;

}

// Public function

//

// 参数说明:

// $t 为处理的模板的类型

//

// 对指定的模板进行处理解析 根据 $t 的类型返回不同的结果

//

// $t == TPL_PHP 立即解析返回 html 文件 ( parse -> result )

// $t == TPL_HTM 立即解析返回 html 文件 ( compile -> cache compiled -> parse -> result )

//

// $e == TRUE 时重新编译模板文件 FALSE 使用上一次的编译过的文件

//

function &Parse( $t, $e = FALSE)

{

switch ( $t )

{

case TPL_PHP :

$c = $this->_ParsePHP( $this->_root_dir.'/'.$this->_file );

break;

case TPL_HTM :

$c = $this->_ParseHTM( $this->_root_dir.'/'.$this->_file, $e );

break;

}

return $c;

}

}

?>

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有